我有一个奇怪的要求,我需要在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 是安全的,可以选择。
注意:
MIN
或MAX
不是我要找的!
我知道这个例子在逻辑上没有意义,但我有一种情况,它完全做了,另一方面难以解释。
有什么想法吗?
修改
我假设我可以通过创建CLR实现我想要的目标 用户定义的聚合函数,但如果存在,我不想这样做 是其他任何选择
答案 0 :(得分:3)
怎么样
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
答案 2 :(得分:1)
使用分区查看ROW_NUMBER():
答案 3 :(得分:0)
我知道有两种方法可以实现FIRST聚合功能:
ROW_NUMBER()
(WHERE ROW_NUMBER_COLUMN = 1)
和
SELECT ...., (SELECT TOP 1 FROM ... WHERE (outer table join) ORDER BY SOMETHING) AS [FIRST]
FROM ...