T-SQL. Что лучше: присоединиться затем к группе или сгруппироваться затем присоединиться

#sql #sql-server #tsql

#sql #sql-server #tsql

Вопрос:

У меня есть 2 таблицы:

Порядок:

 IdProduct (what is ordered - FK to Product table)
Price  (what is the total price for offer)
Piece  (i.e. count - how many products are ordered?) 
  

Продукт:

 Id
Name
  

И есть 2 инструкции SQL, которые возвращают товары по лучшей цене за единицу:

Инструкция # 1:

 SELECT 
    p.Name,
    MIN (Price / Piece) AS MinPrice
FROM 
    [ORDER] o
JOIN 
    Product p ON IdProduct = p.Id
GROUP BY
    p.Name
  

Инструкция # 2:

 SELECT p.Name, t.MinPrice 
FROM
    (SELECT IdProduct, MIN(Price/Piece) AS MinPrice 
     FROM [Order] 
     GROUP BY IdProduct) t 
JOIN 
    Product p ON p.Id = t.IdProduct
  

Я исследовал планы выполнения в Microsoft SQL Server Management Studio, и они выглядят очень похожими, хотя у меня есть несколько замечаний:

  1. Почему первый план использует [order by name] инструкцию? На выходе отображаются названия продуктов, упорядоченные «asc», даже если я не использую инструкцию по порядку T-SQL

  2. Этот неявный «порядок по имени asc» замедляет первый sql. Когда я добавляю «заказ по имени asc» во второй sql — они становятся идентичными по стоимости плана выполнения.

  3. Я предполагаю, что sql # 2 должен превосходить # 1 из-за:

    a). Он группирует по PK (то есть целому числу), а не по имени (которое имеет nvarchar тип столбца, более того, оно не индексируется) b). Он объединяет таблицы только после того, как первая из них сгруппирована, что должно максимизировать производительность (по сравнению с объединением полных двух таблиц, как ожидается, для первого sql) — но планы выполнения, тем не менее, показывают ту же предполагаемую стоимость выполнения.

Какую инструкцию SQL вы бы предпочли и почему? Может быть, у вас есть своя версия для инструкции SQL?

Комментарии:

1. заказ by создается группой by .и не стоил операции group by подразумевает группу по умолчанию by as .. если вы добавляете явный заказ by . оптимизатор sql должен знать, что в данном случае это не нужно .. . В этом случае первый запрос лучше .. второй подразумевает создание временной таблицы

2. Когда дело доходит до вопросов производительности, часто бывает полезно запустить некоторые тесты. Например, создайте макет миллиона строк для тестирования, зафиксируйте время начала до первого запроса, зафиксируйте время окончания после выполнения запроса, затем вычислите время, затраченное на выполнение. Проделайте то же самое со вторым запросом. Возможно, потребуется запустить каждый тест 5 раз или около того, чтобы получить несколько данных о времени выполнения для каждого запроса.

Ответ №1:

Лично я бы предпочел утверждение 2. Моя причина сильно отличается от того, что вы ожидаете.

Вы поняли, что ваши 2 инструкции не созданы для возврата одинаковых результатов?

Первый запрос не группирует записи по продукту, он группирует их по названию продукта. В большинстве БД вызываемые столбцы name никогда не бывают уникальными. Следовательно, 2 GROUP BY не эквивалентны (возможно, случается, что ваши тестовые данные делают два результата идентичными, но здесь играет только удача).

Вот что должно было быть написано:

 SELECT 
    p.Name,
    MIN (Price / Piece) AS MinPrice
FROM 
    [ORDER] o
JOIN 
    Product p ON IdProduct = p.Id
GROUP BY
    IdProduct, p.Name /* GROUP BY PK on Product */
  

ИМХО, 2-й синтаксис является хорошей защитой от такого рода ошибок. Я советую вам использовать именно это.
Это избавит вас от некоторых проблем при работе с устаревшей базой данных с более чем 100 таблицами вместо 2 таблиц, которые вы создали и заполнили самостоятельно, не говоря уже о том, что 1-я инструкция может долгое время корректно работать, пока, наконец, Product.name не станет неуникальной.

Кстати, неявный order by намекал, что он не использует столбец PK. Это не замедляет выполнение вашего запроса. Это упорядочивание записей в рамках подготовки к GROUP BY


PS: чтобы ответить на ваш вопрос о производительности, ваше второе утверждение по сравнению с тем, о котором я написал, должно быть очень похоже (благодаря планировщику запросов).
Иногда я видел, что 1-я инструкция выполняется значительно медленнее, но никогда существенно быстрее, чем 2-я (если исключения существуют, они достаточно редки, чтобы я их пропустил).

PPS: Поскольку вы агрегируете данные из Product , добавление WHERE в поле из Order может усложнить работу с точки зрения производительности.
Я боюсь, что это те вещи, которые вам приходится пробовать каждый раз, когда разрабатывается новый запрос.