如何优化嵌套SELECT

时间:2019-03-20 08:11:43

标签: sql sql-server tsql

我有以下需要优化的SQL(以下代码中“属性”的名称已更改为更通用):

它在Azure SQL中运行

SELECT ContactValueA, ContactValueB, SUM(X.Price) as Price, SUM(X.ValueX) AS ValueX, SUM(X.ValueY) AS ValueY, Count (*) As [Count]
FROM
    (
        SELECT OL.id, O.ContactValueA, O.ContactValueB,
        OL.Price,
        OL.ValueX,
        OL.ValueY
        FROM [OrderLines] AS OL
        JOIN [Orders] AS O
        ON OL.OrderId = O.Id
        WHERE O.Id in 
            (
                SELECT MIN(SO.Id) AS OID
                FROM [Orders] AS SO
                WHERE SO.[Type] = 'cake'
                AND SO.Created >= @begin  and SO.Created < @end
                AND NOT EXISTS
                (
                    SELECT 1
                    FROM [3Orders] AS SOA
                    WHERE SOA.ExtOrderId = SO.ExtOrderId
                    AND SOA.[Type] = 'cake'
                    AND SOA.Created < @begin
                )
                GROUP BY SO.ExtOrderId
            )   
    ) X
GROUP BY X.ContactValueA, X.ContactValueB

@begin和@end均为DateTimeOffset

如果我们看一下这段代码:

                SELECT MIN(SO.Id) AS OID
                FROM [Orders] AS SO
                WHERE SO.[Type] = 'cake'
                AND SO.Created >= @begin  and SO.Created < @end
                AND NOT EXISTS
                (
                    SELECT 1
                    FROM [3Orders] AS SOA
                    WHERE SOA.ExtOrderId = SO.ExtOrderId
                    AND SOA.[Type] = 'cake'
                    AND SOA.Created < @begin
                )
                GROUP BY SO.ExtOrderId

关于订单表-每个订单都有唯一的ID和不唯一的ExtOrderId-因为订单表中的条目可以是部分订单(部分订单共享相同的ExtOrderId)

在上面的代码中,我需要查看两个datetimeoffsets之间的所有订单(部分订单)-并采用ID最低的部分订单-但是部分订单ID(ExtOrderId)不得在开始日期之前有订单。

有人建议如何优化它吗?

3 个答案:

答案 0 :(得分:0)

使用row_number()分析函数获取最小ID

with cte as
(

SELECT OL.id, O.ContactValueA, O.ContactValueB,
        OL.Price,
        OL.ValueX,
        OL.ValueY,
        row_number()over(partition by O.type order by o.id) rn
        FROM [OrderLines] AS OL
        JOIN [Orders] AS O
        ON OL.OrderId = O.Id
) ContactValueA, ContactValueB, SUM(Price) as Price, SUM(ValueX) AS ValueX, SUM(ValueY) AS ValueY, Count (*) As [Count] from cte where rn=1
group by ContactValueA, ContactValueB

答案 1 :(得分:0)

我将使用左联接而不返回子查询,而不使用子查询。

您的查询:

        SELECT MIN(SO.Id) AS OID
        FROM [Orders] AS SO
        WHERE SO.[Type] = 'cake'
        AND SO.Created >= @begin  and SO.Created < @end
        AND NOT EXISTS
        (
            SELECT 1
            FROM [3Orders] AS SOA
            WHERE SOA.ExtOrderId = SO.ExtOrderId
            AND SOA.[Type] = 'cake'
            AND SOA.Created < @begin
        )
        GROUP BY SO.ExtOrderId

将是:

            SELECT MIN(SO.Id) AS OID
            FROM [Orders] AS SO
                 LEFT JOIN [3Orders] AS SOA ON     SOA.ExtOrderId = SO.ExtOrderId
                                               AND SOA.[Type] = 'cake'
                                               AND SOA.Created < @begin
            WHERE     SO.[Type] = 'cake'
                  AND SO.Created >= @begin  and SO.Created < @end
                  AND SOA.Id IS NULL -- Here we ensure that it has no previous orders
            GROUP BY SO.ExtOrderId

答案 2 :(得分:0)

尝试一下

WITH T AS (
    SELECT MIN(SO.Id) AS OID
    FROM [Orders] AS SO
    WHERE SO.[Type] = 'cake' AND SO.Created < @end
    GROUP BY SO.ExtOrderId 
    HAVING MIN(SO.Created) >= @begin
), T1 AS (
    SELECT OL.id, O.ContactValueA, O.ContactValueB,
        OL.Price,
        OL.ValueX,
        OL.ValueY
    FROM [OrderLines] AS OL
    INNER JOIN T ON T.OID = OL.OrderId
    INNER JOIN [Orders] AS O ON T.OID = O.Id
) SELECT ContactValueA, ContactValueB, SUM(T1.Price) as Price, SUM(T1.ValueX) AS ValueX, SUM(T1.ValueY) AS ValueY, Count (*) As [Count]
FROM T1 
GROUP BY T1.ContactValueA, T1.ContactValueB