У нас есть две таблицы:
Мы хотим получить список всех типов документов для одного заданного ShowOn_Id.
Мы видим две возможности:
SELECT DocumentType.*
FROM DocumentType
WHERE DocumentType.id IN (
SELECT DISTINCT Document.document_type_id FROM Document WHERE showon_id = 42
);
SELECT DocumentType.*
FROM DocumentType
WHERE DocumentType.id IN (
SELECT Document.document_type_id FROM Document WHERE showon_id = 42
);
Наш вопрос: когда и если лучше использовать DISTINCT для получения меньшего набора записей по сравнению с извлечением всей таблицы и оператором IN, перемещающим таблицу до первого совпадения. (Мы предполагаем, что это то, что он делает ;-))
Это по-разному для разных баз данных, есть ли общий ответ?
Или есть лучший способ сделать это? (Мы находимся в стране .NET)





Вы можете использовать соединение:
SELECT DISTINCT DocumentType.*
FROM DocumentType
INNER JOIN Document
ON DocumentType.id=Document.document_type_id
WHERE Document.showon_id = 42
Я думаю, это лучший способ сделать это.
Как указал ligged78, и вы тоже узнали, я был бы очень удивлен, если бы план запроса возвращал разные вещи. В конце концов, производительность должна основываться на дизайне вашей таблицы / индекса, а не на синтаксисе запроса.
С моей точки зрения, это не должно иметь никакого значения внутри SQL Server (но кто знает, как это реализовано).
Подумайте об этом так: чтобы вернуть набор результатов, серверу необходимо перейти в таблицу Document и получить все document_type_id WHERE showon_id = 42. В процессе получения document_type_ids (например, путем поиска по индексу) он помещает их в хеш-таблицу. Когда этот процесс завершится, хеш-таблица в любом случае будет содержать различные значения. После этого выполнение запроса переходит в таблицу Document_Type, сканирует первичный ключ и исследует хеш-таблицу. Обратите внимание, что это зависит, например, возможно, более эффективно не использовать хеш-таблицу, когда ожидаемое количество строк из таблицы Document мало по сравнению с Document_Type, но в целом вы получаете тот же план запроса, что и для только что предложенного запроса wmasm.
Это имеет смысл. И да, набор результатов должен быть одинаковым для обоих запросов. Итак, вопрос: отфильтровывает ли сервер повторяющиеся значения самостоятельно (и влияет ли эта фильтрация на производительность?), И если да, то делает ли он это таким же образом при использовании DISTINCT? (-> такая же скорость)
Вы можете попробовать измерить производительность ваших данных для всех типов запросов. См. Инструмент datamanipulation.net/SQLQueryStress.
Используйте EXISTS. Иногда это быстрее, но, на мой взгляд, более читабельно, чем DISTINCT и JOIN. Просто для удовольствия, пожалуйста, ответьте с планом запроса для этого запроса и ПРИСОЕДИНЕНИЕМ выше и посмотрите, не изменилось ли что-нибудь (они могут быть оптимизированы до того же плана). Если они одинаковы, я бы рекомендовал EXISTS, поскольку он ближе к описанию на "простом языке", чем к JOIN (потому что вам не нужны какие-либо данные из документа и т. д.)
SELECT whatever
FROM DocumentType dt
WHERE EXISTS( SELECT *
FROM Document
WHERE dt.id = document_type_id
AND showon_id = 42)
Чтобы получить план запроса (ссылка: http://msdn.microsoft.com/en-us/library/ms180765(SQL.90).aspx), выполните:
SET SHOWPLAN_TEXT ON
GO
SELECT ...
GO
Следите за Мэтт ответ:
Я включил план запроса и протестировал следующие четыре разных запроса, которые возникли на данный момент:
SELECT DocumentType.* FROM DocumentType WHERE DocumentType.id IN (SELECT DISTINCT Document.document_type_id FROM Document WHERE showon_id = 42);
SELECT DocumentType.* FROM DocumentType WHERE DocumentType.id IN (SELECT Document.document_type_id FROM Document WHERE showon_id = 42);
SELECT DISTINCT DocumentType.* FROM DocumentType INNER JOIN Document ON DocumentType.id=Document.document_type_id WHERE Document.showon_id = 42;
SELECT DocumentType.* FROM DocumentType WHERE EXISTS ( SELECT * FROM Document WHERE DocumentType.id=Document.document_type_id AND showon_id = 42);
План запроса для всех четырех запросов оказался одинаковым:
|--Hash Match(Right Semi Join, HASH:([Document].[document_type_id])=([DocumentType].[Id]))
|--Hash Match(Inner Join, HASH:([Document].[Title], [Uniq1005])=([Document].[Title], [Uniq1005]), RESIDUAL:([Document].[Title] as [Document].[Title] = [Document].[Title] as [Document].[Title] AND [Uniq1005] = [Uniq1005]))
| |--Index Seek(OBJECT:([Document].[IX_Document_3] AS [Document]), SEEK:([Document].[showon_id]=(1)) ORDERED FORWARD)
| |--Index Scan(OBJECT:([Document].[IX_Document_1] AS [Document]))
|--Table Scan(OBJECT:([DocumentType] AS [DocumentType]))
Я не уверен, что означает каждая строка и элемент, но кажется, что с точки зрения производительности не имеет значения, как вы строите запрос для такого рода проблем ...
Для лучшей производительности вы должны использовать:
SELECT DISTINCT dt.*
FROM
DocumentType dt
INNER JOIN Document d ON dt.id=d.document_type_id and d.showon_id = 42
Соединения очень эффективны при соединении нескольких таблиц, когда в качестве вложенного запроса в предложении Where потребуется выполнить отдельный выбор результатов, который отфильтрует результаты предложения From. Оператор соединения также более читабелен.
Я бы также поместил индекс на showon_id в дополнение к первичным ключам и отношениям внешнего ключа.
Мой ответ отличается от ответа wmasm только перемещением фильтра showon_id во внутреннее соединение. Для MS SQL 2k5, я думаю, интерпретатор достаточно умен, чтобы делать это автоматически, но вы всегда хотите работать с наименьшим возможным набором результатов. Добавление ваших фильтров к внутренним операторам соединения может ограничить количество строк, с которыми должен работать запрос при объединении множества таблиц вместе. Если вы это сделаете, вы должны понимать, что это происходит для каждого сравнения строк, поэтому сложные фильтры (такие как x = '% a' или вызовы функций) лучше оставить для предложения Where, чтобы внутренние объединения могли отфильтровать ненужные сравнения .
Спасибо, это, наверное, самое простое решение проблемы ... этот запрос быстрее, чем IN-подзапрос?