Академический Документы
Профессиональный Документы
Культура Документы
good choice to have on fields accessed by a range or used in order by and group by statements. An example of this is a record_create_date field which you would often access using a between statement. Indexes that are not selective will not be useful and should be discarded. Indexes that cover queries are very useful. Exceptionally long indexes are slow. The optimizer likes indexes to be unique. Dont index large fields or fields that allow nulls unless you need to because nulls make the index longer and your key fields should really not allow nulls in a good design. The stored procedure sp__badindex identifies many potential index problems. The following explains the best indexes by query type: SELECTS: Add as many indexes as possible. INSERTS: A clustered index and no non clustered indexes. The clustered index reduces contention on the last page, although there is slightly more overhead inserting into a clustered index than into a table with only non clustered indexes. UPDATES: Indexes will speed the retrieval of the row, but the updates that are not done in place act as a delete followed by an insert. DELETES: Each index adds some overhead to the operation. 5) Look at the trigger layout using sp__helptrigger and by reviewing the trigger code. Triggers should only be used if you are allowing users interactive update access to the database or to archive data to history tables. Triggers should always be as lean as possible. Use referential integrity constraints and check constraint instead of triggers. It is good practice to break trigger code into two parts based on the number of rows (@@rowcount). The syntax of these triggers should resemble select @rc=@@rowcount if @rc=1 then begin ... end else begin ... end The single row case can be simplified by selecting inserted and deleted information into variables. All trigger execution is performed within a transaction, so large triggers can lead to deadlock situations in multi user environments. Slow triggers are a major reason applications do not perform and as triggers fire in the background, developers dont really think about them. This can easily cause the perception that the whole system is slow, when in reality the system is doing a lot more work than they give it credit for. Analyze the SQL in your triggers and over optimize your trigger code. 6) Run sp__find_missing_keys. This procedure will find key relationships that do not have associated indexes. It is not incorrect for a key to not be associated with an index, but each of these missing indexes should be investigated. An example of a
3
key that should not be an index is a document table that has a color field which is related to the color table. If you dont care to find all red documents, document need not be indexed on color, although color is still a foreign key. 7) Look through your stored procedures for weak transact SQL statements. Certain transact SQL statements are going to force Sybase NOT to use an index. Review the next section (Tuning Transact SQL Statements). Compare WHERE clauses with existing indexes. Identify major transactions and be sure they are indexed appropriately. You can use sp_depends to identify the procedures that use each table. I find it very helpful to analyze critical transactions by drawing them out graphically. 8) Any remaining slow transactions can be tuned by using the set showplan, set statistics io, and set statistics time options. 9) Look for poor Sybase transaction design. These can often be identified by looking at locks. You never want to do table scans within a transaction. Look for long transactions (the shorter the better). Remember a trigger is an implicit transaction. 10) Look carefully at both loops and cursor coding. It is often difficult to use cursors in an easy and fast manner, but it can be done. Often cursors will prove significantly faster than while loops. Transact SQL is tuned to use set logic instead of cursor processing. 11) If you have locking contention on heavily inserted / updated tables, look at the index design. Sybase will insert to the last page if there is no clustered index or if that index is monotonically increasing or an identity column. Heavily insert environments should cluster on a key which allows rows to be inserted reasonably randomly.
The use of built in functions like rtrim and upper on a column will cause that column to be ignored for the purposes of index selection. Be careful of left hand and right hand arguments. WHERE id=@x is useful for index selection, while WHERE @x=id does not. You must specify the leading portion of the index to use it. This means that if the index exists on columns a,b,c (in that order), then Sybase will not consider it for queries that only specify columns b and c. OR and IN clauses cause dynamic indexes and work tables to be created and will cause a performance penalty. Avoid multiple Or clauses, particularly on different columns within the same table. Over specify your where clauses. This means changing your query from WHERE a.id=b.id and b.id=c.id by adding and a.id=c.id.
4
Avoid comparisons of fields with data type mismatches. Comparisons of fields which are defined differently (i.e. float and int) will usually not use an index. The sp__helpcolumn is very useful to identify columns that have identical names but are of different types. The like operator will cause the optimizer to not use an index when used with wildcards at the beginning of a query (i.e. like %abc will never use an index). Not equal (!=) expressions will not be used for index selection. Avoid 5 or more table scans unless you have specifically optimized them. Optimizer tables are joined in groups of 4. Under system 11 this parameter is configurable. These joins can be reduced by using temp tables, variables, or denormalization. Make sure that the WHERE clause of Update and Delete transactions do not scan the entire table. Transactions should be as short as possible, and there should be no user interaction within a transaction. Avoid the use of WHILE loops and GOTO statements. Avoid using temp tables to communicate between stored procedures. This will cause the called procedures to be reresolved and recompiled each time. Use exists and in instead of not exists and not in. These queries will perform better and most code can easily be rewritten. Use select 1 instead of select * in exists clauses because an asterisk can cause locking problems on syscolumns when used a lot. Use exists instead of count wherever possible. Exists will stop after the first match, as opposed to count which will go through all rows to get a total count. Use >= and <= instead of > and <. >= can save substantial I/O in some cases. Use select into instead of insert/select because these operations are not logged.