Производительность SQL Server 2005: отдельная или полная таблица в выражении WHERE IN - PullRequest
2 голосов
/ 08 октября 2008

У нас есть две таблицы:

  • Документ: id, title, document_type_id, showon_id
  • DocumentType: id, name
  • Отношение: DocumentType имеет много документов. (Document.document_type_id = DocumentType.id)

Мы хотим получить список всех типов документов для одного данного 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 земле)

Ответы [ 5 ]

11 голосов
/ 08 октября 2008

Вы можете использовать объединение:

SELECT DISTINCT DocumentType.*
FROM DocumentType
INNER JOIN Document
ON DocumentType.id=Document.document_type_id
WHERE Document.showon_id = 42

Я думаю, что это лучший способ сделать это.

3 голосов
/ 09 октября 2008

Для лучшей производительности вы должны использовать:

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, чтобы внутренние объединения могли отфильтровывать ненужные сравнения .

2 голосов
/ 08 октября 2008

Используйте EXISTS. Иногда это быстрее, но, на мой взгляд, более читабельно, чем DISTINCT и JOIN. Просто для удовольствия, пожалуйста, ответьте с планом запроса для этого запроса и JOIN выше, и посмотрите, если что-то отличается (они могут быть оптимизированы до того же плана). Если они одинаковые, я бы порекомендовал EXISTS, так как он ближе к «простому языку» описания, чем к JOIN (потому что вам не нужны никакие данные из Document и т. Д.)

SELECT whatever
  FROM DocumentType dt
 WHERE EXISTS( SELECT *
                 FROM Document 
                WHERE dt.id     = document_type_id
                  AND showon_id = 42)

Чтобы получить план запроса (ref: http://msdn.microsoft.com/en-us/library/ms180765(SQL.90).aspx), do:

SET SHOWPLAN_TEXT ON
GO

SELECT ...
GO
1 голос
/ 08 октября 2008

С моей точки зрения, это не должно иметь никакого значения в SQL Server (но кто знает, как это реализовано).

Думайте об этом так: чтобы вернуть набор результатов, серверу нужно перейти в таблицу Document и получить все document_type_id, ГДЕ showon_id = 42. В процессе извлечения document_type_ids (например, путем поиска по индексу) он помещает их в хеш Таблица. Когда этот процесс завершится, хеш-таблица все равно будет содержать разные значения. После этого выполнение запроса идет внутри таблицы Document_Type, сканирует первичный ключ и исследует хеш-таблицу. Обратите внимание, что это зависит, например, может быть, более эффективно не использовать хеш-таблицу, когда ожидаемое количество строк в таблице Document ниже, чем в Document_Type, но в целом вы получаете тот же план запроса, что и для только что предложенного wmasm запроса.

0 голосов
/ 09 октября 2008

Продолжить на Ответ Мэтта :

Я включил план запросов и протестировал следующие четыре разных запроса:

  • 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]))

Я не уверен, что означают каждая строка и элемент, но кажется, что с точки зрения производительности не имеет значения, как вы строите запрос для такого рода проблем ...

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