Разница между использованием из списка и левым соединением - PullRequest
3 голосов
/ 10 июля 2009

У меня есть две таблицы базы данных со следующей структурой:

действия:

action_id   int(11) primary key
action_name     varchar(255)
action_module   varchar(45)

разрешения:

perm_id     int(11) primary key
perm_role   int(11) 
perm_action     int(11)
perm_status     int(11)

Теперь я должен проверить, есть ли запись в таблице разрешений для данной роли в таблице разрешений, введя следующие данные: perm_role, action_name и action_module.

Я подготовил два запроса, чтобы проверить вышеупомянутое условие, но я не знаю, какой из них лучше. Может кто-нибудь подсказать мне, как найти лучший:

Запрос 1

SELECT perm_id FROM permissions 
LEFT JOIN actions ON action_id=perm_action 
WHERE perm_role=1 AND action_name='add' AND action_module='employee';

Запрос 2:

SELECT perm_id FROM permissions, actions 
WHERE perm_role=1 AND perm_action=action_id AND action_name='add' 
AND action_module='employee';

Мне нужно оптимизировать эти запросы, поскольку они должны выполняться при каждом обращении к серверу. Среда разработки PHP-5.2.10 и MySQL-5.1.35

Ответы [ 3 ]

2 голосов
/ 10 июля 2009

Ваш первый вариант с явным объединением двух таблиц в явных полях - лучший выбор по ряду причин.

Второй вариант по-прежнему является эффективным объединением, даже если вы не используете ключевое слово JOIN, поскольку оно подразумевает подразумеваемое объединение. Это означает, что ваш движок базы данных будет эффективно выполнять собственное соединение между списком таблиц.

В MySQL это подразумеваемое соединение является CROSS JOIN, которое в MySQL эквивалентно INNER JOIN, однако следует помнить, что CROSS JOIN НЕ эквивалентно INNER JOIN в «стандартном» SQL, поэтому ваш второй запрос может хорошо работает, если построено правильно с правильной фильтрацией, это неоднозначно.

В результате, ваш второй запрос (с подразумеваемым соединением) может эффективно создать декартово произведение между двумя таблицами (где фактически каждая строка из одной таблицы объединяется с каждой строкой из другой таблицы). Вы почти наверняка НЕ ​​захотите этого, и это один из способов снизить производительность SQL-запроса, особенно если хотя бы одна из таблиц содержит довольно много строк! Даже если ваши предложения фильтра (т.е. ваши предложения WHERE) могут правильно отфильтровывать строки, которые вы не хотите возвращать только правильный набор результатов, это будет менее эффективно, чем явное определение ваших собственных явных объединений (даже если большинство механизмов баз данных будут попытка оптимизировать такие подразумеваемые запросы). Ваш первый запрос использует явное LEFT JOIN, и поэтому декартово произведение не должно быть возможным (при условии, что вы соединяетесь с «разумными» полями - из вашего вопроса кажется, что табличное отношение «разумное»).

Также следует помнить, что приоритет простого указания объединений между таблицами путем перечисления таблиц с разделителем-запятой ниже, чем фактические явные операторы JOIN (по крайней мере, начиная с MySQL v5.x). Это может привести к неверным результатам запроса, особенно в случай объединения трех или более таблиц, где неоднозначность в выражении запроса также затрудняет определение приоритета, и, следовательно, один и тот же запрос может давать совершенно разные результаты в разных версиях базы данных. См. эту ссылку для получения дополнительной информации.

Лучшим источником информации для различных типов JOIN в MySQL является сама документация MySQL, а страницу, относящуюся к соединениям, можно найти здесь:

12.2.8.1. Синтаксис JOIN (MySql v5)

12.2.9.1. Синтаксис JOIN (MySql v6)

Только для скорости, я процитировал наиболее подходящие разделы ниже:

INNER JOIN и (запятая) семантически эквивалентны в отсутствие условия соединения: оба производят декартово произведение между указанными таблицами (то есть каждая строка в первой таблице объединяется с каждой строкой в вторая таблица).

Однако приоритет оператора запятой меньше, чем у INNER JOIN, CROSS JOIN, LEFT JOIN и т. Д. Если вы смешиваете запятые объединения с другими типами соединений, когда существует условие соединения, может произойти ошибка формы Неизвестный столбец «col_name» в «условии».

-

Оценка многомерных естественных объединений отличается очень важным способом, который влияет на результат соединений NATURAL или USING и может потребовать переписывания запросов. Предположим, что у вас есть три таблицы t1 (a, b), t2 (c, b) и t3 (a, c), каждая из которых имеет одну строку: t1 (1,2), t2 (10,2) и t3 ( 7,10). Предположим также, что у вас есть это ЕСТЕСТВЕННОЕ СОЕДИНЕНИЕ на трех таблицах:

ВЫБРАТЬ ... ИЗ t1 ЕСТЕСТВЕННОЕ СОЕДИНЕНИЕ t2 ЕСТЕСТВЕННОЕ СОЕДИНЕНИЕ t3;

Ранее левый операнд второго соединения считался t2, тогда как это должно быть вложенное соединение (t1 NATURAL JOIN t2). В результате столбцы t3 проверяются на наличие общих столбцов только в t2, и, если t3 имеет общие столбцы с t1, эти столбцы не используются в качестве столбцов равного соединения. Таким образом, ранее предыдущий запрос был преобразован в следующее уравнение:

ВЫБРАТЬ ... ОТ t1, t2, t3ГДЕ t1.b = t2.b И t2.c = t3.c;

В этом соединении отсутствует еще один предикат равного соединения (t1.a = t3.a). В результате он выдает одну строку, а не пустой результат, который должен. Правильный эквивалентный запрос:

ВЫБРАТЬ ... ОТ t1, t2, t3 ГДЕ t1.b = t2.b И t2.c = t3.c И t1.a = t3.a;

Если вам требуется тот же результат запроса в текущих версиях MySQL, что и в более старых версиях, перепишите естественное соединение в качестве первого равного соединения.

-

Ранее оператор запятой (,) и JOIN имели одинаковый приоритет, поэтому выражение соединения t1, t2 JOIN t3 интерпретировалось как ((t1, t2) JOIN t3). Теперь JOIN имеет более высокий приоритет, поэтому выражение интерпретируется как (t1, (t2 JOIN t3)). Это изменение влияет на операторы, использующие предложение ON, потому что это предложение может ссылаться только на столбцы в операндах соединения, а изменение в приоритете меняет интерпретацию того, чем являются эти операнды.

Пример:

CREATE TABLE t1 (i1 INT, j1 INT); CREATE TABLE t2 (i2 INT, j2 INT); CREATE TABLE t3 (i3 INT, j3 INT); ВСТАВЬТЕ В ЗНАЧЕНИЯ t1 (1,1); INSERT INTO t2 VALUES (1,1); INSERT INTO T3 VALUES (1,1); ВЫБРАТЬ * ОТ t1, t2 ПРИСОЕДИНИТЬСЯ к t3 ВКЛ (t1.i1 = t3.i3);

Ранее SELECT был допустимым из-за неявной группировки t1, t2 как (t1, t2). Теперь JOIN имеет приоритет, поэтому операндами для предложения ON являются t2 и t3. Поскольку t1.i1 не является столбцом ни в одном из операндов, результатом будет ошибка Unknown column 't1.i1' в 'on clause'. Чтобы разрешить обработку объединения, сгруппируйте первые две таблицы явно в круглых скобках, чтобы операндами для предложения ON были (t1, t2) и t3:

ВЫБРАТЬ * ИЗ (t1, t2) ПРИСОЕДИНИТЬСЯ t3 ВКЛ (t1.i1 = t3.i3);

В качестве альтернативы, избегайте использования оператора запятой и используйте вместо этого JOIN:

ВЫБРАТЬ * ОТ t1 ПРИСОЕДИНИТЬСЯ t2 ПРИСОЕДИНИТЬСЯ t3 ВКЛ (t1.i1 = t3.i3);

Это изменение также применяется к операторам, которые смешивают оператор запятой с INNER JOIN, CROSS JOIN, LEFT JOIN и RIGHT JOIN, и все они теперь имеют более высокий приоритет, чем оператор запятой.

0 голосов
/ 10 июля 2009

Я бы пошел с первым: связь между таблицами намного яснее в этой версии. Я даже не уверен, что второй правильный. Что касается скорости: профилируйте запрос или просто синхронизируйте запросы.

Дополнительное замечание по второму запросу: возможно, здесь достаточно ВНУТРЕННЕГО СОЕДИНЕНИЯ.

0 голосов
/ 10 июля 2009

Запрос 1 лучше, потому что если вы не используете join, то sql использует свой собственный совет для соединения и это какое-то время нехорошо, и, возможно, потребуется время, чтобы дать вам результат. и сначала в соединении добавьте таблицу с несколькими строками, которая будет работать быстрее, и вместо использования '=' будет работать быстрее. Последовательность фильтров также важна.

Я предлагаю еще одну вещь - использовать actionname и получить actionid из таблицы действий. и после этого используйте идентификатор в критериях поиска в таблице разрешений

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...