从每行中选择列的最大值的行中的多个字段

时间:2017-04-03 17:07:16

标签: sql sql-server

我很确定自己已经把自己画成了一个角落而我无法解决问题。

Users表和OrderHistories表都有超过100万条记录:

SELECT
    u.Id ,
    u.Email AS EmailAddress ,
    c.Address_Address1 AS "Address 1" ,
    (
      SELECT
            COUNT(*)
        FROM
            dbo.OrderHistories oh
        WHERE
            oh.UserId = u.UserName
    ) AS NumberOfOrders ,
    Carts.SubtotalAmount AS CartTotal ,
    (
      SELECT
            MAX(oh.CreateDate)
        FROM
            dbo.OrderHistories AS oh
        WHERE
            oh.UserId = u.Id
    ) AS LastOrderDate ,
    (
      SELECT
            LastOrders.SubtotalAmount AS LastOrderSubtotal
        FROM
            (
              SELECT
                    UserId ,
                    CreateDate ,
                    SubtotalAmount ,
                    MAX(CreateDate) OVER ( PARTITION BY UserId ) MyLastOrderDate
                FROM
                    Users u
                    INNER JOIN dbo.OrderHistories oh
                    ON u.Id = oh.UserId
            ) AS LastOrders
        WHERE
            LastOrders.MyLastOrderDate = LastOrders.CreateDate
            AND LastOrders.UserId = u.Id
    ) AS LastOrderSubtotal
FROM
    Users u
    INNER JOIN Customers AS c
    ON u.Id = c.Id
    LEFT JOIN dbo.Carts
    ON c.Id = Carts.CustomerId

这个特殊的子查询是我当前的问题(非常低效),但我没有足够的经验来理解为什么,或者我应该如何做到(我无法从这里到达那里!) :

(
      SELECT
            LastOrders.SubtotalAmount AS LastOrderSubtotal
        FROM
            (
              SELECT
                    UserId ,
                    CreateDate ,
                    SubtotalAmount ,
                    MAX(CreateDate) OVER ( PARTITION BY UserId ) MyLastOrderDate
                FROM
                    Users u
                    INNER JOIN dbo.OrderHistories oh
                    ON u.Id = oh.UserId
            ) AS LastOrders
        WHERE
            LastOrders.MyLastOrderDate = LastOrders.CreateDate
            AND LastOrders.UserId = u.Id
    ) AS LastOrderSubtotal

任何人都在想我是多么可怕,然后直接进入建议的改进?

1 个答案:

答案 0 :(得分:1)

只需查看您的查询,您就可以使用cross apply()进行简化,如下所示:

select
    u.Id
  , EmailAddress = u.Email 
  , [Address 1]  = c.Address_Address1
  , CartTotal    = Carts.SubtotalAmount
  , NumberOfOrders = oh.NumberOfOrders
  , LastOrderDate  = oh.CreateDate
  , LastOrderSubtotal = oh.SubtotalAmount
from Users u
  inner join Customers AS c
    on u.Id = c.Id
  left join dbo.Carts
    on c.Id = Carts.CustomerId
  cross apply (
    select top 1
        i.CreateDate
      , i.SubtotalAmount
      , NumberOfOrders = count(*) over (partition by i.UserId) 
   from dbo.OrderHistories i
   where i.UserId = u.Id
   order by i.CreateDate desc
   ) as oh

如果您想要的行可能没有OrderHistory,请切换为outer apply()

参考: