Search code examples
t-sqldatabase-performancesap-ase

sybase - fails to use index unless string is hard-coded


I'm using Sybase 12.5.3 (ASE); I'm new to Sybase though I've worked with MSSQL pretty extensively. I'm running into a scenario where a stored procedure is really very slow. I've traced the issue to a single SELECT stmt for a relatively large table. Modifying that statement dramatically improves the performance of the procedure (and reverting it drastically slows it down; i.e., the SELECT stmt is definitely the culprit).

-- Sybase optimizes and uses multi-column index... fast!<br>
SELECT ID,status,dateTime
FROM myTable
WHERE status in ('NEW','SENT')
ORDER BY ID

-- Sybase does not use index and does very slow table scan<br>
SELECT ID,status,dateTime
FROM myTable
WHERE status in (select status from allowableStatusValues)
ORDER BY ID

The code above is an adapted/simplified version of the actual code. Note that I've already tried recompiling the procedure, updating statistics, etc.

I have no idea why Sybase ASE would choose an index only when strings are hard-coded and choose a table scan when choosing from another table. Someone please give me a clue, and thank you in advance.


Solution

  • 1.The issue here is poor coding. In your release, poor code and poor table design are the main reasons (98%) the optimiser makes incorrect decisions (the two go hand-in-hand, I have not figured out the proportion of each). Both:

        WHERE status IN ('NEW','SENT')
    

    and

        WHERE status IN (SELECT status FROM allowableStatusValues)
    

    are substandard, because in both cases they cause ASE to create a worktable for the contents between the brackets, which can easily be avoided (and all consequential issues avoided with it). There is no possibility of statistics on a worktable, since the statistics on either t.status or s.status is missing (AdamH is correct re that point), it correctly chooses a table scan.

    Subqueries have their place, but never as a substitute for a pure (the tables are related) join. The corrections are:

        WHERE status = "NEW" OR status = "SENT"
    

    and

        FROM  myTable t,
              allowableStatusValues s
        WHERE t.status = s.status
    

    2.The statement

    |Now you don't have to add an index to get statistics on a column, but it's probably the best way.

    is incorrect. Never create Indices that you will not use. If you want statistics updated on a column, simply

        UPDATE STATISTICS myTable (status)
    

    3.It is important to ensure that you have current statistics on (a) all indexed columns and (b) all join columns.

    4.Yes, there is no substitute for SHOWPLAN on every code segment that is intended for release, doubly so for any code with questionable performance. You can also SET NOEXEC ON, to avoid execution, eg. for large result sets.