Являются ли эти два запроса одинаковыми - GROUP BY и DISTINCT? - PullRequest
7 голосов
/ 28 июля 2010

Эти два запроса, похоже, возвращают одинаковые результаты. Это случайно или они действительно одинаковы?

1

SELECT t.ItemNumber,
  (SELECT TOP 1 ItemDescription
   FROM Transactions
   WHERE ItemNumber = t.ItemNumber
   ORDER BY DateCreated DESC) AS ItemDescription
FROM Transactions t
GROUP BY t.ItemNumber

2

SELECT DISTINCT(t.ItemNumber),
  (SELECT TOP 1 ItemDescription
   FROM Transactions
   WHERE ItemNumber = t.ItemNumber
   ORDER BY DateCreated DESC) AS ItemDescription
FROM Transactions t

Немного объяснений: Я пытаюсь получить четкий список предметов из таблицы, полной транзакций. Для каждого элемента я ищу ItemNumber (идентифицирующее поле) и самое последнее ItemDescription.

Ответы [ 8 ]

5 голосов
/ 28 июля 2010

Ваш пример №2 заставил меня немного почесать голову - я подумал про себя: "Вы не можете DISTINCT один столбец, что бы это значило?" - пока я не понял, что происходит.

Когда у вас есть

SELECT DISTINCT(t.ItemNumber)

вы не , несмотря на внешность, фактически запрашивая различные значения из t.ItemNumber! Ваш пример # 2 фактически анализируется так же, как

SELECT DISTINCT
  (t.ItemNumber)
  ,
  (SELECT TOP 1 ItemDescription
   FROM Transactions
   WHERE ItemNumber = t.ItemNumber
   ORDER BY DateCreated DESC) AS ItemDescription
FROM Transactions t

с синтаксически правильными, но лишними скобками около t.ItemNumber. * Для набора результатов в целом применяется DISTINCT.

В этом случае, поскольку ваши GROUP BY группируются по столбцу, который на самом деле меняется, вы получите те же результаты. На самом деле я немного удивлен, что SQL Server (в примере GROUP BY) не настаивает на том, чтобы в списке GROUP BY упоминался столбец подзапроса.

4 голосов
/ 28 июля 2010

Те же результаты, но у второго, кажется, есть более дорогой шаг сортировки, чтобы применить DISTINCT к моему быстрому тесту.

Оба были выбиты из поля зрения ROW_NUMBER хотя ...

with T as
(
SELECT ItemNumber, 
       ItemDescription,
       ROW_NUMBER() OVER ( PARTITION BY ItemNumber ORDER BY DateCreated DESC) AS RN
FROM Transactions
)
SELECT * FROM T
WHERE RN=1

edit ... что в свою очередь было выбито решением Джо в моей тестовой настройке.

Планы http://img842.imageshack.us/img842/4105/executionplan.png

Тестовая настройка

CREATE TABLE Transactions
(
ItemNumber INT not null,
ItemDescription VARCHAR(50) not null,
DateCreated DATETIME not null
)

INSERT INTO Transactions
SELECT 
number, NEWID(),DATEADD(day, cast(rand(CAST(newid() as varbinary))*10000 
  as int),getdate()) 
FROM master.dbo.spt_values

ALTER TABLE dbo.Transactions ADD CONSTRAINT
    PK_Transactions PRIMARY KEY CLUSTERED 
    (ItemNumber,DateCreated) 
3 голосов
/ 28 июля 2010

На основании данных и простых запросов оба будут возвращать одинаковые результаты. Тем не менее, основные операции очень разные.

DISTINCT, как указывал AakashM, применяется к всем значениям столбцов, включая значения из подвыборов и вычисленных столбцов. Все, что DISTINCT делает, это удаляет дубликаты, основанные на всех задействованных столбцах, из видимости . Вот почему это обычно считается хаком, потому что люди будут использовать его, чтобы избавиться от дубликатов, не понимая, почему запрос возвращает их в первую очередь (потому что они должны использовать IN или EXISTS вместо объединения, как правило, ). PostgreSQL является единственной базой данных, о которой я знаю, с предложением DISTINCT ON, которая работает так, как, вероятно, и предполагал OP.

Предложение GROUP BY отличается - оно в основном используется для группировки для точного использования функции агрегирования. Для сервера этой функции значения столбцов будут уникальными в зависимости от того, что определено в предложении GROUP BY. Этот запрос никогда не будет нуждаться в DISTINCT, потому что интересующие значения уже уникальны.

Заключение

Это плохой пример, потому что он изображает DISTINCT и GROUP BY как равные, когда они не.

3 голосов
/ 28 июля 2010

Если вы работаете по крайней мере в 2005 году и можете использовать CTE , это немного чище ИМХО.

РЕДАКТИРОВАТЬ: Как указано в ответ Мартина , это также работает намного лучше.

;with cteMaxDate as (
    select t.ItemNumber, max(DateCreated) as MaxDate
        from Transactions t
        group by t.ItemNumber
)
SELECT t.ItemNumber, t.ItemDescription
    FROM cteMaxDate md
        inner join Transactions t
            on md.ItemNumber = t.ItemNumber
                and md.MaxDate = t.DateCreated
2 голосов
/ 28 июля 2010

Поскольку вы не используете никаких агрегатных функций, SQL Server должен быть достаточно умным, чтобы рассматривать GROUP BY как DISTINCT.

Вам также может быть интересно ознакомиться со следующей записью переполнения стека для дальнейшего чтения по этой теме:

2 голосов
/ 28 июля 2010

Да, они будут возвращать одинаковые результаты.

1 голос
/ 28 июля 2010

Да, они возвращают одинаковые результаты.

Обычно предложение group by (найдено здесь ) группирует строки по указанному столбцу, так что если у вас есть сумма в вашем операторе select.Таким образом, если у вас есть таблица типа:

O_Id        OrderDate   OrderPrice      Customer
1           2008/11/12  1000            Hansen
2           2008/10/23  1600            Nilsen
3           2008/09/02  700             Hansen
4           2008/09/03  300             Hansen
5           2008/08/30  2000            Jensen
6           2008/10/04  100             Nilsen

Если вы группируете по клиенту и запрашиваете сумму или цену заказа, вы получите

Customer    SUM(OrderPrice)
Hansen          2000
Nilsen             1700
Jensen          2000

В отличие от этого, здесь ) просто делает так, чтобы у вас не было повторяющихся строк.В этом случае исходная таблица останется прежней, поскольку каждая строка отличается от других.

1 голос
/ 28 июля 2010

GROUP BY необходимо для правильного возврата результатов при использовании агрегатных функций в SQL-запросе.Поскольку вы не используете агрегатную функцию, в GROUP BY нет необходимости, поэтому запросы одинаковы.

...