我有张桌子看起来像这样:
PartNumber Part ValuesList x1 x2 x3 x4
123456 Fender x1 11 10 9 12
123456 Fender x1,x2 11 10 9 12
123456 Fender x2,x4 11 10 9 12
123456 Fender x1,x2,x3,x4 11 10 11 12
123456 Fender x2,x3,x4 11 10 9 12
对于每一行,如何根据列x1、x2、x3、x4中给出的列名找到最低值?
例如,第1行很简单。ValuesList值是x1,所以我只需要查看列x1,最低值是11。
第二排..。ValuesList值为x1,x2。我需要x1列和x2列(11和10)之间的较低的编号。最低值为10。
第三排..。ValuesList值为x2,x4。我需要列x2和x4 (10和12)之间的较低的编号。最低值为10。
第四排..。ValuesList值为x1、x2、x3、x4。我需要列x1、x2、x3和x4 (11、10、11、12)之间的较低编号。最低值是10。
第5排..。ValuesList值为x2、x3、x4。我需要x2、x3和x4列(10、9、12)之间的较低的编号。最低值为9。
实际的表有数十万行。
在伪码中,我想要的是
SELECT PartNumber, Part, MIN(ColumnValue) WHERE ColumnName IN (SELECT ColumnNames FROM ValuesList)
如果最低的数字出现在多个列中,那么从哪个列取最低的数字并不重要。
谢谢!
发布于 2021-07-27 17:32:49
我将假设您在这里使用的是最新版本的Server,因此可以访问STRING_SPLIT
。如果没有,那么您将需要使用用户定义的字符串拆分器;如果您进行搜索(虽然我建议使用基于集合的字符串拆分器),那么就有大量的字符串拆分器。然后,沿着STRING_SPLIT
,您可以解除数据的枢轴,为此,我使用一个表VALUES
构造,在WHERE
中隐式地使用JOIN
,最后聚合:
CREATE TABLE dbo.YourTable (PartNumber int,
Part varchar(10),
ValuesList varchar(8000),
x1 int,
x2 int,
x3 int,
x4 int);
GO
INSERT INTO dbo.YourTable
VALUES(123456,'Fender','x1 ',11,10,9 ,12),
(123456,'Fender','x1,x2 ',11,10,9 ,12),
(123456,'Fender','x2,x4 ',11,10,9 ,12),
(123456,'Fender','x1,x2,x3,x4',11,10,11,12),
(123456,'Fender','x2,x3,x4 ',11,10,9 ,12);
GO
SELECT YT.PartNumber,
YT.Part,
YT.ValuesList,
MIN(V.XVal) AS MinX
FROM dbo.YourTable YT
CROSS APPLY STRING_SPLIT(YT.ValuesList,',') SS
CROSS APPLY (VALUES(N'x1',x1),
(N'x2',x2),
(N'x3',x3),
(N'x4',x4))V(XCol,XVal)
WHERE SS.[value] = V.XCol
GROUP BY YT.PartNumber,
YT.Part,
YT.ValuesList;
GO
DROP TABLE dbo.YourTable;
当然,我真的建议你在这里规范化你的设计。不要存储分隔的数据,也不要重复列值。如果您将您的设计规范化,您可能只需要几个JOIN
和一个聚合。
发布于 2021-07-27 18:01:23
只是另一种选择
示例
Select A.PartNumber
,A.Part
,A.ValuesList
,B.MinValue
From YourTable A
Cross Apply ( Select MinValue=min(Value)
From (values (case when charindex('x1',ValuesList)>0 then x1 end)
,(case when charindex('x2',ValuesList)>0 then x2 end)
,(case when charindex('x3',ValuesList)>0 then x3 end)
,(case when charindex('x4',ValuesList)>0 then x4 end)
) B1(Value)
)B
结果
PartNumber Part ValuesList MinValue
123456 Fender x1 11
123456 Fender x1,x2 10
123456 Fender x1,x2,x3,x4 10
123456 Fender x2,x3,x4 9
123456 Fender x2,x4 10
发布于 2021-07-27 18:17:43
我强烈建议在没有外部group by
的情况下这样做。
SELECT t.*, s.min_val
FROM t OUTER APPLY
(SELECT MIN(val) as min_val
FROM STRING_SPLIT(YT.ValuesList,',') s JOIN
(VALUES('x1', x1), ('x2', x2), ('x3', x3),('x4', x4)
)v(col, val)
ON s.value = v.name
) s;
对行内的值使用APPLY
通常比对整个数据集进行聚合具有更好的性能特征。
https://stackoverflow.com/questions/68549299
复制相似问题