Эффективность объединения подзапросов в SQL Server

У меня есть таблица клиентов и заказов в SQL Server 2008 R2. Оба имеют индексы для идентификатора клиента (называемого id). Мне нужно вернуть сведения обо всех клиентах в таблице клиентов и информацию из таблицы заказов, например сведения о первом заказе.

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

SELECT c.id
        ,c.country      
        ,First_orders.product
        ,First_orders.order_id
FROM customers c

LEFT JOIN   SELECT( id, 
                    product 
            FROM (SELECT    id
                            ,product
                            ,order_id
                            ,ROW_NUMBER() OVER (PARTITION BY id ORDER BY Order_Date asc) as order_No 
                        FROM orders) orders
            WHERE Order_no = 1) First_Orders
ON c.id = First_orders.id

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

Итак, я делаю это эффективно или это можно улучшить? Например, я не уверен, что мой индекс по идентификатору в таблице заказов полезен, и, возможно, я мог бы ускорить запрос, создав временную таблицу того, что находится в подзапросе, и создав уникальный индекс по идентификатору в таблице заказов. временная таблица, поэтому SQL Server знает, что id теперь является уникальным столбцом, а затем присоединяет таблицу моих клиентов к этой временной таблице? Обычно у меня есть один или два миллиона строк в таблицах клиентов и заказов.

Спасибо заранее!


person user2112153    schedule 26.02.2013    source источник
comment
С точки зрения удобочитаемости большинство разработчиков SQL стараются избегать подобных вложенных подзапросов, если могут. В тех случаях, когда требуется связывание производных таблиц, подобных этому, предпочтение отдается использованию CTE через префикс команды WITH . С точки зрения производительности это не должно иметь никакого значения, это просто вопрос соглашения и удобочитаемости.   -  person RBarryYoung    schedule 26.02.2013


Ответы (3)


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

SELECT c.id
        ,c.country      
        ,First_orders.product
        ,First_orders.order_id
FROM customers c
   LEFT JOIN  (SELECT id
                    ,product
                    ,order_id
                    ,ROW_NUMBER() OVER (PARTITION BY id ORDER BY Order_Date asc) as order_No 
               FROM orders) First_Orders
     ON c.id = First_orders.id AND First_Orders.order_No = 1

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

person sgeddes    schedule 26.02.2013

Для тех, кто только изучает SQL, ваш запрос выглядит довольно хорошо.

Индекс клиентов может использоваться или не использоваться для запроса - вам нужно будет посмотреть план выполнения. Индекс на orders(id, order_date) можно было бы весьма эффективно использовать для функции row_number.

Один комментарий относится к именованию полей. Поле orders.id не должно быть идентификатором клиента. Это должно быть что-то вроде «orders.Customer_Id». Сохранение единообразия системы именования таблиц поможет вам в будущем.

person Gordon Linoff    schedule 26.02.2013

Попробуйте это... это легко понять

;WITH cte
AS (
    SELECT id
        ,product
        ,order_id
        ,ROW_NUMBER() OVER (
            PARTITION BY id ORDER BY Order_Date ASC
            ) AS order_No
    FROM orders
    )
SELECT c.id
    ,c.country
    ,c1.Product
    ,c1.order_id
FROM customers c
INNER JOIN cte c1 ON c.id = c1.id
WHERE c1.order_No = 1
person Utsav Anand    schedule 18.01.2018