Loading .bzrignore +1 −0 Original line number Diff line number Diff line Loading @@ -451,3 +451,4 @@ vio/test-ssl vio/test-sslclient vio/test-sslserver vio/viotest-ssl libmysqld/ha_innodb.cc Docs/manual.texi +149 −47 Original line number Diff line number Diff line Loading @@ -750,7 +750,7 @@ Large server clusters using replication are in production use, with good results. Work on enhanced replication features is continuing in MySQL 4.0. @item @code{InnoDB} tables -- Gamma @item @code{InnoDB} tables -- Stable While the @code{InnoDB} transactional table handler is a fairly recent addition to @code{MySQL}, it appears to work well and is already being used in some large, heavy load production systems. Loading Loading @@ -3698,9 +3698,6 @@ Allow users to change startup options without taking down the server. @item Fail safe replication. @item More functions for full-text search. @xref{Fulltext TODO}. @item New key cache @item New table definition file format (@code{.frm} files) This will enable us Loading Loading @@ -3733,9 +3730,6 @@ When using @code{SET CHARACTER SET} we should translate the whole query at once and not only strings. This will enable users to use the translated characters in database, table and column names. @item Add a portable interface over @code{gethostbyaddr_r()} so that we can change @code{ip_to_hostname()} to not block other threads while doing DNS lookups. @item Add @code{record_in_range()} method to @code{MERGE} tables to be able to choose the right index when there is many to choose from. We should also extend the info interface to get the key distribution for each index, Loading Loading @@ -3867,15 +3861,6 @@ Don't add automatic @code{DEFAULT} values to columns. Give an error when using an @code{INSERT} that doesn't contain a column that doesn't have a @code{DEFAULT}. @item Caching of queries and results. This should be done as a separated module that examines each query and if this is query is in the cache the cached result should be returned. When one updates a table one should remove as few queries as possible from the cache. This should give a big speed bost on machines with much RAM where queries are often repeated (like WWW applications). One idea would be to only cache queries of type: @code{SELECT CACHED ...} @item Fix @file{libmysql.c} to allow two @code{mysql_query()} commands in a row without reading results or give a nice error message when one does this. @item Loading Loading @@ -3940,10 +3925,7 @@ ADD_TO_SET(value,set) and REMOVE_FROM_SET(value,set) Add use of @code{t1 JOIN t2 ON ...} and @code{t1 JOIN t2 USING ...} Currently, you can only use this syntax with @code{LEFT JOIN}. @item Add full support for @code{unsigned long long} type. @item Many more variables for @code{show status}. Counts for: @code{INSERT}/@code{DELETE}/@code{UPDATE} statements. Records reads and Many more variables for @code{show status}. Records reads and updated. Selects on 1 table and selects with joins. Mean number of tables in select. Number of @code{ORDER BY} and @code{GROUP BY} queries. @item Loading @@ -3958,7 +3940,7 @@ should be implemented. @item Add support for UNICODE. @item @code{NATURAL JOIN} and @code{UNION JOIN} @code{NATURAL JOIN}. @item Allow @code{select a from crash_me left join crash_me2 using (a)}; In this case @code{a} is assumed to come from the @code{crash_me} table. Loading Loading @@ -4075,8 +4057,6 @@ Use of full calculation names in the order part. (For ACCESS97) @code{MINUS}, @code{INTERSECT} and @code{FULL OUTER JOIN}. (Currently @code{UNION} (in 4.0) and @code{LEFT OUTER JOIN} are supported) @item Allow @code{UNIQUE} on fields that can be @code{NULL}. @item @code{SQL_OPTION MAX_SELECT_TIME=#} to put a time limit on a query. @item Make the update log to a database. Loading Loading @@ -24584,6 +24564,7 @@ great tool to find out if this is a problem with your query. * Where optimisations:: How MySQL optimises @code{WHERE} clauses * DISTINCT optimisation:: How MySQL Optimises @code{DISTINCT} * LEFT JOIN optimisation:: How MySQL optimises @code{LEFT JOIN} * ORDER BY optimisation:: * LIMIT optimisation:: How MySQL optimises @code{LIMIT} * Insert speed:: Speed of @code{INSERT} queries * Update speed:: Speed of @code{UPDATE} queries Loading Loading @@ -25177,7 +25158,7 @@ MySQL will stop reading from t2 (for that particular row in t1) when the first row in t2 is found. @node LEFT JOIN optimisation, LIMIT optimisation, DISTINCT optimisation, Query Speed @node LEFT JOIN optimisation, ORDER BY optimisation, DISTINCT optimisation, Query Speed @subsection How MySQL Optimises @code{LEFT JOIN} and @code{RIGHT JOIN} @findex LEFT JOIN Loading Loading @@ -25243,7 +25224,119 @@ SELECT * FROM b,a LEFT JOIN c ON (c.key=a.key) LEFT JOIN d (d.key=a.key) WHERE b @end example @node LIMIT optimisation, Insert speed, LEFT JOIN optimisation, Query Speed @node ORDER BY optimisation, LIMIT optimisation, LEFT JOIN optimisation, Query Speed @subsection How MySQL Optimises @code{ORDER BY} In some cases MySQL can uses index to satisfy an @code{ORDER BY} or @code{GROUP BY} request without doing any extra sorting. The index can also be used even if the @code{ORDER BY} doesn't match the index exactly, as long as all the unused index parts and all the extra are @code{ORDER BY} columns are constants in the @code{WHERE} clause. The following queries will use the index to resolve the @code{ORDER BY} / @code{GROUP BY} part: @example SELECT * FROM t1 ORDER BY key_part1,key_part2,... SELECT * FROM t1 WHERE key_part1=constant ORDER BY key_part2 SELECT * FROM t1 WHERE key_part1=constant GROUP BY key_part2 SELECT * FROM t1 ORDER BY key_part1 DESC,key_part2 DESC SELECT * FROM t1 WHERE key_part1=1 ORDER BY key_part1 DESC,key_part2 DESC @end example Some cases where MySQL can NOT use indexes to resolve the @code{ORDER BY}: (Note that MySQL will still use indexes to find the rows that matches the where clause): @itemize @bullet @item You are doing an @code{ORDER BY} on different keys: @code{SELECT * FROM t1 ORDER BY key1,key2} @item You are doing an @code{ORDER BY} on not following key parts. @code{SELECT * FROM t1 WHERE key2=constant ORDER BY key_part2} @item You are mixing @code{ASC} and @code{DESC}. @code{SELECT * FROM t1 ORDER BY key_part1 DESC,key_part2 ASC} @item The key used to fetch the rows are not the same one that is used to do the @code{ORDER BY}: @code{SELECT * FROM t1 WHERE key2=constant ORDER BY key1} @item You are joining many tables and the columns you are doing an @code{ORDER BY} on are not all from the first not-const table that is used to retrieve rows (This is the first table in the @code{EXPLAIN} output which doesn't use a @code{const} row fetch method). @item You have different @code{ORDER BY} and @code{GROUP BY} expressions. @item The used table index is an index type that doesn't store rows in order. (Like index in @code{HEAP} tables). @end itemize In the cases where MySQL have to sort the result, it uses the following algorithm: @itemize @bullet @item Read all rows according to key or by table scanning. Rows that doesn't match the WHERE clause are skipped. @item Store the sort-key in a buffer (of size @code{sort_buffer}). @item When the buffer gets full, run a qsort on it and store the result in a temporary file. Save a pointer to the sorted block. (In the case where all rows fits into the sort buffer, no temporary file is created) @item Repeat the above until all rows have been read. @item Do a multi-merge of up to @code{MERGEBUFF} (7) regions to one block in another temporary file. Repeat until all blocks from the first file are in the second file. @item Repeat the following until there is less than @code{MERGEBUFF2} (15) blocks left. @item On the last multi-merge, only the pointer to the row (last part of the sort-key) is written to a result file. @item Now the code in @file{sql/records.cc} will be used to read through them in sorted order by using the row pointers in the result file. To optimize this, we read in a big block of row pointers, sort these and then we read the rows in the sorted order into a row buffer (@code{record_rnd_buffer}) . @end itemize You can with @code{EXPLAIN SELECT ... ORDER BY} check if MySQL can use indexes to resolve the query. If you get @code{Using filesort} in the @code{extra} column, then MySQL can't use indexes to resolve the @code{ORDER BY}. @xref{EXPLAIN}. If you want to have a higher @code{ORDER BY} speed, you should first see if you can get MySQL to use indexes instead of having to do an extra sorting phase. If this is not possible, then you can do: @itemize @bullet @item Increase the size of the @code{sort_buffer} variable. @item Increase the size of the @code{record_rnd_buffer} variable. @item Change @code{tmpdir} to point to a dedicated disk with lots of empty space. @end itemize @node LIMIT optimisation, Insert speed, ORDER BY optimisation, Query Speed @subsection How MySQL Optimises @code{LIMIT} @findex LIMIT Loading Loading @@ -25992,19 +26085,9 @@ SELECT MIN(key_part2),MAX(key_part2) FROM table_name where key_part1=10 @item Sort or group a table if the sorting or grouping is done on a leftmost prefix of a usable key (for example, @code{ORDER BY key_part_1,key_part_2 }). The key is read in reverse order if all key parts are followed by @code{DESC}. The index can also be used even if the @code{ORDER BY} doesn't match the index exactly, as long as all the unused index parts and all the extra are @code{ORDER BY} columns are constants in the @code{WHERE} clause. The following queries will use the index to resolve the @code{ORDER BY} part: @example SELECT * FROM foo ORDER BY key_part1,key_part2,key_part3; SELECT * FROM foo WHERE column=constant ORDER BY column, key_part1; SELECT * FROM foo WHERE key_part1=const GROUP BY key_part2; @end example prefix of a usable key (for example, @code{ORDER BY key_part_1,key_part_2 }). The key is read in reverse order if all key parts are followed by @code{DESC}. @xref{ORDER BY optimisation}. @item In some cases a query can be optimised to retrieve values without Loading Loading @@ -33370,7 +33453,12 @@ DELETE [LOW_PRIORITY | QUICK] FROM table_name or DELETE [LOW_PRIORITY | QUICK] table_name[.*] [table_name[.*] ...] FROM DELETE [LOW_PRIORITY | QUICK] table_name[.*] [,table_name[.*] ...] FROM table-references [WHERE where_definition] or DELETE [LOW_PRIORITY | QUICK] FROM table_name[.*], [table_name[.*] ...] USING table-references [WHERE where_definition] @end example Loading Loading @@ -33407,18 +33495,23 @@ TABLE} statement or the @code{myisamchk} utility to reorganise tables. @code{OPTIMIZE TABLE} is easier, but @code{myisamchk} is faster. See @ref{OPTIMIZE TABLE, , @code{OPTIMIZE TABLE}} and @ref{Optimisation}. The multi table delete format is supported starting from MySQL 4.0.0. The first multi table delete format is supported starting from MySQL 4.0.0. The second multi table delete format is supported starting from MySQL 4.0.2. The idea is that only matching rows from the tables listed @strong{before} the @code{FROM} clause is deleted. The effect is that you can delete rows from many tables at the same time and also have additional tables that are used for searching. The idea is that only matching rows from the tables listed @strong{before} the @code{FROM} or before the @code{USING} clause is deleted. The effect is that you can delete rows from many tables at the same time and also have additional tables that are used for searching. The @code{.*} after the table names is there just to be compatible with @code{Access}: @example DELETE t1,t2 FROM t1,t2,t3 WHERE t1.id=t2.id AND t2.id=t3.id or DELETE FROM t1,t2 USING t1,t2,t3 WHERE t1.id=t2.id AND t2.id=t3.id @end example In the above case we delete matching rows just from tables @code{t1} and Loading Loading @@ -48044,10 +48137,16 @@ Our TODO section contains what we plan to have in 4.0. @xref{TODO MySQL 4.0}. @item Fixed bug with empty expression for boolean fulltext search. @item Fixed bug in updating fulltext key from/to @code{NULL}. No coredump anymore. Fixed core dump bug in updating fulltext key from/to @code{NULL}. @item ODBC compatibility: added @code{BIT_LENGTH()} function. ODBC compatibility: Added @code{BIT_LENGTH()} @item Fixed core dump bug in @code{GROUP BY BINARY column}. @item Added support for @code{NULL} keys in HEAP tables. @item Use index for @code{ORDER BY} in queries of type: @code{SELECT * FROM t WHERE key_part1=1 ORDER BY key_part1 DESC,key_part2 DESC} @item Fixed bug in @code{FLUSH QUERY CACHE}. @item Loading @@ -48056,6 +48155,9 @@ Added @code{CAST()} and @code{CONVERT()} functions. The @code{CAST} and want to create a column with a specific type in a @code{CREATE ... SELECT}. For more information, read @ref{Cast Functions}. @item @code{CREATE ... SELECT} on @code{DATE} and @code{TIME} functions now create columns of the expected type. @item Changed order of how keys are created in tables. @item Added a new columns @code{Null} and @code{Index_type} to @code{SHOW INDEX}. heap/_check.c +4 −2 Original line number Diff line number Diff line Loading @@ -79,9 +79,11 @@ static int check_one_key(HP_KEYDEF *keydef, uint keynr, ulong records, } DBUG_PRINT("info", ("records: %ld seeks: %d max links: %d hitrate: %.2f", records,seek,max_links,(float) seek / (float) (records ? records : 1))); records,seek,max_links, (float) seek / (float) (records ? records : 1))); if (print_status) printf("Key: %d records: %ld seeks: %d max links: %d hitrate: %.2f\n", keynr, records, seek, max_links, (float) seek / (float) records); keynr, records, seek, max_links, (float) seek / (float) (records ? records : 1)); return error; } heap/heapdef.h +1 −0 Original line number Diff line number Diff line Loading @@ -70,6 +70,7 @@ extern int _hp_rec_key_cmp(HP_KEYDEF *keydef,const byte *rec1, extern int _hp_key_cmp(HP_KEYDEF *keydef,const byte *rec, const byte *key); extern void _hp_make_key(HP_KEYDEF *keydef,byte *key,const byte *rec); extern my_bool hp_if_null_in_key(HP_KEYDEF *keyinfo, const byte *record); extern int _hp_close(register HP_INFO *info); extern void _hp_clear(HP_SHARE *info); Loading heap/hp_hash.c +82 −6 Original line number Diff line number Diff line Loading @@ -158,11 +158,22 @@ ulong _hp_hashnr(register HP_KEYDEF *keydef, register const byte *key) { uchar *pos=(uchar*) key; key+=seg->length; if (seg->null_bit) { key++; /* Skipp null byte */ if (*pos) /* Found null */ { nr^= (nr << 1) | 1; continue; } pos++; } if (seg->type == HA_KEYTYPE_TEXT) { for (; pos < (uchar*) key ; pos++) { nr^=(ulong) ((((uint) nr & 63)+nr2)*((uint) my_sort_order[(uint) *pos]))+ (nr << 8); nr^=(ulong) ((((uint) nr & 63)+nr2) * ((uint) my_sort_order[(uint) *pos])) + (nr << 8); nr2+=3; } } Loading @@ -188,11 +199,20 @@ ulong _hp_rec_hashnr(register HP_KEYDEF *keydef, register const byte *rec) for (seg=keydef->seg,endseg=seg+keydef->keysegs ; seg < endseg ; seg++) { uchar *pos=(uchar*) rec+seg->start,*end=pos+seg->length; if (seg->null_bit) { if (rec[seg->null_pos] & seg->null_bit) { nr^= (nr << 1) | 1; continue; } } if (seg->type == HA_KEYTYPE_TEXT) { for (; pos < end ; pos++) { nr^=(ulong) ((((uint) nr & 63)+nr2)*((uint) my_sort_order[(uint) *pos]))+ (nr << 8); nr^=(ulong) ((((uint) nr & 63)+nr2)* ((uint) my_sort_order[(uint) *pos]))+ (nr << 8); nr2+=3; } } Loading Loading @@ -234,6 +254,16 @@ ulong _hp_hashnr(register HP_KEYDEF *keydef, register const byte *key) { uchar *pos=(uchar*) key; key+=seg->length; if (seg->null_bit) { key++; if (*pos) { nr^= (nr << 1) | 1; continue; } pos++; } if (seg->type == HA_KEYTYPE_TEXT) { for (; pos < (uchar*) key ; pos++) Loading Loading @@ -264,6 +294,14 @@ ulong _hp_rec_hashnr(register HP_KEYDEF *keydef, register const byte *rec) for (seg=keydef->seg,endseg=seg+keydef->keysegs ; seg < endseg ; seg++) { uchar *pos=(uchar*) rec+seg->start,*end=pos+seg->length; if (seg->null_bit) { if (rec[seg->null_pos] & seg->null_bit) { nr^= (nr << 1) | 1; continue; } } if (seg->type == HA_KEYTYPE_TEXT) { for ( ; pos < end ; pos++) Loading Loading @@ -295,6 +333,14 @@ int _hp_rec_key_cmp(HP_KEYDEF *keydef, const byte *rec1, const byte *rec2) for (seg=keydef->seg,endseg=seg+keydef->keysegs ; seg < endseg ; seg++) { if (seg->null_bit) { if ((rec1[seg->null_pos] & seg->null_bit) != (rec2[seg->null_pos] & seg->null_bit)) return 1; if (rec1[seg->null_pos] & seg->null_bit) continue; } if (seg->type == HA_KEYTYPE_TEXT) { if (my_sortcmp(rec1+seg->start,rec2+seg->start,seg->length)) Loading @@ -309,14 +355,24 @@ int _hp_rec_key_cmp(HP_KEYDEF *keydef, const byte *rec1, const byte *rec2) return 0; } /* Compare a key in a record to a hole key */ /* Compare a key in a record to a whole key */ int _hp_key_cmp(HP_KEYDEF *keydef, const byte *rec, const byte *key) { HP_KEYSEG *seg,*endseg; for (seg=keydef->seg,endseg=seg+keydef->keysegs ; seg < endseg ; seg++) for (seg=keydef->seg,endseg=seg+keydef->keysegs ; seg < endseg ; key+= (seg++)->length) { if (seg->null_bit) { int found_null=test(rec[seg->null_pos] & seg->null_bit); if (found_null != (int) *key++) return 1; if (found_null) continue; } if (seg->type == HA_KEYTYPE_TEXT) { if (my_sortcmp(rec+seg->start,key,seg->length)) Loading @@ -327,7 +383,6 @@ int _hp_key_cmp(HP_KEYDEF *keydef, const byte *rec, const byte *key) if (bcmp(rec+seg->start,key,seg->length)) return 1; } key+=seg->length; } return 0; } Loading @@ -341,7 +396,28 @@ void _hp_make_key(HP_KEYDEF *keydef, byte *key, const byte *rec) for (seg=keydef->seg,endseg=seg+keydef->keysegs ; seg < endseg ; seg++) { if (seg->null_bit) *key++= test(rec[seg->null_pos] & seg->null_bit); memcpy(key,rec+seg->start,(size_t) seg->length); key+=seg->length; } } /* Test if any of the key parts are NULL. Return: 1 if any of the key parts was NULL 0 otherwise */ my_bool hp_if_null_in_key(HP_KEYDEF *keydef, const byte *record) { HP_KEYSEG *seg,*endseg; for (seg=keydef->seg,endseg=seg+keydef->keysegs ; seg < endseg ; seg++) { if (seg->null_bit && (record[seg->null_pos] & seg->null_bit)) return 1; } return 0; } Loading
.bzrignore +1 −0 Original line number Diff line number Diff line Loading @@ -451,3 +451,4 @@ vio/test-ssl vio/test-sslclient vio/test-sslserver vio/viotest-ssl libmysqld/ha_innodb.cc
Docs/manual.texi +149 −47 Original line number Diff line number Diff line Loading @@ -750,7 +750,7 @@ Large server clusters using replication are in production use, with good results. Work on enhanced replication features is continuing in MySQL 4.0. @item @code{InnoDB} tables -- Gamma @item @code{InnoDB} tables -- Stable While the @code{InnoDB} transactional table handler is a fairly recent addition to @code{MySQL}, it appears to work well and is already being used in some large, heavy load production systems. Loading Loading @@ -3698,9 +3698,6 @@ Allow users to change startup options without taking down the server. @item Fail safe replication. @item More functions for full-text search. @xref{Fulltext TODO}. @item New key cache @item New table definition file format (@code{.frm} files) This will enable us Loading Loading @@ -3733,9 +3730,6 @@ When using @code{SET CHARACTER SET} we should translate the whole query at once and not only strings. This will enable users to use the translated characters in database, table and column names. @item Add a portable interface over @code{gethostbyaddr_r()} so that we can change @code{ip_to_hostname()} to not block other threads while doing DNS lookups. @item Add @code{record_in_range()} method to @code{MERGE} tables to be able to choose the right index when there is many to choose from. We should also extend the info interface to get the key distribution for each index, Loading Loading @@ -3867,15 +3861,6 @@ Don't add automatic @code{DEFAULT} values to columns. Give an error when using an @code{INSERT} that doesn't contain a column that doesn't have a @code{DEFAULT}. @item Caching of queries and results. This should be done as a separated module that examines each query and if this is query is in the cache the cached result should be returned. When one updates a table one should remove as few queries as possible from the cache. This should give a big speed bost on machines with much RAM where queries are often repeated (like WWW applications). One idea would be to only cache queries of type: @code{SELECT CACHED ...} @item Fix @file{libmysql.c} to allow two @code{mysql_query()} commands in a row without reading results or give a nice error message when one does this. @item Loading Loading @@ -3940,10 +3925,7 @@ ADD_TO_SET(value,set) and REMOVE_FROM_SET(value,set) Add use of @code{t1 JOIN t2 ON ...} and @code{t1 JOIN t2 USING ...} Currently, you can only use this syntax with @code{LEFT JOIN}. @item Add full support for @code{unsigned long long} type. @item Many more variables for @code{show status}. Counts for: @code{INSERT}/@code{DELETE}/@code{UPDATE} statements. Records reads and Many more variables for @code{show status}. Records reads and updated. Selects on 1 table and selects with joins. Mean number of tables in select. Number of @code{ORDER BY} and @code{GROUP BY} queries. @item Loading @@ -3958,7 +3940,7 @@ should be implemented. @item Add support for UNICODE. @item @code{NATURAL JOIN} and @code{UNION JOIN} @code{NATURAL JOIN}. @item Allow @code{select a from crash_me left join crash_me2 using (a)}; In this case @code{a} is assumed to come from the @code{crash_me} table. Loading Loading @@ -4075,8 +4057,6 @@ Use of full calculation names in the order part. (For ACCESS97) @code{MINUS}, @code{INTERSECT} and @code{FULL OUTER JOIN}. (Currently @code{UNION} (in 4.0) and @code{LEFT OUTER JOIN} are supported) @item Allow @code{UNIQUE} on fields that can be @code{NULL}. @item @code{SQL_OPTION MAX_SELECT_TIME=#} to put a time limit on a query. @item Make the update log to a database. Loading Loading @@ -24584,6 +24564,7 @@ great tool to find out if this is a problem with your query. * Where optimisations:: How MySQL optimises @code{WHERE} clauses * DISTINCT optimisation:: How MySQL Optimises @code{DISTINCT} * LEFT JOIN optimisation:: How MySQL optimises @code{LEFT JOIN} * ORDER BY optimisation:: * LIMIT optimisation:: How MySQL optimises @code{LIMIT} * Insert speed:: Speed of @code{INSERT} queries * Update speed:: Speed of @code{UPDATE} queries Loading Loading @@ -25177,7 +25158,7 @@ MySQL will stop reading from t2 (for that particular row in t1) when the first row in t2 is found. @node LEFT JOIN optimisation, LIMIT optimisation, DISTINCT optimisation, Query Speed @node LEFT JOIN optimisation, ORDER BY optimisation, DISTINCT optimisation, Query Speed @subsection How MySQL Optimises @code{LEFT JOIN} and @code{RIGHT JOIN} @findex LEFT JOIN Loading Loading @@ -25243,7 +25224,119 @@ SELECT * FROM b,a LEFT JOIN c ON (c.key=a.key) LEFT JOIN d (d.key=a.key) WHERE b @end example @node LIMIT optimisation, Insert speed, LEFT JOIN optimisation, Query Speed @node ORDER BY optimisation, LIMIT optimisation, LEFT JOIN optimisation, Query Speed @subsection How MySQL Optimises @code{ORDER BY} In some cases MySQL can uses index to satisfy an @code{ORDER BY} or @code{GROUP BY} request without doing any extra sorting. The index can also be used even if the @code{ORDER BY} doesn't match the index exactly, as long as all the unused index parts and all the extra are @code{ORDER BY} columns are constants in the @code{WHERE} clause. The following queries will use the index to resolve the @code{ORDER BY} / @code{GROUP BY} part: @example SELECT * FROM t1 ORDER BY key_part1,key_part2,... SELECT * FROM t1 WHERE key_part1=constant ORDER BY key_part2 SELECT * FROM t1 WHERE key_part1=constant GROUP BY key_part2 SELECT * FROM t1 ORDER BY key_part1 DESC,key_part2 DESC SELECT * FROM t1 WHERE key_part1=1 ORDER BY key_part1 DESC,key_part2 DESC @end example Some cases where MySQL can NOT use indexes to resolve the @code{ORDER BY}: (Note that MySQL will still use indexes to find the rows that matches the where clause): @itemize @bullet @item You are doing an @code{ORDER BY} on different keys: @code{SELECT * FROM t1 ORDER BY key1,key2} @item You are doing an @code{ORDER BY} on not following key parts. @code{SELECT * FROM t1 WHERE key2=constant ORDER BY key_part2} @item You are mixing @code{ASC} and @code{DESC}. @code{SELECT * FROM t1 ORDER BY key_part1 DESC,key_part2 ASC} @item The key used to fetch the rows are not the same one that is used to do the @code{ORDER BY}: @code{SELECT * FROM t1 WHERE key2=constant ORDER BY key1} @item You are joining many tables and the columns you are doing an @code{ORDER BY} on are not all from the first not-const table that is used to retrieve rows (This is the first table in the @code{EXPLAIN} output which doesn't use a @code{const} row fetch method). @item You have different @code{ORDER BY} and @code{GROUP BY} expressions. @item The used table index is an index type that doesn't store rows in order. (Like index in @code{HEAP} tables). @end itemize In the cases where MySQL have to sort the result, it uses the following algorithm: @itemize @bullet @item Read all rows according to key or by table scanning. Rows that doesn't match the WHERE clause are skipped. @item Store the sort-key in a buffer (of size @code{sort_buffer}). @item When the buffer gets full, run a qsort on it and store the result in a temporary file. Save a pointer to the sorted block. (In the case where all rows fits into the sort buffer, no temporary file is created) @item Repeat the above until all rows have been read. @item Do a multi-merge of up to @code{MERGEBUFF} (7) regions to one block in another temporary file. Repeat until all blocks from the first file are in the second file. @item Repeat the following until there is less than @code{MERGEBUFF2} (15) blocks left. @item On the last multi-merge, only the pointer to the row (last part of the sort-key) is written to a result file. @item Now the code in @file{sql/records.cc} will be used to read through them in sorted order by using the row pointers in the result file. To optimize this, we read in a big block of row pointers, sort these and then we read the rows in the sorted order into a row buffer (@code{record_rnd_buffer}) . @end itemize You can with @code{EXPLAIN SELECT ... ORDER BY} check if MySQL can use indexes to resolve the query. If you get @code{Using filesort} in the @code{extra} column, then MySQL can't use indexes to resolve the @code{ORDER BY}. @xref{EXPLAIN}. If you want to have a higher @code{ORDER BY} speed, you should first see if you can get MySQL to use indexes instead of having to do an extra sorting phase. If this is not possible, then you can do: @itemize @bullet @item Increase the size of the @code{sort_buffer} variable. @item Increase the size of the @code{record_rnd_buffer} variable. @item Change @code{tmpdir} to point to a dedicated disk with lots of empty space. @end itemize @node LIMIT optimisation, Insert speed, ORDER BY optimisation, Query Speed @subsection How MySQL Optimises @code{LIMIT} @findex LIMIT Loading Loading @@ -25992,19 +26085,9 @@ SELECT MIN(key_part2),MAX(key_part2) FROM table_name where key_part1=10 @item Sort or group a table if the sorting or grouping is done on a leftmost prefix of a usable key (for example, @code{ORDER BY key_part_1,key_part_2 }). The key is read in reverse order if all key parts are followed by @code{DESC}. The index can also be used even if the @code{ORDER BY} doesn't match the index exactly, as long as all the unused index parts and all the extra are @code{ORDER BY} columns are constants in the @code{WHERE} clause. The following queries will use the index to resolve the @code{ORDER BY} part: @example SELECT * FROM foo ORDER BY key_part1,key_part2,key_part3; SELECT * FROM foo WHERE column=constant ORDER BY column, key_part1; SELECT * FROM foo WHERE key_part1=const GROUP BY key_part2; @end example prefix of a usable key (for example, @code{ORDER BY key_part_1,key_part_2 }). The key is read in reverse order if all key parts are followed by @code{DESC}. @xref{ORDER BY optimisation}. @item In some cases a query can be optimised to retrieve values without Loading Loading @@ -33370,7 +33453,12 @@ DELETE [LOW_PRIORITY | QUICK] FROM table_name or DELETE [LOW_PRIORITY | QUICK] table_name[.*] [table_name[.*] ...] FROM DELETE [LOW_PRIORITY | QUICK] table_name[.*] [,table_name[.*] ...] FROM table-references [WHERE where_definition] or DELETE [LOW_PRIORITY | QUICK] FROM table_name[.*], [table_name[.*] ...] USING table-references [WHERE where_definition] @end example Loading Loading @@ -33407,18 +33495,23 @@ TABLE} statement or the @code{myisamchk} utility to reorganise tables. @code{OPTIMIZE TABLE} is easier, but @code{myisamchk} is faster. See @ref{OPTIMIZE TABLE, , @code{OPTIMIZE TABLE}} and @ref{Optimisation}. The multi table delete format is supported starting from MySQL 4.0.0. The first multi table delete format is supported starting from MySQL 4.0.0. The second multi table delete format is supported starting from MySQL 4.0.2. The idea is that only matching rows from the tables listed @strong{before} the @code{FROM} clause is deleted. The effect is that you can delete rows from many tables at the same time and also have additional tables that are used for searching. The idea is that only matching rows from the tables listed @strong{before} the @code{FROM} or before the @code{USING} clause is deleted. The effect is that you can delete rows from many tables at the same time and also have additional tables that are used for searching. The @code{.*} after the table names is there just to be compatible with @code{Access}: @example DELETE t1,t2 FROM t1,t2,t3 WHERE t1.id=t2.id AND t2.id=t3.id or DELETE FROM t1,t2 USING t1,t2,t3 WHERE t1.id=t2.id AND t2.id=t3.id @end example In the above case we delete matching rows just from tables @code{t1} and Loading Loading @@ -48044,10 +48137,16 @@ Our TODO section contains what we plan to have in 4.0. @xref{TODO MySQL 4.0}. @item Fixed bug with empty expression for boolean fulltext search. @item Fixed bug in updating fulltext key from/to @code{NULL}. No coredump anymore. Fixed core dump bug in updating fulltext key from/to @code{NULL}. @item ODBC compatibility: added @code{BIT_LENGTH()} function. ODBC compatibility: Added @code{BIT_LENGTH()} @item Fixed core dump bug in @code{GROUP BY BINARY column}. @item Added support for @code{NULL} keys in HEAP tables. @item Use index for @code{ORDER BY} in queries of type: @code{SELECT * FROM t WHERE key_part1=1 ORDER BY key_part1 DESC,key_part2 DESC} @item Fixed bug in @code{FLUSH QUERY CACHE}. @item Loading @@ -48056,6 +48155,9 @@ Added @code{CAST()} and @code{CONVERT()} functions. The @code{CAST} and want to create a column with a specific type in a @code{CREATE ... SELECT}. For more information, read @ref{Cast Functions}. @item @code{CREATE ... SELECT} on @code{DATE} and @code{TIME} functions now create columns of the expected type. @item Changed order of how keys are created in tables. @item Added a new columns @code{Null} and @code{Index_type} to @code{SHOW INDEX}.
heap/_check.c +4 −2 Original line number Diff line number Diff line Loading @@ -79,9 +79,11 @@ static int check_one_key(HP_KEYDEF *keydef, uint keynr, ulong records, } DBUG_PRINT("info", ("records: %ld seeks: %d max links: %d hitrate: %.2f", records,seek,max_links,(float) seek / (float) (records ? records : 1))); records,seek,max_links, (float) seek / (float) (records ? records : 1))); if (print_status) printf("Key: %d records: %ld seeks: %d max links: %d hitrate: %.2f\n", keynr, records, seek, max_links, (float) seek / (float) records); keynr, records, seek, max_links, (float) seek / (float) (records ? records : 1)); return error; }
heap/heapdef.h +1 −0 Original line number Diff line number Diff line Loading @@ -70,6 +70,7 @@ extern int _hp_rec_key_cmp(HP_KEYDEF *keydef,const byte *rec1, extern int _hp_key_cmp(HP_KEYDEF *keydef,const byte *rec, const byte *key); extern void _hp_make_key(HP_KEYDEF *keydef,byte *key,const byte *rec); extern my_bool hp_if_null_in_key(HP_KEYDEF *keyinfo, const byte *record); extern int _hp_close(register HP_INFO *info); extern void _hp_clear(HP_SHARE *info); Loading
heap/hp_hash.c +82 −6 Original line number Diff line number Diff line Loading @@ -158,11 +158,22 @@ ulong _hp_hashnr(register HP_KEYDEF *keydef, register const byte *key) { uchar *pos=(uchar*) key; key+=seg->length; if (seg->null_bit) { key++; /* Skipp null byte */ if (*pos) /* Found null */ { nr^= (nr << 1) | 1; continue; } pos++; } if (seg->type == HA_KEYTYPE_TEXT) { for (; pos < (uchar*) key ; pos++) { nr^=(ulong) ((((uint) nr & 63)+nr2)*((uint) my_sort_order[(uint) *pos]))+ (nr << 8); nr^=(ulong) ((((uint) nr & 63)+nr2) * ((uint) my_sort_order[(uint) *pos])) + (nr << 8); nr2+=3; } } Loading @@ -188,11 +199,20 @@ ulong _hp_rec_hashnr(register HP_KEYDEF *keydef, register const byte *rec) for (seg=keydef->seg,endseg=seg+keydef->keysegs ; seg < endseg ; seg++) { uchar *pos=(uchar*) rec+seg->start,*end=pos+seg->length; if (seg->null_bit) { if (rec[seg->null_pos] & seg->null_bit) { nr^= (nr << 1) | 1; continue; } } if (seg->type == HA_KEYTYPE_TEXT) { for (; pos < end ; pos++) { nr^=(ulong) ((((uint) nr & 63)+nr2)*((uint) my_sort_order[(uint) *pos]))+ (nr << 8); nr^=(ulong) ((((uint) nr & 63)+nr2)* ((uint) my_sort_order[(uint) *pos]))+ (nr << 8); nr2+=3; } } Loading Loading @@ -234,6 +254,16 @@ ulong _hp_hashnr(register HP_KEYDEF *keydef, register const byte *key) { uchar *pos=(uchar*) key; key+=seg->length; if (seg->null_bit) { key++; if (*pos) { nr^= (nr << 1) | 1; continue; } pos++; } if (seg->type == HA_KEYTYPE_TEXT) { for (; pos < (uchar*) key ; pos++) Loading Loading @@ -264,6 +294,14 @@ ulong _hp_rec_hashnr(register HP_KEYDEF *keydef, register const byte *rec) for (seg=keydef->seg,endseg=seg+keydef->keysegs ; seg < endseg ; seg++) { uchar *pos=(uchar*) rec+seg->start,*end=pos+seg->length; if (seg->null_bit) { if (rec[seg->null_pos] & seg->null_bit) { nr^= (nr << 1) | 1; continue; } } if (seg->type == HA_KEYTYPE_TEXT) { for ( ; pos < end ; pos++) Loading Loading @@ -295,6 +333,14 @@ int _hp_rec_key_cmp(HP_KEYDEF *keydef, const byte *rec1, const byte *rec2) for (seg=keydef->seg,endseg=seg+keydef->keysegs ; seg < endseg ; seg++) { if (seg->null_bit) { if ((rec1[seg->null_pos] & seg->null_bit) != (rec2[seg->null_pos] & seg->null_bit)) return 1; if (rec1[seg->null_pos] & seg->null_bit) continue; } if (seg->type == HA_KEYTYPE_TEXT) { if (my_sortcmp(rec1+seg->start,rec2+seg->start,seg->length)) Loading @@ -309,14 +355,24 @@ int _hp_rec_key_cmp(HP_KEYDEF *keydef, const byte *rec1, const byte *rec2) return 0; } /* Compare a key in a record to a hole key */ /* Compare a key in a record to a whole key */ int _hp_key_cmp(HP_KEYDEF *keydef, const byte *rec, const byte *key) { HP_KEYSEG *seg,*endseg; for (seg=keydef->seg,endseg=seg+keydef->keysegs ; seg < endseg ; seg++) for (seg=keydef->seg,endseg=seg+keydef->keysegs ; seg < endseg ; key+= (seg++)->length) { if (seg->null_bit) { int found_null=test(rec[seg->null_pos] & seg->null_bit); if (found_null != (int) *key++) return 1; if (found_null) continue; } if (seg->type == HA_KEYTYPE_TEXT) { if (my_sortcmp(rec+seg->start,key,seg->length)) Loading @@ -327,7 +383,6 @@ int _hp_key_cmp(HP_KEYDEF *keydef, const byte *rec, const byte *key) if (bcmp(rec+seg->start,key,seg->length)) return 1; } key+=seg->length; } return 0; } Loading @@ -341,7 +396,28 @@ void _hp_make_key(HP_KEYDEF *keydef, byte *key, const byte *rec) for (seg=keydef->seg,endseg=seg+keydef->keysegs ; seg < endseg ; seg++) { if (seg->null_bit) *key++= test(rec[seg->null_pos] & seg->null_bit); memcpy(key,rec+seg->start,(size_t) seg->length); key+=seg->length; } } /* Test if any of the key parts are NULL. Return: 1 if any of the key parts was NULL 0 otherwise */ my_bool hp_if_null_in_key(HP_KEYDEF *keydef, const byte *record) { HP_KEYSEG *seg,*endseg; for (seg=keydef->seg,endseg=seg+keydef->keysegs ; seg < endseg ; seg++) { if (seg->null_bit && (record[seg->null_pos] & seg->null_bit)) return 1; } return 0; }