获取保存行最大值的列名
Getting the column name that holds the maximum value of a row
我生成了一个广泛的视图,它根据不同的统计模型模拟了某些事件。这些模型在每一列 header 中由一个数字定义,并且一行中的最大值是最佳模型。
table 生成的外观(部分)如下;
+--------+----+------+------+------+------+------+------+
| Number | LI | PHSE | 0505 | 0506 | 0507 | 0508 | 0509 | [...] etc.
+--------+----+------+------+------+------+------+------+
| 100254 | 2 | M1 | 44 | 46 | 45 | 44 | 44 |
| 100254 | 2 | M2 | 36 | 36 | 35 | 37 | 37 |
| 100254 | 2 | M3 | 5 | 5 | 5 | 5 | 5 |
| 100254 | 2 | R1 | 34 | 36 | 37 | 37 | 37 |
| 100254 | 2 | R2 | 41 | 41 | 40 | 41 | 41 |
| 100329 | 1 | M1 | 37 | 38 | 38 | 38 | 39 |
| 100329 | 1 | M2 | 31 | 29 | 28 | 29 | 29 |
| 100329 | 1 | M3 | 6 | 6 | 6 | 6 | 6 |
| 100329 | 1 | R1 | 29 | 29 | 29 | 30 | 30 |
| 100329 | 1 | R2 | 25 | 26 | 26 | 27 | 26 |
+--------+----+------+------+------+------+------+------+
[...] etc.
现在我想在每个行中找到最大值并显示相应的列名;
| Number | LI | PHSE | MAXCOL |
+--------+----+------+--------+
| 100254 | 2 | M1 | 0506 |
| 100254 | 2 | M2 | 0508 |
| 100254 | 2 | M3 | 0505 |
| 100254 | 2 | R1 | 0507 |
| 100254 | 2 | R2 | 0505 |
+--------+----+------+--------+
[...] etc.
这是从100254 - 2 - M1最大值46出现在第0506列等推导出来的
我一直在研究 PIVOT
函数,但没有成功。我还寻找了 Index/Match 中的等价物,例如 Excel,但由于我无法将 headers 列作为值,这显然行不通(而且还没有找到这样的功能)。
任何帮助将不胜感激。
根据 Damien 的评论更新:
导致此问题的代码摘录:
SELECT DISTINCT sub2.Number, sub2.LI, sub2.PHSE
, sum(sub2.[0505]) over (partition by sub2.Number, sub2.LI, sub2.PHSE) as '0505'
, sum(sub2.[0506]) over (partition by sub2.Number, sub2.LI, sub2.PHSE) as '0506'
[...] etc. /*64 rows*/
FROM
(SELECT DISTINCT sub.*
, CASE WHEN sub.MF > sub.[5PAV] - sub.[5PSTDEV] THEN 1 ELSE 0 END AS '0505'
, CASE WHEN sub.MF > sub.[5PAV] - sub.[6PSTDEV] THEN 1 ELSE 0 END AS '0506'
[...] etc. /*64 rows*/
FROM
(SELECT DISTINCT ra.*
, sum(ra.qtyr) OVER (partition BY ra.Number, ra.LI, ra.PHSE ORDER BY (ra.Number) rows BETWEEN 5 preceding AND 1 preceding) /
sum(ra.qtyu) OVER (partition BY ra.Number, ra.LI, ra.PHSE ORDER BY (ra.Number) rows BETWEEN 5 preceding AND 1 preceding) AS '5PAV'
, sum(ra.qtyr) OVER (partition BY ra.Number, ra.LI, ra.PHSE ORDER BY (ra.Number) rows BETWEEN 6 preceding AND 1 preceding) /
sum(ra.qtyu) OVER (partition BY ra.Number, ra.LI, ra.PHSE ORDER BY (ra.Number) rows BETWEEN 6 preceding AND 1 preceding) AS '6PAV'
[...] etc. /*8 rows*/
, stdev(ra.MF) OVER (partition BY ra.Number, ra.LI, ra.PHSE ORDER BY (ra.Number) rows BETWEEN 4 preceding AND CURRENT row) AS '5PSTDEV'
, stdev(ra.MF) OVER (partition BY ra.Number, ra.LI, ra.PHSE ORDER BY (ra.Number) rows BETWEEN 5 preceding AND CURRENT row) AS '6PSTDEV'
[...] etc. /*8 rows*/
FROM ra
) AS sub
) AS sub2
毫无疑问,这可能是SQL最无效的用法之一,但是time-pressure和缺乏经验让我这样写。
任何更改此代码并更有效地实现所需 table 结果的建议也将不胜感激。
根据安东的回答进行编辑;
以上代码总结为pvt
继续如下;
SELECT Number, LI, PHSE, combo, hitrate
FROM (...) AS pvt
UNPIVOT
(Hitrate FOR Combo IN (
[0505],
[0506],
[...] etc.)) AS upvt
解决不雅问题的好方法。
您需要使用 UNPIVOT,而不是 PIVOT
https://msdn.microsoft.com/en-us/library/ms177410.aspx
如果列数可变,则必须使用动态sql构造列列表
我会使用 CROSS APPLY
如下:
WITH Src AS
(
SELECT * FROM (VALUES
(100254, 2, 'M1', 44, 46, 45, 44, 44),
(100254, 2, 'M2', 36, 36, 35, 37, 37),
(100254, 2, 'M3', 5, 5, 5, 5, 5),
(100254, 2, 'R1', 34, 36, 37, 37, 37),
(100254, 2, 'R2', 41, 41, 40, 41, 41),
(100329, 1, 'M1', 37, 38, 38, 38, 39),
(100329, 1, 'M2', 31, 29, 28, 29, 29),
(100329, 1, 'M3', 6, 6, 6, 6, 6),
(100329, 1, 'R1', 29, 29, 29, 30, 30),
(100329, 1, 'R2', 25, 26, 26, 27, 26)) T(Number, LI, PHSE, [0505], [0506], [0507], [0508], [0509])
)
SELECT Number, LI, PHSE, MaxCol
FROM Src
CROSS APPLY (SELECT TOP 1 * FROM (VALUES
('0505', [0505]),
('0506', [0506]),
('0507', [0507]),
('0508', [0508]),
('0509', [0509])
) T(MaxCol, Val) ORDER BY Val DESC) Q
请注意,可以随机选择相等的列,除非指定了一些附加值,即 MaxCol。
更新
您需要动态查询,如下所示:
DECLARE @sql nvarchar(MAX) =
'SELECT Number, LI, PHSE, MaxCol
FROM Src
CROSS APPLY (SELECT TOP 1 * FROM (VALUES' +STUFF(
(SELECT ',(', QUOTENAME(name, '''')+','+QUOTENAME(name)+')'
FROM sys.columns
WHERE object_id=OBJECT_ID('Src') AND name NOT IN ('Number', 'LI', 'PHSE')
FOR XML PATH('')), 1, 1, '')+') T(MaxCol, Val) ORDER BY Val DESC) Q';
EXEC(@sql);
Src
是你的 table 名字,相应地替换它。
如果您想查看所有 MAXCOL(等列),试试这个:
CREATE TABLE table1 (
Number NUMERIC(10),
LI numeric(10),
PHSE NVARCHAR(10),
[0505] numeric(10),
[0506] numeric(10),
[0507] numeric(10),
[0508] numeric(10),
[0509] numeric(10))
INSERT INTO table1 VALUES(100254,2,'M1',44 ,46 ,45 ,44,44)
INSERT INTO table1 VALUES(100254,2,'M2',36,36,35,37,37)
INSERT INTO table1 VALUES(100254,2,'M3',5,5,5,5,5)
INSERT INTO table1 VALUES(100254,2,'R1',34,36,37,37,37)
INSERT INTO table1 VALUES(100254,2, 'R2',41,41,40,41,41)
INSERT INTO table1 VALUES(100329,1, 'M1',37,38,38,38,39)
INSERT INTO table1 VALUES(100329,1, 'M2',31,29,28,29,29)
INSERT INTO table1 VALUES(100329,1, 'M3',6,6,6,6,6)
INSERT INTO table1 VALUES(100329,1, 'R1',29,29,29,30,30)
INSERT INTO table1 VALUES(100329,1, 'R2',25,26,26,27,26)
SELECT *
INTO #UNPIVOT
FROM table1
UNPIVOT ( num
FOR MAXCOL IN ([0505],[0506],[0507],[0508],[0509])) AS k
SELECT A.Number,A.LI,A.PHSE,A.num,B.MAXCOL FROM
(SELECT number,LI,PHSE,MAX(num) AS num FROM #UNPIVOT GROUP BY number,LI,PHSE) A
LEFT JOIN
(SELECT * FROM #UNPIVOT) B ON A.num=B.num AND A.Number=B.Number AND A.LI=B.LI AND A.PHSE=B.PHSE
您可以使用 UNPIVOT 和 CROSS APPLY。
使用此解决方案,您不必多次指定所有列。
源数据:
CREATE TABLE Student
([Name] varchar(5), [Maths] int, [Science] int, [English] int)
;
INSERT INTO Student
([Name], [Maths], [Science], [English])
VALUES
('Tilak', 90, 40, 60),
('Raj', 30, 50, 70)
;
解法:
with foo as (
select name, subject, marks
from student
unpivot
(
marks
for subject in (Science, Maths, English)
) u
)
select distinct f1.name, f2.subject
from foo f1
cross apply (
select top 1 name, subject
from foo
where f1.Name = Name
order by Marks desc) f2
结果:
--------------------------------------------------
| Name | Subject
--------------------------------------------------
| Tilak | Maths
--------------------------------------------------
| Raj | Science
--------------------------------------------------
我生成了一个广泛的视图,它根据不同的统计模型模拟了某些事件。这些模型在每一列 header 中由一个数字定义,并且一行中的最大值是最佳模型。
table 生成的外观(部分)如下;
+--------+----+------+------+------+------+------+------+
| Number | LI | PHSE | 0505 | 0506 | 0507 | 0508 | 0509 | [...] etc.
+--------+----+------+------+------+------+------+------+
| 100254 | 2 | M1 | 44 | 46 | 45 | 44 | 44 |
| 100254 | 2 | M2 | 36 | 36 | 35 | 37 | 37 |
| 100254 | 2 | M3 | 5 | 5 | 5 | 5 | 5 |
| 100254 | 2 | R1 | 34 | 36 | 37 | 37 | 37 |
| 100254 | 2 | R2 | 41 | 41 | 40 | 41 | 41 |
| 100329 | 1 | M1 | 37 | 38 | 38 | 38 | 39 |
| 100329 | 1 | M2 | 31 | 29 | 28 | 29 | 29 |
| 100329 | 1 | M3 | 6 | 6 | 6 | 6 | 6 |
| 100329 | 1 | R1 | 29 | 29 | 29 | 30 | 30 |
| 100329 | 1 | R2 | 25 | 26 | 26 | 27 | 26 |
+--------+----+------+------+------+------+------+------+
[...] etc.
现在我想在每个行中找到最大值并显示相应的列名;
| Number | LI | PHSE | MAXCOL |
+--------+----+------+--------+
| 100254 | 2 | M1 | 0506 |
| 100254 | 2 | M2 | 0508 |
| 100254 | 2 | M3 | 0505 |
| 100254 | 2 | R1 | 0507 |
| 100254 | 2 | R2 | 0505 |
+--------+----+------+--------+
[...] etc.
这是从100254 - 2 - M1最大值46出现在第0506列等推导出来的
我一直在研究 PIVOT
函数,但没有成功。我还寻找了 Index/Match 中的等价物,例如 Excel,但由于我无法将 headers 列作为值,这显然行不通(而且还没有找到这样的功能)。
任何帮助将不胜感激。
根据 Damien 的评论更新:
导致此问题的代码摘录:
SELECT DISTINCT sub2.Number, sub2.LI, sub2.PHSE
, sum(sub2.[0505]) over (partition by sub2.Number, sub2.LI, sub2.PHSE) as '0505'
, sum(sub2.[0506]) over (partition by sub2.Number, sub2.LI, sub2.PHSE) as '0506'
[...] etc. /*64 rows*/
FROM
(SELECT DISTINCT sub.*
, CASE WHEN sub.MF > sub.[5PAV] - sub.[5PSTDEV] THEN 1 ELSE 0 END AS '0505'
, CASE WHEN sub.MF > sub.[5PAV] - sub.[6PSTDEV] THEN 1 ELSE 0 END AS '0506'
[...] etc. /*64 rows*/
FROM
(SELECT DISTINCT ra.*
, sum(ra.qtyr) OVER (partition BY ra.Number, ra.LI, ra.PHSE ORDER BY (ra.Number) rows BETWEEN 5 preceding AND 1 preceding) /
sum(ra.qtyu) OVER (partition BY ra.Number, ra.LI, ra.PHSE ORDER BY (ra.Number) rows BETWEEN 5 preceding AND 1 preceding) AS '5PAV'
, sum(ra.qtyr) OVER (partition BY ra.Number, ra.LI, ra.PHSE ORDER BY (ra.Number) rows BETWEEN 6 preceding AND 1 preceding) /
sum(ra.qtyu) OVER (partition BY ra.Number, ra.LI, ra.PHSE ORDER BY (ra.Number) rows BETWEEN 6 preceding AND 1 preceding) AS '6PAV'
[...] etc. /*8 rows*/
, stdev(ra.MF) OVER (partition BY ra.Number, ra.LI, ra.PHSE ORDER BY (ra.Number) rows BETWEEN 4 preceding AND CURRENT row) AS '5PSTDEV'
, stdev(ra.MF) OVER (partition BY ra.Number, ra.LI, ra.PHSE ORDER BY (ra.Number) rows BETWEEN 5 preceding AND CURRENT row) AS '6PSTDEV'
[...] etc. /*8 rows*/
FROM ra
) AS sub
) AS sub2
毫无疑问,这可能是SQL最无效的用法之一,但是time-pressure和缺乏经验让我这样写。
任何更改此代码并更有效地实现所需 table 结果的建议也将不胜感激。
根据安东的回答进行编辑;
以上代码总结为pvt
继续如下;
SELECT Number, LI, PHSE, combo, hitrate
FROM (...) AS pvt
UNPIVOT
(Hitrate FOR Combo IN (
[0505],
[0506],
[...] etc.)) AS upvt
解决不雅问题的好方法。
您需要使用 UNPIVOT,而不是 PIVOT
https://msdn.microsoft.com/en-us/library/ms177410.aspx
如果列数可变,则必须使用动态sql构造列列表
我会使用 CROSS APPLY
如下:
WITH Src AS
(
SELECT * FROM (VALUES
(100254, 2, 'M1', 44, 46, 45, 44, 44),
(100254, 2, 'M2', 36, 36, 35, 37, 37),
(100254, 2, 'M3', 5, 5, 5, 5, 5),
(100254, 2, 'R1', 34, 36, 37, 37, 37),
(100254, 2, 'R2', 41, 41, 40, 41, 41),
(100329, 1, 'M1', 37, 38, 38, 38, 39),
(100329, 1, 'M2', 31, 29, 28, 29, 29),
(100329, 1, 'M3', 6, 6, 6, 6, 6),
(100329, 1, 'R1', 29, 29, 29, 30, 30),
(100329, 1, 'R2', 25, 26, 26, 27, 26)) T(Number, LI, PHSE, [0505], [0506], [0507], [0508], [0509])
)
SELECT Number, LI, PHSE, MaxCol
FROM Src
CROSS APPLY (SELECT TOP 1 * FROM (VALUES
('0505', [0505]),
('0506', [0506]),
('0507', [0507]),
('0508', [0508]),
('0509', [0509])
) T(MaxCol, Val) ORDER BY Val DESC) Q
请注意,可以随机选择相等的列,除非指定了一些附加值,即 MaxCol。
更新 您需要动态查询,如下所示:
DECLARE @sql nvarchar(MAX) =
'SELECT Number, LI, PHSE, MaxCol
FROM Src
CROSS APPLY (SELECT TOP 1 * FROM (VALUES' +STUFF(
(SELECT ',(', QUOTENAME(name, '''')+','+QUOTENAME(name)+')'
FROM sys.columns
WHERE object_id=OBJECT_ID('Src') AND name NOT IN ('Number', 'LI', 'PHSE')
FOR XML PATH('')), 1, 1, '')+') T(MaxCol, Val) ORDER BY Val DESC) Q';
EXEC(@sql);
Src
是你的 table 名字,相应地替换它。
如果您想查看所有 MAXCOL(等列),试试这个:
CREATE TABLE table1 (
Number NUMERIC(10),
LI numeric(10),
PHSE NVARCHAR(10),
[0505] numeric(10),
[0506] numeric(10),
[0507] numeric(10),
[0508] numeric(10),
[0509] numeric(10))
INSERT INTO table1 VALUES(100254,2,'M1',44 ,46 ,45 ,44,44)
INSERT INTO table1 VALUES(100254,2,'M2',36,36,35,37,37)
INSERT INTO table1 VALUES(100254,2,'M3',5,5,5,5,5)
INSERT INTO table1 VALUES(100254,2,'R1',34,36,37,37,37)
INSERT INTO table1 VALUES(100254,2, 'R2',41,41,40,41,41)
INSERT INTO table1 VALUES(100329,1, 'M1',37,38,38,38,39)
INSERT INTO table1 VALUES(100329,1, 'M2',31,29,28,29,29)
INSERT INTO table1 VALUES(100329,1, 'M3',6,6,6,6,6)
INSERT INTO table1 VALUES(100329,1, 'R1',29,29,29,30,30)
INSERT INTO table1 VALUES(100329,1, 'R2',25,26,26,27,26)
SELECT *
INTO #UNPIVOT
FROM table1
UNPIVOT ( num
FOR MAXCOL IN ([0505],[0506],[0507],[0508],[0509])) AS k
SELECT A.Number,A.LI,A.PHSE,A.num,B.MAXCOL FROM
(SELECT number,LI,PHSE,MAX(num) AS num FROM #UNPIVOT GROUP BY number,LI,PHSE) A
LEFT JOIN
(SELECT * FROM #UNPIVOT) B ON A.num=B.num AND A.Number=B.Number AND A.LI=B.LI AND A.PHSE=B.PHSE
您可以使用 UNPIVOT 和 CROSS APPLY。
使用此解决方案,您不必多次指定所有列。
源数据:
CREATE TABLE Student
([Name] varchar(5), [Maths] int, [Science] int, [English] int)
;
INSERT INTO Student
([Name], [Maths], [Science], [English])
VALUES
('Tilak', 90, 40, 60),
('Raj', 30, 50, 70)
;
解法:
with foo as (
select name, subject, marks
from student
unpivot
(
marks
for subject in (Science, Maths, English)
) u
)
select distinct f1.name, f2.subject
from foo f1
cross apply (
select top 1 name, subject
from foo
where f1.Name = Name
order by Marks desc) f2
结果:
--------------------------------------------------
| Name | Subject
--------------------------------------------------
| Tilak | Maths
--------------------------------------------------
| Raj | Science
--------------------------------------------------