代码之家  ›  专栏  ›  技术社区  ›  Rony Patel

如何从SQLServer中的几行中获取最小值的列的名称?

  •  1
  • Rony Patel  · 技术社区  · 6 年前

    给一张桌子 TestTable 在SQL Server中,如下所示:

    =======================================
    No |  Name | Q1  |  Q2  |  Q3  |  Q4  |
    =======================================
    1  |  ABC  | 10  |  15  |  10  |  50  |
    2  |  ABC  | 30  |  25  |  12  |  40  |
    3  |  ABC  | 05  |  35  |  13  |  30  |
    4  |  ABC  | 10  |  10  |  10  |  20  |
    5  |  ABC  | 90  |  50  |  15  |  10  |
    ======================================
    

    .

    我的预期结果:

    ================================================
    No |  Name | Q1  |  Q2  |  Q3  |  Q4  | Min    |
    ================================================
    1  |  ABC  | 10  |  15  |  10  |  50  |  Q1,Q3 |
    2  |  ABC  | 30  |  25  |  12  |  40  |   Q3   |
    3  |  ABC  | 05  |  35  |  13  |  30  |   Q1   |
    4  |  ABC  | 10  |  10  |  10  |  20  |Q1,Q2,Q3|
    5  |  ABC  | 90  |  50  |  15  |  10  |   Q4   |
    ================================================
    
    6 回复  |  直到 6 年前
        1
  •  4
  •   Paweł Dyl    6 年前

    这应该起作用:

    --SELECT * INTO #t
    --FROM (VALUES 
    --(1, 'ABC', 10, 15, 10, 50),
    --(2, 'ABC', 30, 25, 12, 40),
    --(3, 'ABC', 5, 35, 13, 30),
    --(4, 'ABC', 10, 10, 10, 20),
    --(5, 'ABC', 90, 50, 15, 10)
    -- )T(No,Name,Q1,Q2,Q3,Q4)
    
    SELECT *, LEFT([Min],LEN([Min])-1) FROM (
        SELECT *,
           CASE WHEN Q1=M THEN 'Q1,' ELSE '' END+
           CASE WHEN Q2=M THEN 'Q2,' ELSE '' END+
           CASE WHEN Q3=M THEN 'Q3,' ELSE '' END+
           CASE WHEN Q4=M THEN 'Q4,' ELSE '' END [Min]
        FROM #t
        CROSS APPLY (SELECT MIN(X) M FROM (VALUES (Q1),(Q2),(Q3),(Q4)) T(X)) T
    ) T
    

    编辑 -这甚至可以缩短与 STUFF :

    SELECT No, Name, Q1, Q2, Q3, Q4,
        STUFF(CASE WHEN Q1=M THEN ',Q1' ELSE '' END+
              CASE WHEN Q2=M THEN ',Q2' ELSE '' END+
              CASE WHEN Q3=M THEN ',Q3' ELSE '' END+
              CASE WHEN Q4=M THEN ',Q4' ELSE '' END, 1,1, '') [Min]
    FROM #t
    CROSS APPLY (SELECT MIN(X) M FROM (VALUES (Q1),(Q2),(Q3),(Q4)) T(X)) T
    
        2
  •  1
  •   Thom A    6 年前

    这看起来有点复杂,但是,一种方法可能是:

    CREATE TABLE SampleData ([No] int,
                             [Name] varchar(3),
                             Q1 int,
                             Q2 int,
                             Q3 int,
                             Q4 int);
    GO
    INSERT INTO dbo.SampleData ([No],
                                [Name],
                                Q1,
                                Q2,
                                Q3,
                                Q4)
    VALUES (1,'ABC',10,15,10,50),
           (2,'ABC',30,25,12,40),
           (3,'ABC',05,35,13,30),
           (4,'ABC',10,10,10,20),
           (5,'ABC',90,50,15,10);
    GO
    
    SELECT *
    FROM SampleData;
    GO
    --Normalise the Data:
    WITH T AS(
        SELECT I
        FROM (VALUES(1),(2),(3),(4)) V(I)),
    Norm AS(
        SELECT SD.[No],
               SD.[name],
               T.I AS Q,
               CHOOSE(T.I,SD.Q1,SD.Q2,SD.Q3,SD.Q4) AS Qv,
               MIN(CHOOSE(T.I,SD.Q1,SD.Q2,SD.Q3,SD.Q4)) OVER (PARTITION BY SD.[No]) AS QvMin
        FROM dbo.SampleData SD
             CROSS JOIN T)
    SELECT SD.*,
           STUFF((SELECT ',Q' + CONVERT(varchar(1),n.Q)
                  FROM Norm n
                  WHERE n.Qv = n.QvMin
                    AND n.[No] = SD.[No]
                  ORDER BY n.Q
                  FOR XML PATH('')),1,1,'') AS [Min]
    FROM dbo.SampleData SD;
    GO
    DROP TABLE SampleData;
    

    MIN 用于的函数。然后是一个子查询 FOR XML PATH 可以连接值和最小值具有相同值的值。

    CASE 表达式只需要1。

        3
  •  1
  •   JNevill    6 年前

    这里有点不同 SQL Server 2017+ 我们可以利用的地方 STRING_AGG

    CREATE TABLE testtable(
       No   INTEGER  NOT NULL PRIMARY KEY 
      ,Name VARCHAR(7) NOT NULL
      ,Q1   INTEGER  NOT NULL
      ,Q2   INTEGER  NOT NULL
      ,Q3   INTEGER  NOT NULL
      ,Q4   INTEGER  NOT NULL
    );
    
    INSERT INTO testtable(No,Name,Q1,Q2,Q3,Q4) VALUES
     (1,'ABC',10,15,10,50)
    ,(2,'ABC',30,25,12,40)
    ,(3,'ABC',5,35,13,30)
    ,(4,'ABC',10,10,10,20)
    ,(5,'ABC',90,50,15,10);
    


    SELECT No, Name, STRING_AGG(Q_Number, ',') as [Min]
    FROM
      (
        SELECT No, Name, RANK() OVER (PARTITION BY No ORDER BY Q_Value) as Q_Rank, CA.*
        FROM testtable
        CROSS APPLY ( values (Q1, 'Q1'),(Q2, 'Q2'),(Q3, 'Q3'),(Q4, 'Q4')) CA (Q_Value, Q_Number)
       ) minrank
    WHERE Q_Rank = 1
    GROUP BY No, Name;
    


    +----+------+----------+
    | No | Name |   Min    |
    +----+------+----------+
    |  1 | ABC  | Q1,Q3    |
    |  2 | ABC  | Q3       |
    |  3 | ABC  | Q1       |
    |  4 | ABC  | Q1,Q2,Q3 |
    |  5 | ABC  | Q4       |
    +----+------+----------+
    

    我认为,从所有其他答案来看,这里的主要想法是,在Rank/Min/String\u Agg的最后一步之前,数据需要更加规范化。它应该采取 No | Name | Quarter | Value Cross Apply UNPIVOT ,或 UNION 以达到中间状态。

        4
  •  0
  •   Tab Alleman    6 年前

    您可以通过连接4个case语句的输出来实现这一点:每个Q列对应一个case语句。

    对于每个Q列,如果它小于或等于其他列,则返回其名称加逗号,否则返回空字符串。

    对每个Q列都这样做,去掉最后一个逗号,Bob就是你的叔叔。

        5
  •  0
  •   Yogesh Sharma    6 年前

    你可以用 apply &使用(amp;U) dense_rank() 分配排名的函数 Q1, Q2, . .

    剩下的将由 cte & xml path() 条款:

    with t as (
         select *, dense_rank() over (partition by No, Name order by qval) as seq
         from TestTable t cross apply
              ( values ('Q1', Q1), ('Q2', Q2), ('Q3', Q3), ('Q4', Q4)
              ) tt (qname, qval)
    )
    
    select distinct No, Name, Q1, Q2, Q3, Q4,
           stuff( (select ',' + t1.qname
                   from t t1 
                   where t1.no = t.no and t1.name = t.name and t1.seq = 1
                   for xml path('')
                  ), 1, 1, ''
                )  as Min    
    from t;
    
        6
  •  0
  •   Gary Webb    6 年前

    你可以用 UNPIVOT MIN 价值和用途 STUFF STRING_AGG

    WITH UnPvt([No], Qn, ColName) AS
    (
        SELECT [No], Qn, #temp AS ColName
        FROM   #temp
        UNPIVOT (Qn FOR #temp In (Q1, Q2, Q3, Q4)) AS unpvt
    )
    SELECT t.*,
           STUFF((SELECT ', ' + ColName
                  FROM   UnPvt u1
                  WHERE  u1.[No] = t.[No]
                  AND    u1.Qn = (SELECT MIN(u2.Qn)
                                  FROM   UnPvt u2
                                  WHERE  u2.[No] = u1.[No]
                                  GROUP BY u2.[No]
                                 )
                  FOR XML PATH('')), 1, 2, '')
    from   #temp t