如何从子查询中的 2 个或多个字段中获取最大值

Posted

技术标签:

【中文标题】如何从子查询中的 2 个或多个字段中获取最大值【英文标题】:How to get biggest value from 2 or more fields in a subquery 【发布时间】:2021-06-10 13:10:18 【问题描述】:

我有一个客户表,我根据发票加入了一个包含销售的事实表。

我需要从我的报告中获得第一部分的最大销售额,该值基于去年客户的传入订单类型(1、2、3、C、D)。并且在第二部分中获得相同的内容,但对于本年度。我从当前查询中得到的结果是所有传入的订单类型以及每个订单类型的客户收入。我尝试使用外部应用作为子查询,以仅获取按收入降序排序的前 1 个值,但结果我得到相同的结果 - 对于所有订单类型,客户收入。请帮忙!我希望我的解释不是只有我能理解(经常发生..)

  use dwh01;
WITH OrderTypeUsedLY AS
(
SELECT 
     c.CustomerKey
    ,c.BranchId
    ,c.CustomerId
    ,c.CustomerName
    ,ISNULL(SUM(y.[Sale_Revenue]), 0) as [Sale_Revenue_LY]
    ,ISNULL(SUM(y.[Sale_GrossMarginTotal]), 0) as [Sale_GrossMarginTotal_LY] 
    ,y.IncomingOrderTypeId as IncomingOrderType_LY
FROM live.DimCustomer c
left join (SELECT s.CustomerKey,iot.IncomingOrderTypeid, s.[Sale_Revenue], s.[Sale_GrossMarginTotal] FROM [dwh01].[live].[FactSales] s 
inner join live.DimDate d
    on d.DateId = s.PostingDateKey
inner join live.DimIncomingOrderType iot on iot.IncomingOrderTypeKey = s.IncomingOrderTypeKey
where s.ReportCurrencyId = 'LC'      
and D.Year = YEAR(GETDATE())-1   --- Last Year            
) y on c.CustomerKey = y.CustomerKey             
where c.CustomerKey = '157053'
group by c.CustomerKey, c.CustomerId, c.CustomerName, c.BranchId, y.IncomingOrderTypeId
),
--*********************************************************************************************************************************--
OrderTypeCY as(
SELECT 
     c.CustomerKey
    ,c.BranchId
    ,c.SalesRepKey
    ,c.CustomerId
    ,c.CustomerName
    ,ISNULL(SUM(y.[Sale_Revenue]), 0) as [Sale_Revenue_CY]
    ,ISNULL(SUM(y.[Sale_GrossMarginTotal]), 0) as [Sale_GrossMarginTotal_CY] 
    ,y.IncomingOrderTypeId as IncomingOrderType_CY
FROM live.DimCustomer c
left join (SELECT s.CustomerKey,iot.IncomingOrderTypeid, s.[Sale_Revenue], s.[Sale_GrossMarginTotal] FROM [dwh01].[live].[FactSales] s 
inner join live.DimDate d
    on d.DateId = s.PostingDateKey
inner join live.DimIncomingOrderType iot on iot.IncomingOrderTypeKey = s.IncomingOrderTypeKey
where s.ReportCurrencyId = 'LC'      
and D.Year = YEAR(GETDATE())   --- Current Year           
) y on c.CustomerKey = y.CustomerKey             
where c.CustomerKey = '157053'
group by c.CustomerKey, c.CustomerId, c.CustomerName, c.BranchId, y.IncomingOrderTypeId, c.SalesRepKey
)
--*********************************************************************************************************************************--
SELECT
    otly.BranchId,
    rep.SalesRepId,
    rep.SalesRepName,
    otly.CustomerId,
    otly.CustomerName,
    otly.Sale_Revenue_LY,
    otly.Sale_GrossMarginTotal_LY,
    IncomingOrderType_LY,
    otcy.Sale_Revenue_CY,
    otcy.Sale_GrossMarginTotal_CY,
    IncomingOrderType_CY
from OrderTypeUsedLY otly
left join OrderTypeCY otcy 
    on otly.CustomerKey = otcy.CustomerKey 
join live.DimCustomer cus on cus.CustomerKey = otcy.CustomerKey
join live.DimSalesRep rep on rep.SalesRepKey = otcy.SalesRepKey
order by otcy.Sale_Revenue_CY desc, otly.Sale_Revenue_LY desc
,rep.SalesRepId

这是我尝试过的外部应用:

outer apply (
        SELECT top 1
    iot.IncomingOrderTypeId,
    Sale_Revenue
    FROM [dwh01].[live].DimIncomingOrderType iot
    where iot.IncomingOrderTypeKey = y.IncomingOrderTypeKey
    order by Sale_Revenue desc) x

在第一个选择中(使用 OrderTypeUsed_LY)我得到了这个:

我在第二次选择中得到相同的结果,但使用的是当年的值。

报告的目的是查看客户去年最常使用的传入订单类型(从中获得最大利润)的差异,并查看他今年是否继续使用它,或使用其他传入订单类型今年。

再次对不好的解释感到抱歉,我正在尽力(我非常了解自己)

这是预期的结果: Expected Result

我用红色标记了去年的部分,用绿色标记了当年的部分。

【问题讨论】:

我删除了 mysql 标签,因为这是一个 SQL Server 问题。它们是不相同的。另外,请以文本格式提供示例数据和您的预期结果。 我添加了预期结果作为 excel 表格的截图,我希望它能完成工作! :) outer apply 类似于outer join,它不会过滤掉任何记录。 问题:第二个 GROUP BY(当前年份)有一个 SalesRepKey 字段,在第一个 GROUP BY(去年)中缺少。对吗? 请不要在问题中编辑解决方案公告。接受(即单击旁边的“勾号”)现有答案之一,如果有的话。如果现有答案尚未涵盖您的解决方案,您还可以创建自己的答案,甚至接受它。 【参考方案1】:

如果您将子查询更改为:

     SELECT
    iot.IncomingOrderTypeKey,
    MAX(Sale_Revenue)
    FROM [dwh01].[live].DimIncomingOrderType iot
    GROUP BY iot.IncomingOrderTypeKey

然后您可以直接在 IncomingOrderTypeKey AND Sale_Revenue 上加入(而不是应用)。

【讨论】:

由于 sale_revenue 与传入订单类型(维度)不在同一个表中,但实际上我做了这样的子查询:SELECT iot.IncomingOrderTypeKey, MAX(fs.Sale_Revenue) as Sale_revenue FROM [dwh01].[live].DimIncomingOrderType iot join live.FactSales fs on fs.IncomingOrderTypeKey = iot.IncomingOrderTypeKey GROUP BY iot.IncomingOrderTypeKey 但最终结果是一样的,每个传入的一行订单类型和收入,不仅是收入最多的 1 行。【参考方案2】:

试试这个:

USE dwh01;

DECLARE @CustomerKey        varchar(6)  =   '157053'
    ,   @ReportCurrencyId   varchar(2)  =   'LC'
    ,   @CurrentYear        int         =   YEAR(GETDATE())
    ,   @TargetYear         int         =   YEAR(GETDATE())-1
;

WITH FactsTable AS
    (
        SELECT 
                        s.CustomerKey
                    ,   i.IncomingOrderTypeid
                    ,   [Sale_Revenue]          =   ISNULL(s.[Sale_Revenue]         , 0)
                    ,   [Sale_GrossMarginTotal] =   ISNULL(s.[Sale_GrossMarginTotal], 0) 
                    ,   d.[Year]

        FROM            [dwh01].[live].[FactSales]  s 
            inner join  live.DimDate                d   on  d.DateId                =   s.PostingDateKey
            inner join  live.DimIncomingOrderType   i   on  i.IncomingOrderTypeKey  =   s.IncomingOrderTypeKey
        where   
                        s.CustomerKey       =   @CustomerKey
                and     s.ReportCurrencyId  =   @ReportCurrencyId
    )
,   OrderTypeTable
    (
        SELECT 
                        c.CustomerKey
                    ,   c.BranchId
                    ,   c.CustomerId
                    ,   c.CustomerName
                    ,   c.SalesRepKey
                    
                    ,   IncomingOrderType_LY        =   SUM(CASE WHEN y.[Year] = @TargetYear    THEN y.IncomingOrderTypeId      ELSE 0 END)
                    ,   [Sale_Revenue_LY]           =   SUM(CASE WHEN y.[Year] = @TargetYear    THEN y.[Sale_Revenue]           ELSE 0 END)
                    ,   [Sale_GrossMarginTotal_LY]  =   SUM(CASE WHEN y.[Year] = @TargetYear    THEN y.[Sale_GrossMarginTotal]  ELSE 0 END)
                    
                    ,   IncomingOrderType_LY        =   SUM(CASE WHEN y.[Year] = @CurrentYear   THEN y.IncomingOrderTypeId      ELSE 0 END)
                    ,   [Sale_Revenue_CY]           =   SUM(CASE WHEN y.[Year] = @CurrentYear   THEN y.[Sale_Revenue]           ELSE 0 END)
                    ,   [Sale_GrossMarginTotal_CY]  =   SUM(CASE WHEN y.[Year] = @CurrentYear   THEN y.[Sale_GrossMarginTotal]  ELSE 0 END)

        FROM            live.DimCustomer    c
            left join   FactsTable          y   on  y.CustomerKey   =   c.CustomerKey

        group by    
                    c.CustomerKey
                ,   c.BranchId
                ,   c.CustomerId
                ,   c.CustomerName
                ,   y.IncomingOrderTypeId
                ,   c.SalesRepKey
    )

SELECT
        O.BranchId
    ,   R.SalesRepId
    ,   R.SalesRepName
    ,   O.CustomerId
    ,   O.CustomerName
    ,   O.Sale_Revenue_LY
    ,   O.Sale_GrossMarginTotal_LY
    ,   O.IncomingOrderType_LY
    ,   O.Sale_Revenue_CY
    ,   O..Sale_GrossMarginTotal_CY
    ,   O.IncomingOrderType_CY

from    OrderTypeTable      O
join    live.DimSalesRep    R   on  R.SalesRepKey = O.SalesRepKey
order by 
        O.Sale_Revenue_CY desc
    ,   O.Sale_Revenue_LY desc
    ,   R.SalesRepId

【讨论】:

嗨,谢谢您的回复,我在 ordertypTable 中的 IncomingOrderType_LY/CY 遇到问题,因为该列是 varchar,它给出了错误,我尝试将其转换为 int 但没有用,因为有列中的字符串(订单类型 D、C),你能建议做什么吗? , `IncomingOrderType_LY = SUM(CASE WHEN y.[Year] = @TargetYear THEN y.IncomingOrderTypeId ELSE 0 END)`。也许这行不通,因为 1 个客户可能使用了 1 个以上的传入订单类型,而我需要的是收入最多的传入订单类型。 用 ELSE '' 替换 ELSE 0。请记得投票。 Msg 8117, Level 16, State 1, Line 34 操作数数据类型 varchar 对 sum 运算符无效。我找到了另一个使用 row_number() 的解决方案,我把它贴出来。【参考方案3】:

解决方案是在第一个 CTE 的内部查询中使用 row_number() 函数:

    (
    select *, row_number() over (partition by x0.CustomerKey order by x0.Sale_Revenue desc) as rn
    from 
        (
        select fs.CustomerKey, iot.IncomingOrderTypeKey,
        sum(fs.Sale_Revenue) as Sale_Revenue
        FROM [dwh01].[live].DimIncomingOrderType iot
        join live.FactSales fs 
        on iot.IncomingOrderTypeKey = fs.IncomingOrderTypeKey
        join live.DimDate d
        on d.DateId = fs.PostingDateKey 
        where d.[year] = @CurrentYear
        GROUP BY fs.CustomerKey, iot.IncomingOrderTypeKey
        ) as x0
) as x1 on x1.CustomerKey = s.CustomerKey

row_number() 函数只从每个客户的结果中获取第一行,即销售收入最大的(order by sale_revenue desc)。

【讨论】:

以上是关于如何从子查询中的 2 个或多个字段中获取最大值的主要内容,如果未能解决你的问题,请参考以下文章

mysql中多个字段中的最大值

mysql取多个最大值

不能从子查询将行转换为列

从子查询 SQL 中选择最大数据,但它显示来自子查询的所有结果

thinkPHP 如何查询出数据库中id最大的一条数据

如何从子查询中获取结果作为查询中的参数