如果对于组中的每一行都相同,请选择行值

时间:2017-12-20 10:44:47

标签: sql sql-server

我有一个带有join和group-by的查询,它返回如下内容:

CONSTRUCTOR | MODEL | COLOR
------------+-------+------
Mercedes    | SLK   | Gray
Ferrari     | 430   | Red
Mercedes    | GLK   | Black
Porsche     | 911   | Blue
Ferrari     | 458   | Red

我想按CONSTRUCTOR添加一个组,如果每个组中的颜色相同,则获取颜色的值;如果有多个值,则获取“倍数”(对MODEL不感兴趣)

CONSTRUCTOR | COLOR
------------+---------
Mercedes    | multiple
Ferrari     | Red
Porsche     | Blue

此查询有效,但我想知道是否有更有效的方法来实现相同的结果而不会使查询本身复杂化

SELECT
  CONSTRUCTOR,
  CASE COUNT(DISTINCT COLOR)
    WHEN 1 THEN MIN(COLOR)
    ELSE 'multiple'
  END AS COLOR
FROM MYTABLE
GROUP BY CONSTRUCTOR

我认为我无法避免MIN(或MAXclosely related question

我可以避免使用CASE或以更有效的方式使用它吗?

2 个答案:

答案 0 :(得分:2)

编写查询的最有效方法可能是:

SELECT CONSTRUCTOR,
       (CASE WHEN MIN(COLOR) = MAX(COLOR) THEN MIN(COLOR)
             ELSE 'multiple'
        END) AS COLOR
FROM MYTABLE
GROUP BY CONSTRUCTOR;

通常,COUNT(DISTINCT)比其他聚合函数更昂贵。仅使用MIN()MAX()应该会更有效率。使用CASE几乎没有任何开销。

如果值为NULL,您可以考虑附加条件:

SELECT CONSTRUCTOR,
       (CASE WHEN MIN(COLOR) = MAX(COLOR) AND COUNT(COLOR) = COUNT(*)
             THEN MIN(COLOR)
             ELSE 'multiple'
        END) AS COLOR
FROM MYTABLE
GROUP BY CONSTRUCTOR;

答案 1 :(得分:1)

救援时

CTE

With g as (
  select
    CONSTRUCTOR, 
    count(distinct color) as n_colors,
    max(color) as color
  from MYTABLE
  group by CONSTRUCTOR
)
select CONSTRUCTOR, 
       case when n_colors = 1 then color
       else 'multiple' end
from g;      

解释:您可以使用CTE句子烹饪数据,然后以简单的方式使用预先烹饪的数据进行最终查询。 CTE是你的朋友。

注意:优雅的方式看起来是窗口函数。但count( distinct条款不允许over partition

编辑编辑OP评论:

您可以使用first_value窗口函数来避免最大化:

With g1 as (
  select
    CONSTRUCTOR, 
    count(distinct color) as n_colors
  from t
  group by cons
),
g2 as (
  select distinct
    CONSTRUCTOR,
    first_Value(color) over (partition by cons order by color) as color
  from t
)  
select g1.cons, 
       case when g1.n_colors = 1 then g2.color
       else 'multiple' end
from g1 inner join g2 on g1.CONSTRUCTOR = g2.CONSTRUCTOR

最终答案

没有group by,使用窗口函数,索引友好(通过CONSTRUCTOR和COLOR):

with cte as (
 select 
    CONSTRUCTOR,
    first_Value(color) over (partition by CONSTRUCTOR order by color 
                             rows BETWEEN UNBOUNDED PRECEDING AND 
                                          UNBOUNDED FOLLOWING   ) as f,
    last_Value(color) over (partition by CONSTRUCTOR order by color  
                            rows BETWEEN UNBOUNDED PRECEDING AND 
                                         UNBOUNDED FOLLOWING  ) as l,
    ROW_NUMBER ( ) over (partition by CONSTRUCTOR order by color  ) as n       
  from t
) 
select CONSTRUCTOR, 
       case when f=l then f else 'multiple' end as color
from cte       
where n = 1;