使用HAVING子句标识具有已定义记录组合的组

时间:2015-09-06 13:55:53

标签: sql-server having

我有一张包含姓名,类型和价值的表格。

DECLARE @t_Table TABLE
(
    Name VARCHAR(10),
    [Type] VARCHAR(10),
    Value INT
)

INSERT INTO @t_Table
VALUES('Jill', 'Yellow', 100)
INSERT INTO @t_Table
VALUES('Jill', 'Blue', 200)
INSERT INTO @t_Table
VALUES('Jill', 'Green', 300)
INSERT INTO @t_Table
VALUES('Jill', 'Green', 400)
INSERT INTO @t_Table
VALUES('Jill', 'Green', 500)

INSERT INTO @t_Table
VALUES('Bob', 'Yellow', 100)
INSERT INTO @t_Table
VALUES('Bob', 'Blue', 200)
INSERT INTO @t_Table
VALUES('Bob', 'Green', 300)
INSERT INTO @t_Table
VALUES('Bob', 'Orange', 400)
INSERT INTO @t_Table
VALUES('Bob', 'Orange', 400)
INSERT INTO @t_Table
VALUES('Bob', 'Purple', 500)

INSERT INTO @t_Table
VALUES('Steve', 'Yellow', 100)
INSERT INTO @t_Table
VALUES('Steve', 'Blue', 200)
INSERT INTO @t_Table
VALUES('Steve', 'Green', 300)
INSERT INTO @t_Table
VALUES('Steve', 'Orange', 400)
INSERT INTO @t_Table
VALUES('Steve', 'Orange', 400)

我想获取名称组的总值,其中组中的基础记录满足特定类型出现的约束。 我想在HAVING子句中使用单个聚合来完成此任务。

如果我想要一个只有一个 x 类型记录的组,只需要一个 y 类型的记录,零个或多个 z类型的记录并没有其他记录,我已经达到了以下解决方案,例如,当我想要一个黄色,一个蓝色和零个或更多绿色时:

SELECT Name,
    TotalValue = SUM(Value)
FROM @t_Table
GROUP BY Name
HAVING SUM(CASE WHEN [Type] = 'Yellow' THEN 1
        WHEN [Type] = 'Blue' THEN 2
        WHEN [Type] = 'Green' THEN 0
        ELSE 4 END) = 3

正确返回此结果:

Name       TotalValue
---------- -----------
Jill       1500

我将如何构建以下内容?

SELECT Name,
    TotalValue = SUM(Value)
FROM @t_Table
GROUP BY Name
/*HAVING exactly one record with [Type] = 'Yellow'
    and exactly one record with [Type] = 'Blue'
    and exactly one record with [Type] = 'Green'
    and zero or more records with [Type] = 'Orange'
    and no records of any other type
*/

如果给出上述数据的预期结果将是

Name       TotalValue
---------- -----------
Steve      1400

我知道以下解决方案(下面),但我需要一个在HAVING子句中有一个聚合的解决方案。我也对另一个解决我的问题的查询结构持开放态度,只要它与我提出的结构一样简单或简单,并且表现相似或更好。

SELECT 
    Name,
    TotalValue = SUM(Value)
FROM 
    @t_Table
GROUP BY 
    Name
HAVING 
    SUM(CASE WHEN [Type] = 'Yellow' THEN 1 ELSE NULL END) = 1
    AND SUM(CASE WHEN [Type] = 'Blue' THEN 1 ELSE NULL END) = 1
    AND SUM(CASE WHEN [Type] = 'Green' THEN 1 ELSE NULL END) = 1
    AND SUM(CASE WHEN [Type] IN ('Yellow','Blue','Green','Orange') THEN 0 ELSE 1 END) = 0

3 个答案:

答案 0 :(得分:1)

如何使用您的概念,但每种类型都有小数:

<强> SqlFiddleDemo

data.table

这意味着正好是1黄色,1蓝色,1绿色。

使用SELECT Name, TotalValue = SUM(Value) FROM @t_Table GROUP BY Name HAVING SUM( CASE [Type] WHEN 'Yellow' THEN 1 WHEN 'Blue' THEN 10 WHEN 'Green' THEN 100 WHEN 'Orange' THEN 0 ELSE 0 END) = 111 BETWEEN可以实现更复杂的条件。一个注意事项只要你在一个组中搜索最多9个就行了。

如果您害怕因基于10的系统而导致溢出,请考虑使用基于1000的系统,例如:

< <= > =

答案 1 :(得分:1)

想象一下,在HAVING子句中,您可以比较两个多项式 然后想象你定义(从你拥有的数据)这个多项式:

count_YELLOW * x ^ 3 + count_BLUE * x ^ 2 + count_GREEN * x ^ 1 + count_ORANGE

/ *     用[Type] ='Yellow'记录一条记录     和[Type] ='Blue'的确切记录     和[Type] ='Green'的确切记录     和[Type] ='Orange'的零个或多个记录     并且没有任何其他类型的记录 * /

现在......在HAVING子句中表达你想要的东西,你会说:

HAVING 
count_YELLOW * x^3 + 
count_BLUE * x^2 + 
count_GREEN * x^1 + 
count_ORANGE >=   
1 * x^3 + 
1 * x^2 + 
1 * x^1 + 
0

AND

count_YELLOW * x^3 + 
count_BLUE * x^2 + 
count_GREEN * x^1 + 
count_ORANGE <   
1 * x^4 

现在...... 只需选择x = sum (all counts) + 1, 或x = max (all counts) + 1, 你可以把它变成数字。

我认为这会奏效。我明天可以在T-SQL中试一试 你会遇到一些大数字。这是不可避免的,
因为你想要将4个数字的矢量无差别地编码成一个数字。

答案 2 :(得分:0)

我认为这个查询效率更高

with cte as 
(
select name, [type] tp, nb = count(*)
from @t_table
group by name, [type]
)

Select t1.name, sum(t1.Value)
from @ t_table t1 inner join cte t2 on t1.name = t2.name
where nb = (case t2.tp when 'Yellow' then 1
                       when 'Blue' then 1
                       ...
            end)
      AND Exist (select * from cte where name = t2.name and t2.tp = 'Yellow')
      AND Exist (select * from cte where name = t2.name and t2.tp = 'Blue')
      ...
group by t1.name