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

问题描述 投票:1回答:2

我有一个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(或MAX)按照这个closely related question

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

sql sql-server
2个回答
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
投票

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窗口函数来避免max:

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;
© www.soinside.com 2019 - 2024. All rights reserved.