SELECT *
FROM tag
JOIN tag_post ON tag_post.tag_id=tag.id
JOIN post ON tag_post.post_id=post.id
WHERE tag.tag=mysql;
SELECT * FROM tag WHERE tag = "mysql";
SELECT * FROM tag_post WHERE tag_id = 1234;
SELECT * FROM post WHERE post.id IN (123,456,567,9098,8904);
SELECT SQL_NO_CACHE COUNT(*) FROM sakila.film_actor;
mysql> SHOW STATUS LIKE last_query_cost;
Variable_name | Value |
---|---|
Last_query_cost | 1040.599000 |
Таблицы не обязательно соединять именно в том порядке, который указан в запросе.
MySQL применяет алгебраические преобразования для упрощения выражений и приведения их к каноническому виду. Она умеет так-же вычислять константные выражения, исключая заведомо невыполнимые и всегда выполняющиеся условия.
(5 = 5 AND a > 5) -> a > 5
(a < b AND b = c) AND a = 5 -> b > 5 AND b = c AND a = 5
Наличие индексов и сведений о возможности хранения NULL-значений в столбцах часто позволяет вообще не вычислять эти выражения. Если применена такая оптимизация, то в плане, выведенном командой EXPLAIN, будет присутствовать фраза «Select tables optimized away» (некоторые таблицы исключены при оптимизации).
Если MySQL обнаруживает, что выражение можно свернуть в константу, то делает это на стадии оптимизации.
EXPLAIN SELECT film.film_id, film_actor.actor_id
FROM sakila.film INNER JOIN sakila.film_actor USING (film_id)
WHERE film.film_id = 1;
| id | select_type | table | type | key | ref | rows |
| 1 | SIMPLE | film | const | PRIMARY | const | 1 |
| 1 | SIMPLE | film_actor | ref | idx_fk_film_id | const | 10 |
Если индекс содержит все необходимые запросу столбцы, то MySQL может воспользоваться им, вообще не читая данные таблицы.
MySQL умеет преобразовывать некоторые виды подзапросов в более эффективные эквивалентные формы, сводя их к поиску по индексу.
MySQL может прекратить обработку запроса (или какой-то шаг обработки), как только поймет, что этот запрос или шаг полностью выполнен.
EXPLAIN SELECT film.film_id FROM sakila.film WHERE film_id = -1;
| id |...| Extra |
| 1 |...| Impossible WHERE noticed after reading const tables |
Во многих СУБД оператор IN() – не более чем синоним нескольких условий OR, поскольку логически они эквивалентны. Но не в MySQL, здесь перечисленные в списке IN()значения сортируются, и для работы с ним применяется быстрый двоичный поиск.
MySQL распознает ситуации, когда в некотором запросе два столбца должны быть равны, – например, в условии JOIN, и распространяет условие WHERE на эквивалентные.
SELECT film.film_id
FROM sakila.film
INNER JOIN sakila.film_actor USING(film_id)
WHERE film.film_id > 500;
...
WHERE film.film_id > 500 AND film_actor.film_id > 500
SELECT tbl1.col1, tbl2.col2
FROM tbl1 INNER JOIN tbl2 USING(col3)
WHERE tbl1.col1 IN(5,6);
outer_iter = ITERATOR OVER tbl1 WHERE col1 IN(5,6)
outer_row = outer_iter.next
WHILE outer_row
inner_iter = ITERATOR OVER tbl2 WHERE col3 = outer_row.col3
inner_row = inner_iter.next
WHILE inner_row
OUTPUT [ outer_row.col1, inner_row.col2 ]
inner_row = inner_iter.next
END
outer_row = outer_iter.next
END
Читает указатели на строки и столбцы, упомянутые во фразе ORDER BY, сортирует их, затем проходит по отсортированному списку и снова читает исходные строки, чтобы вывести результат.
Читает все необходимые запросу столбцы, сортирует строки по столбцам, упомянутым во фразе ORDER BY, проходит по отсортированному списку и выводит заданные столбцы. Следовательно, в буфер сортировки поместится меньше строк и надо будет выполнить больше циклов слияния.
SELECT * FROM sakila.film
WHERE film_id IN (
SELECT film_id FROM sakila.film_actor WHERE actor_id = 1
);
SELECT GROUP_CONCAT(film_id)
FROM sakila.film_actor
WHERE actor_id = 1;
-- Result: 1,23,25,106,140,166,277,361,438,499,506,509,605,635
SELECT * FROM sakila.film
WHERE film_id
IN (1,23,25,106,140,166,277,361,438,499,506,509,605,635);
SELECT * FROM sakila.film
WHERE film_id IN (
SELECT film_id FROM sakila.film_actor WHERE actor_id = 1
);
SELECT * FROM sakila.film
WHERE EXISTS (
SELECT * FROM sakila.film_actor
WHERE actor_id = 1 AND film_actor.film_id = film.film_id
);
SELECT film_id, actor_id FROM sakila.film_actor
WHERE actor_id = 1 OR film_id = 1;
SELECT film_id, actor_id FROM sakila.film_actor
WHERE actor_id = 1
UNION ALL
SELECT film_id, actor_id FROM sakila.film_actor
WHERE film_id = 1 AND actor_id <> 1;
SELECT ... FROM tbl WHERE a BETWEEN 1 AND 4 AND b BETWEEN 2 AND 3;
SELECT ... FROM tbl WHERE a IN (1, 2, 3, 4) AND b BETWEEN 2 AND 3;
EXPLAIN SELECT actor_id, MAX(film_id)
FROM sakila.film_actor
GROUP BY actor_id\G
********************* 1. row **************************
id: 1
select_type: SIMPLE
table: film_actor
type: range
possible_keys: NULL
key: PRIMARY
key_len: 2
Ограничения оптимизатора MySQL 241
ref: NULL
rows: 396
Extra: Using index for group-by
SELECT MIN(actor_id) FROM sakila.actor
WHERE first_name = 'PENELOPE';
SELECT actor_id FROM sakila.actor USE INDEX(PRIMARY)
WHERE first_name = 'PENELOPE' LIMIT 1;
UPDATE tbl AS outer_tbl
SET cnt = (
SELECT COUNT(*) FROM tbl AS inner_tbl
WHERE inner_tbl.type = outer_tbl.type
);
ERROR 1093 (HY000): You can’t specify target table
‘outer_tbl’ for update in FROM clause
UPDATE tbl
INNER JOIN (
SELECT type, COUNT(*) AS cnt
FROM tbl
GROUP BY type
) AS der USING (type)
SET tbl.cnt = der.cnt;
COUNT() ― это особая функция, которая решает две очень разные задачи: подсчитывает значения и строки.
Значение ― это выражение, отличное от NULL.COUNT(*) просто подсчитывает количество строк в результирующем наборе.
Если вы хотите знать, сколько строк в результирующем наборе, всегда употребляйте COUNT(*).
Для таблиц типа MyISAM запросы, содержащие функцию COUNT(), выполняются очень быстро.
SELECT COUNT(*) FROM world.City WHERE ID > 5;
SELECT (SELECT COUNT(*) FROM world.City) - COUNT(*)
FROM world.City WHERE ID <= 5;
SELECT COUNT(color = 'blue' OR color= 'red' OR NULL) FROM items;
SELECT COUNT(*) FROM items WHERE color = 'blue' OR color = 'red';
SELECT COUNT(*) FROM items
WHERE color IN ('blue', 'red');
SELECT actor.first_name, actor.last_name, COUNT(*)
FROM sakila.film_actor
INNER JOIN sakila.actor USING (actor_id)
GROUP BY actor.first_name, actor.last_name;
SELECT actor.first_name, actor.last_name, COUNT(*)
FROM sakila.film_actor
INNER JOIN sakila.actor USING (actor_id)
GROUP BY film_actor.actor_id;
SELECT MIN(actor.first_name), MAX(actor.last_name), COUNT(*)
FROM sakila.film_actor
INNER JOIN sakila.actor USING (actor_id)
GROUP BY film_actor.actor_id;
SELECT actor.first_name, actor.last_name, COUNT(*)
FROM sakila.film_actor
INNER JOIN sakila.actor USING (actor_id)
GROUP BY actor.first_name, actor.last_name;
SELECT actor.first_name, actor.last_name, c.cnt
FROM sakila.actor
INNER JOIN (
SELECT actor_id, COUNT(*) AS cnt
FROM sakila.film_actor
GROUP BY actor_id
) AS c USING (actor_id);
SELECT film_id, description
FROM sakila.film
ORDER BY title
LIMIT 50, 5;
SELECT film.film_id, film.description
FROM sakila.film
INNER JOIN (
SELECT film_id FROM sakila.film
ORDER BY title LIMIT 50, 5
) AS lim USING(film_id);
SELECT film_id, description FROM sakila.film
WHERE position BETWEEN 50 AND 54 ORDER BY position;
SELECT SQL_CALC_FOUND_ROWS film_id, description
FROM sakila.film
ORDER BY title
LIMIT 50, 5;
SELECT FOUND_ROWS ();
CREATE TABLE t1(a INT NOT NULL PRIMARY KEY) ENGINE=MyISAM;
CREATE TABLE t2(a INT NOT NULL PRIMARY KEY) ENGINE=MyISAM;
INSERT INTO t1(a) VALUES(1),(2);
INSERT INTO t2(a) VALUES(1),(2);
CREATE TABLE mrg(a INT NOT NULL PRIMARY KEY)
ENGINE=MERGE UNION=(t1, t2) INSERT_METHOD=LAST;
SELECT a FROM mrg;
| a |
| 1 |
| 1 |
| 2 |
| 2 |
CREATE TABLE orders_range (
customer_surname VARCHAR(30),
store_id INT,
salesperson_id INT,
order_date DATE,
note VARCHAR(500)
) ENGINE = InnoDB
PARTITION BY RANGE (YEAR (order_date)) (
PARTITION p_old VALUES LESS THAN(2008),
PARTITION p_2008 VALUES LESS THAN(2009),
PARTITION p_2009 VALUES LESS THAN(MAXVALUE)
);
По диапазону значений
PARTITION BY RANGE (store_id) (
PARTITION p0 VALUES LESS THAN (10),
PARTITION p1 VALUES LESS THAN (20),
PARTITION p3 VALUES LESS THAN (30)
);
По точному списку значений
PARTITION BY LIST(store_id) (
PARTITION pNorth VALUES IN (3,5,6,9,17),
PARTITION pEast VALUES IN (1,2,10,11,19,20)
);
По хэшу от какой-либо функции
PARTITION BY HASH(YEAR(hired))
PARTITIONS 10;
Почти то же самое что и HASH, но по ключу
PARTITION BY KEY(s1)
PARTITIONS 10;