【问题标题】:Correctly grouping this query to get minimum value ONLY?正确分组此查询以获得最小值?
【发布时间】:2020-05-20 08:08:46
【问题描述】:

桌车:

id  serial  size    batch
--------------------------
1   x99     Large    NULL
2   x99     Small    Q
3   x99     Med     NULL
4   x99     Large    K

使用表代码分配连接两次:

Id  serial  size    batch   code    precedence
-----------------------------------------------
1   x99     Large   NULL    5000    1
2   x99     NULL      K     3000    2
3   x99     NULL      Q     2000    3
4   x99     NULL    NULL    500     4

使用以下内容:

select 
    c.*, 
    coalesce(ca1.code, ca2.code) as code, 
    min(coalesce(ca1.precedence, ca2.precedence)) as precedence
from
    cars c 
left join
    codeassignment ca1 on ca1.serial = c.serial 
                       and (ca1.size = c.size or ca1.batch = c.batch) 
left join
    codeassignment ca2 on ca2.serial = c.serial 
                       and ca1.size is null and ca2.size is null 
                       and ca1.batch is null and ca2.batch is null
group by 
    c.id, c.serial, c.size, c.batch, ca1.code, ca2.code

但它会导致 'Large' 和批次 'K' 的匹配出现两次:

id  serial  size    batch   code    precedence
----------------------------------------------
1   x99     Large   A       5000    1
2   x99     Small   Q       2000    3
3   x99     Med     P       500     4
4   x99     Large   K       3000    2
4   x99     Large   K       5000    1

我想选择所有上述行,但对于最后两行,只有优先级最低的行(这就是我尝试min(precedence) 的原因),但我认为 group by 正确执行此操作是错误的。所以基本上serialsizebatch相同的地方应该只有一行。

这是一个小提琴:

https://dbfiddle.uk/?rdbms=sqlserver_2019&fiddle=47700d67170a7beb39bfc523ffdfbcfc

【问题讨论】:

    标签: sql sql-server


    【解决方案1】:

    为迟来的答案道歉,但我仍然有这个开放。作为替代方案,无论如何都要完成它。

    如果我正确理解目标,您想匹配序列号、填充的大小和填充的批次,优先级领先,无论填充的字段是否“更强”匹配?

    那么一个连接就足够了:

    select c.Serial, c.Size,c.Batch, min(isnull(ca.Precedence,0)) Precedence
    from cars c
    left join codeassignment ca on ca.serial = c.serial and (ca.size is null or ca.size=c.size) and (ca.batch is null or ca.batch=c.batch)
    group by c.Serial,c.Size,c.Batch
    

    这只是为了显示单连接,不需要 ca1 和 ca2(除非我缺少某些要求)

    以此为基础,另一种匹配最小值或其他聚合的方法是使用窗口函数而不是分组依据。我对那些有一点偏好(结合 cte,因为你不能直接使用窗口函数作为标准)

    with q as(
    select c.*, ca.Code, ca.Precedence, row_number() over (partition by c.Serial, c.Size, c.Batch order by Precedence) rnr
    from cars c
    left join codeassignment ca on ca.serial = c.serial and (ca.size is null or ca.size=c.size) and (ca.batch is null or ca.batch=c.batch)
    )
    select Serial, Size, Batch, Precedence, Code
    from q
    where rnr = 1
    

    【讨论】:

    • 感谢您的回答。双重加入是为了解决我昨天遇到的问题。当时我没有优先列,只是后来出现了一些其他条件才被引入。我也许可以使用此答案使我的查询更加简洁,我现在将进行调查。
    • 进行了一些修改,因为我要合并另一个连接表中的额外列,但我得到了它的工作,谢谢!
    【解决方案2】:

    问题是您在查询中按“太多东西”进行分组,因此您最终会得到不想要的离散行。解决此问题的一种方法是将查询分为两个阶段,一个是获取每个 id、序列、大小和批次的最小优先级,然后是获取丢失的数据。像这样的:

    WITH x AS (
        SELECT 
            c.id, 
            c.serial, 
            c.size, 
            c.batch, 
            MIN(ISNULL(ca1.precedence, ca2.precedence)) AS precedence
        FROM 
            cars c 
            LEFT JOIN codeassignment ca1
                ON ca1.serial = c.serial AND (ca1.size = c.size OR ca1.batch = c.batch) 
            LEFT JOIN codeassignment ca2 ON ca2.serial = c.serial AND ca1.size is null and ca2.size is null and ca1.batch is null and ca2.batch is null
         GROUP BY 
            c.id, 
            c.serial, 
            c.size, 
            c.batch)
    SELECT
        x.*,
        ISNULL(ca1.code, ca2.code) AS code
    FROM
        x
        INNER JOIN cars c ON c.id = x.id AND c.serial = x.serial AND c.size = x.size AND c.batch = x.batch
        LEFT JOIN codeassignment ca1
         ON ca1.serial = c.serial and (ca1.size = c.size or ca1.batch = c.batch) AND ca1.precedence = x.precedence
        LEFT JOIN codeassignment ca2
         ON ca2.serial = c.serial and
         ca1.size is null and ca2.size is null and ca1.batch is null and ca2.batch is null AND ca2.precedence = x.precedence;
    

    由于某种原因,我无法使用它,但我可以在 SQL Server 2019 上本地运行它以获得:

    id  serial  size    batch   precedence  code
    1   x99     Large   A       1           5000
    2   x99     Small   Q       3           2000
    3   x99     Med     P       4           500
    4   x99     Large   K       1           5000
    

    【讨论】:

    • 对我来说似乎在小提琴上工作得很好!太棒了,谢谢你。我真的需要一些 CTE 练习,它们似乎可以解决我一半以上的 SQL 问题。
    猜你喜欢
    • 1970-01-01
    • 2014-02-04
    • 2020-11-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多