【问题标题】:FIRST aggregate function which I can use with HAVING clause我可以与 HAVING 子句一起使用的第一个聚合函数
【发布时间】:2012-04-12 08:39:38
【问题描述】:

我有一个奇怪的要求,我需要在 SQL Server 2008 R2 的存储过程中使用它。

我需要一个返回序列的第一个元素的 FIRST 聚合函数,我会将它与 HAVING 子句一起使用。

让我举个例子:

DECLARE @fooTable AS TABLE(
    ID INT,
    CategoryName NVARCHAR(100),
    Name NVARCHAR(100),
    MinAllow INT,
    Price DECIMAL(18,2)
);

INSERT INTO @fooTable  VALUES(1, 'Cat1', 'Product1', 2, 112.2);
INSERT INTO @fooTable  VALUES(2, 'Cat2', 'Product2', 4, 12.34);
INSERT INTO @fooTable  VALUES(3, 'Cat1', 'Product3', 5, 233.32);
INSERT INTO @fooTable  VALUES(4, 'Cat3', 'Product4', 4, 12.43);
INSERT INTO @fooTable  VALUES(5, 'Cat3', 'Product5', 1, 13.00);

DECLARE @minAllowParam AS INT = 3;

SELECT ft.CategoryName, SUM(ft.Price) FROM @fooTable ft
GROUP BY ft.CategoryName;

如您所见,我们有一个表格和一些虚拟值。在SELECT 查询中,我们将类别分组在一起,并将产品的价格相加。

此查询返回以下结果:

CategoryName     TotalPrice
---------------- ----------------
Cat1              345.52
Cat2              12.34
Cat3              25.43

我需要的是这样的:

SELECT ft.CategoryName, SUM(ft.Price) FROM @fooTable ft
GROUP BY ft.CategoryName
HAVING GetFIRST(MinAllow) >= @minAllowParam;

如果我们的查询是这样的,我们应该能够选择以下结果:

INSERT INTO @fooTable  VALUES(2, 'Cat2', 'Product2', 4, 12.34);
INSERT INTO @fooTable  VALUES(4, 'Cat3', 'Product4', 4, 12.43);
INSERT INTO @fooTable  VALUES(5, 'Cat3', 'Product5', 1, 13.00);

由于INSERT INTO @fooTable VALUES(1, 'Cat1', 'Product1', 2, 112.2); 记录是序列的第一个元素,MinAllow 列的值为 2,因此 Cat1 应该不在此范围内。另一方面,INSERT INTO @fooTable VALUES(5, 'Cat3', 'Product5', 1, 13.00); 记录的 MinAllow 列的值为 1,但它是序列的第二个元素。所以,Cat3是安全的,可以选择。

注意:MINMAX 不是我要找的!

我知道这个例子在逻辑上没有意义,但另一方面我有一种情况,它完全可以,而且很难在这里解释。

有什么想法吗?

编辑:

我假设我可以通过创建 CLR 来实现我想要的 用户定义的聚合函数,但我不想这样做 还有其他选择吗

【问题讨论】:

    标签: sql sql-server tsql sql-server-2008-r2 aggregate-functions


    【解决方案1】:

    怎么样

    SELECT f1.CategoryName, SUM(f1.Price) 
    FROM @fooTable AS f1
    INNER JOIN (
        SELECT MinAllow, CategoryName
        FROM (
             SELECT MinAllow, CategoryName, ROW_NUMBER() OVER (PARTITION BY CategoryName ORDER BY ID) AS m
             FROM @fooTable
        ) AS f
        WHERE m = 1
    ) AS f2 ON f1.CategoryName = f2.CategoryName
    WHERE f2.MinAllow >= @minAllowParam
    GROUP BY f1.CategoryName
    

    我不知道一个非常优雅的查询。如果我再努力一点,也许我可以稍微调整一下!

    编辑:好的,最里面的子查询应该是不必要的。这也应该有效:

    SELECT f1.CategoryName, SUM(f1.Price) 
    FROM @fooTable AS f1
    INNER JOIN (
        SELECT MinAllow, CategoryName, ROW_NUMBER() OVER (PARTITION BY CategoryName ORDER BY ID) AS m
        FROM @fooTable
    ) AS f2 ON f1.CategoryName = f2.CategoryName
    WHERE f2.m = 1 AND f2.MinAllow >= @minAllowParam
    GROUP BY f1.CategoryName
    

    【讨论】:

    • 工作中的一个,谢谢!让我们看看是否还有其他优雅的方式。并感谢您将它们放在一起。我一路提供样品,你还报了这个人情。也为此 +1。
    【解决方案2】:

    更新:一个可读的查询:

    SELECT ft.CategoryName, SUM(ft.Price) 
    FROM fooTable ft
        cross apply
        (
           select top 1 MinAllow
             from fooTable a
            where a.CategoryName = ft.CategoryName
            order by ID
        ) a
    where a.MinAllow >= @minAllowParam
    GROUP BY ft.CategoryName;
    

    您可以过滤具有第一个(按 id?)MinAllow >= @minAllowParam 的类别:

    ...
    inner join 
    (
       select 
       -- Add columns you might need
         CategoryName,
         Price
       from
         fooTable
       inner join
       (
         -- First ID in category
         select
           min(id) id
         from
           fooTable
         group by
           CategoryName
       ) firstID
       -- Back to all columns
         ON fooTable.ID = firstID.ID
       -- but only if category sequence starts properly
        AND fooTable.MinAllow >= @minAllowParam
    ) a
    -- Allow MinAllow categories only
      ON fooTable.CategoryName = a.CategoryName
    

    【讨论】:

    • 那个代码很恶心,看在上帝的份上,我根本看不懂!
    • @tugberk:解决方案不会很简单,所以习惯这种类型的 SQL...
    • 我对复杂性没有意见,先生,但您的代码不可读。您已经多次使用名为 fooTable 的东西,但很难弄清楚您指的是哪一个。
    • @tugberk:欣赏别人看到你的问题的努力和粗鲁之间有细微的区别
    • @LukasEder 粗鲁?我做了什么?如果 OP 一直通过示例提供一个很好的问题,我希望回答者也能做到这一点。这就是我的意思。
    【解决方案3】:

    查看带有分区的 ROW_NUMBER():

    http://msdn.microsoft.com/en-us/library/ms186734.aspx

    【讨论】:

      【解决方案4】:

      我知道有两种方法可以实现 FIRST 聚合函数:

      ROW_NUMBER()
      
      (WHERE ROW_NUMBER_COLUMN = 1)
      

      http://msdn.microsoft.com/en-us/library/ms186734.aspx

      SELECT ...., (SELECT TOP 1 FROM ... WHERE (outer table join) ORDER BY SOMETHING) AS [FIRST]
      FROM ...
      

      【讨论】:

        猜你喜欢
        • 2013-01-23
        • 2018-05-19
        • 2012-09-18
        • 1970-01-01
        • 2020-08-13
        • 2021-11-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多