Каковы преимущества запроса с использованием производных таблиц над запросом, не использующим их? - PullRequest
4 голосов
/ 04 мая 2010

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

Например, в следующей статье http://techahead.wordpress.com/2007/10/01/sql-derived-tables/ автор попытался показать преимущества запроса с использованием производной таблицы над запросом без примера с примером, в котором мы хотим создать отчет, показывающий общее количество заказов, которые каждый клиент разместил в 1996 году, и мы хотим, чтобы этот набор результатов включал всех клиентов, в том числе тех, которые не делали заказов в этом году, и тех, которые вообще никогда не размещали заказов (он использует базу данных Northwind).

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

Обычный запрос:

SELECT C.CustomerID, C.CompanyName, COUNT(O.OrderID) AS TotalOrders
FROM Customers C LEFT OUTER JOIN Orders O ON
       C.CustomerID = O.CustomerID AND YEAR(O.OrderDate) = 1996
GROUP BY C.CustomerID, C.CompanyName

Запрос с использованием производной таблицы:

SELECT C.CustomerID, C.CompanyName, COUNT(dOrders.OrderID) AS TotalOrders
FROM Customers C LEFT OUTER JOIN
        (SELECT * FROM Orders WHERE YEAR(Orders.OrderDate) = 1996) AS dOrders
     ON
        C.CustomerID = dOrders.CustomerID
GROUP BY C.CustomerID, C.CompanyName

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

1017 * спасибо *

ОТВЕТИТЬ НА ГБН:

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

Не могли бы вы уточнить, что именно вы имеете в виду? Не приведет ли следующий запрос к тому же набору результатов, что и ваш запрос:

SELECT 
     C.CustomerID, C.CompanyName,
     COUNT(O.OrderID) AS TotalOrders,
     COUNT(DISTINCT P.ProductID) AS DifferentProducts 
FROM Customers C LEFT OUTER JOIN Orders O ON
       C.CustomerID = O.CustomerID AND YEAR(O.OrderDate) = 1996
   LEFT OUTER JOIN Products P ON 
       O.somethingID = P.somethingID  
GROUP BY C.CustomerID, C.CompanyName

ОТВЕТИТЬ НА CADE ROUX:

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

SELECT x, y, z1, z2
FROM (
    SELECT *
           ,x + y AS z1
           ,x - y AS z2
    FROM (
        SELECT x * 2 AS y
        FROM A
    ) AS A
) AS A

Не приведет ли следующий запрос к тому же результату, что и ваш запрос:

SELECT x, x * 2 AS y, x + x*2 AS z1, x - x*2 AS z2
FROM A

Ответы [ 5 ]

5 голосов
/ 05 мая 2010

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

SELECT *
FROM A
LEFT JOIN (
    SELECT x, SUM(y)
    FROM B
    GROUP BY x
) AS B
    ON B.x = A.x

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

SELECT x, y, z1, z2
FROM (
    SELECT *
           ,x + y AS z1
           ,x - y AS z2
    FROM (
        SELECT x * 2 AS y
        FROM A
    ) AS A
) AS A

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

Обычно я использую составные CTE вместо вложенности для немного лучшей читаемости (те же два примера):

WITH B AS (
    SELECT x, SUM(y)
    FROM B
    GROUP BY x
)
SELECT *
FROM A
LEFT JOIN B
    ON B.x = A.x

WITH A1 AS (
    SELECT x * 2 AS y
    FROM A
)
,A2 AS (
    SELECT *
           ,x + y AS z1
           ,x - y AS z2
    FROM A1
)
SELECT x, y, z1, z2
FROM A2

Относительно вашего вопроса о:

SELECT x, x * 2 AS y, x + x*2 AS z1, x - x*2 AS z2 
FROM A 

Код x * 2 повторяется 3 раза. Если это бизнес-правило нужно изменить, оно должно будет измениться в 3 местах - рецепт для внедрения дефектов. Это усложняется всякий раз, когда у вас есть промежуточные вычисления, которые должны быть согласованы и определены только в одном месте.

Это не было бы такой большой проблемой, если скалярные пользовательские функции SQL Server могли бы быть встроенными (или если они выполнялись приемлемо), вы могли бы просто создать свои пользовательские функции для стека ваших результатов, и оптимизатор исключил бы избыточные вызовы. К сожалению, скалярная реализация UDF в SQL Server не может хорошо справляться с большими наборами строк.

5 голосов
/ 04 мая 2010

Обычно я использую производную таблицу (или CTE , которая иногда является лучшей альтернативой производным запросам в SQL 2005/2008), чтобы упростить чтение и построение запросов, или в случаях, когда SQL не ' Я не могу сделать определенную операцию.

Например, одна из вещей, которую вы не можете сделать без производной таблицы или CTE, - поместить агрегатную функцию в предложение WHERE. Это не сработает:

SELECT  name, city, joindate
FROM    members 
        INNER JOIN cities ON cities.cityid = derived.cityid
WHERE   ROW_NUMBER() OVER (PARTITION BY cityid ORDER BY joindate) = 1

Но это будет работать:

SELECT  name, city, joindate
FROM    
( 
    SELECT  name, 
            cityid,
            joindate,
            ROW_NUMBER() OVER (PARTITION BY cityid ORDER BY joindate) AS rownum 
    FROM    members 
) derived INNER JOIN cities ON cities.cityid = derived.cityid
WHERE   rn = 1

Продвинутые предостережения, особенно для крупномасштабной аналитики

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

Для очень крупномасштабных операций с данными иногда предпочтительнее создать временную таблицу, чем использовать производный запрос. Это может произойти, если статистика SQL предполагает, что ваш производный запрос вернет намного больше строк, чем запрос на самом деле, что происходит чаще, чем вы думаете. Запросы, в которых ваш основной запрос сам объединяется с нерекурсивным CTE , также проблематичны.

Также возможно, что производные таблицы будут генерировать неожиданные планы запросов. Например, даже если вы поместите строгое предложение WHERE в производную таблицу, чтобы сделать этот запрос очень избирательным, SQL Server может изменить порядок вашего плана запросов, чтобы ваше предложение WHERE оценивалось в плане запросов. См. отзыв Microsoft Connect для обсуждения этой проблемы и обходного пути.

Таким образом, для запросов с высокой производительностью (особенно запросов к хранилищу данных для таблиц объемом более 100 ГБ) я всегда хотел бы создать прототип решения для временных таблиц, чтобы увидеть, получаете ли вы лучшую производительность, чем у производной таблицы или CTE. Это кажется нелогичным, поскольку вы делаете больше операций ввода-вывода, чем идеальное решение с одним запросом, но с временными таблицами вы получаете полный контроль над используемым планом запроса и порядком оценки каждого подзапроса. Иногда это может увеличить производительность в 10 раз и более.

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

Я не утверждаю, что это распространенный случай - большую часть времени решение для временных таблиц будет, по крайней мере, немного хуже, и иногда подсказки к запросу являются единственным выходом. Но не думайте, что CTE или решение на основе производных запросов также будет вашим самым быстрым вариантом. Тест, тест, тест!

3 голосов
/ 04 мая 2010

Производные таблицы часто заменяют коррелированные подзапросы и, как правило, значительно быстрее.

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

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

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

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

Агрегирование данных из объединения - это еще одно место, где вам нужны производные таблицы.

1 голос
/ 04 мая 2010

В этом случае производная таблица допускает YEAR(O.OrderDate) = 1996 в предложении WHERE.

Во внешнем выражении where это бесполезно, потому что оно изменит JOIN на INNER.

Лично я предпочитаю конструкцию производной таблицы (или CTE), потому что она помещает фильтр в правильное место

Другой пример:

SELECT
     C.CustomerID, C.CompanyName,
     COUNT(D.OrderID) AS TotalOrders,
     COUNT(DISTINCT D.ProductID) AS DifferentProducts
FROM
     Customers C
     LEFT OUTER JOIN
     (
     SELECT
        OrderID, P.ProductID
     FROM
        Orders O
        JOIN
        Products P ON O.somethingID = P.somethingID
     WHERE YEAR(Orders.OrderDate) = 1996
     ) D
     ON C.CustomerID = D.CustomerID
GROUP BY
     C.CustomerID, C.CompanyName

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

Edit:

Мне нужно явно присоединиться к T1 и T2, прежде чем присоединиться к MyTable. Это случается Производное соединение T1 / T2 может быть другим запросом к двум левым соединениям без производной таблицы. Это случается довольно часто

SELECT
     --stuff--
FROM
     myTable M1
     LEFT OUTER JOIN
     (
     SELECT
        T1.ColA, T2.ColB
     FROM
        T1
        JOIN
        T2 ON T1.somethingID = T2.somethingID
     WHERE
        --filter--
     ) D
     ON M1.ColA = D.ColA AND M1.ColB = D.ColB
1 голос
/ 04 мая 2010

Используя вашу терминологию и пример, производные таблицы только более сложны и не имеют преимуществ. Однако некоторые вещи требуют производной таблицы. Это могут быть в наиболее сложных случаях CTE (как показано выше). Но простые объединения могут продемонстрировать необходимость производных таблиц, все, что вам нужно сделать, это создать запрос, который требует использования агрегата, здесь мы используем вариант запроса квоты, чтобы продемонстрировать это.

Выберите все самые дорогие транзакции клиента

SELECT transactions.*
FROM transactions
JOIN (
  select user_id, max(spent) AS spent
  from transactions
  group by user_id
) as derived_table
USING (
  derived_table.user_id = transaction.user_id
  AND derived_table.spent = transactions.spent
)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...