从多列中选择 MIN 值

Ter*_*rry 6 sql-server

我有一个非常基本的数据库,用于跟踪 6 个不同事件的分数。每个事件包含 4 个事件。

我的当前 SQL 查询非常基本,看起来与下面的非常相似;

Select Name, 
       SUM(ISNULL(p.e1_e1_points, 0)+ISNULL(p.e1_e2_points, 0)+ISNULL(p.e1_e3_points, 0)+ISNULL(p.e1_e4_points, 0)) AS Event1, 
       SUM(ISNULL(p.e2_e1_points, 0)+ISNULL(p.e2_e2_points, 0)+ISNULL(p.e2_e3_points, 0)+ISNULL(p.e2_e4_points, 0)) AS Event2,
       SUM(ISNULL(p.e3_e1_points, 0)+ISNULL(p.e3_e2_points, 0)+ISNULL(p.e3_e3_points, 0)+ISNULL(p.e3_e4_points, 0)) AS Event3,
       SUM(ISNULL(p.e4_e1_points, 0)+ISNULL(p.e4_e2_points, 0)+ISNULL(p.e4_e3_points, 0)+ISNULL(p.e4_e4_points, 0)) AS Event4,
       SUM(ISNULL(p.e5_e1_points, 0)+ISNULL(p.e5_e2_points, 0)+ISNULL(p.e5_e3_points, 0)+ISNULL(p.e5_e4_points, 0)) AS Event5,
       SUM(ISNULL(p.e6_e1_points, 0)+ISNULL(p.e6_e2_points, 0)+ISNULL(p.e6_e3_points, 0)+ISNULL(p.e6_e4_points, 0)) AS Event6
       from points
Run Code Online (Sandbox Code Playgroud)

我需要能够添加一个额外的列,它是列(事件 1、事件 2、事件 3 等)中的最小值。

And*_*y M 14

基本上,这是关于寻找行式最小值。

在这个 Stack Overflow 问题的最受好评的答案中有一个优雅的内联解决方案:

根据该解决方案,如果您有这样的表格T

C1  C2  C3
--  --  --
…   …   …
…   …   …
Run Code Online (Sandbox Code Playgroud)

您可以找到这样的逐行最小值:

SELECT
  C1,
  C2,
  C3,
  (
    SELECT MIN(C)
    FROM (VALUES (C1), (C2), (C3) AS v (C)
  ) AS MinC
FROM
  T
;
Run Code Online (Sandbox Code Playgroud)

基本上你是安排的价值观C1C2C3为一列,并正在申请一个正常的(逐列)聚合函数,以它来寻找最小。

现在在你的情况下C1C2等是表达式。通常这很好,您可以将VALUES行构造函数与表达式一起使用。但是在这种情况下,每个表达式都已经包含一个聚合函数 ( SUM()),这阻止了我们直接应用该方法(VALUES表达式不能使用聚合函数)。

不过,这个问题很容易解决。您可以将当前查询用作派生表或 CTE,并在外部级别应用该方法,其中表达式只是引用,如下所示:

SELECT
  Name,
  Event1,
  Event2,
  Event3,
  Event4,
  Event5,
  Event6,
  (
    SELECT MIN(Event)
    FROM (VALUES (Event1), (Event2), (Event3), (Event4), (Event5), (Event6)) AS v (Event)
  ) AS MinEvent
FROM
  (
    SELECT
      ... /* your current query */
  ) AS derived
;
Run Code Online (Sandbox Code Playgroud)

这是一个相同的解决方案,但使用 CTE:

WITH cte AS
  (
    SELECT
      ... /* your current query */
  )
SELECT
  Name,
  Event1,
  Event2,
  Event3,
  Event4,
  Event5,
  Event6,
  (
    SELECT MIN(Event)
    FROM (VALUES (Event1), (Event2), (Event3), (Event4), (Event5), (Event6)) AS v (Event)
  ) AS MinEvent
FROM
  cte
;
Run Code Online (Sandbox Code Playgroud)

  • `CROSS APPLY` 是另一种选择(使用派生表或 CTE) (2认同)

McN*_*ets 3

我已经设置了一个 dbfiddle 示例来模拟您的数据:

create table events (name varchar(20), event1 int, event2 int, event3 int, event4 int);
insert into events values 
('name1',10,11,12,13),('name2',20,21,22,23),('name3',30,31,32,33);
GO
Run Code Online (Sandbox Code Playgroud)
3 行受影响

恕我直言,你有一些选择:

嵌套IIF

-- this is your query
with a as
(
    select name, event1, event2, event3, event4
    from   events
)
select name, event1, event2, event3, event4,
       iif (event1 < event2, event1, 
              iif(event2 < event3, event2, 
                  iif(event3 < event4, event3, event4))) min_event
from a;
GO
Run Code Online (Sandbox Code Playgroud)
名称 | 事件1 | 事件2 | 事件3 | 事件4 | 最小事件
:---- | -----: | -----: | -----: | -----: | --------:
名称1 | 10 | 10 11 | 11 12 | 12 13 | 10
名称2 | 20 | 21 | 21 22 | 22 23 | 23 20
名称3 | 30| 31 | 32 | 32 33 | 33 30

取消透视

您可以使用 UNPIVOT 解决方案以这种方式获取最小值:

with a as
(
    select name, event1, event2, event3, event4
    from events
)
, b as
(
    select name, min(event) as min_event
    from   a
    unpivot (event for n in([event1],[event2],[event3],[event4])) upv
    group by name
)
select a.name, a.event1, a.event2, a.event3, a.event4, b.min_event
from   a
join   b
on     b.name = a.name


GO
Run Code Online (Sandbox Code Playgroud)
名称 | 事件1 | 事件2 | 事件3 | 事件4 | 最小事件
:---- | -----: | -----: | -----: | -----: | --------:
名称1 | 10 | 10 11 | 11 12 | 12 13 | 10
名称2 | 20 | 21 | 21 22 | 22 23 | 23 20
名称3 | 30| 31 | 32 | 32 33 | 33 30

dbfiddle在这里