从SQL Server(2012)中的一系列csv字符串生成二进制矩阵?

时间:2019-06-14 20:36:23

标签: sql-server sql-server-2012

具有表格的形式

pk items
--------
1  a,b,c,...
2  d,e,f,...
3  g,h,i,...
4  j,k,l,...
.
.
.

商品列的值是可变长度的csv字符串(其元素不必是唯一的),例如杂货(仅在此处使用字母以提高可读性)。

因此,希望能够生成一个二进制矩阵来表示任何给定pk所包含的项目,例如。

pk a b c d e f g ...
--------------------
1  1 1 1 0 0 0 0 ...
2  0 0 0 1 1 1 0 ...
3  0 0 0 0 0 0 0 ...
.
.
.

请注意,我还可以访问一个表,该表代表csv列表中任何项目的可能值的整个域,例如。

item
------
a
b
c
.
.
.

有人知道这样做的有效方法吗?

1 个答案:

答案 0 :(得分:2)

我完全同意所作的评论,这与存储数据的理想方式相去甚远。您的表Form应该被规范化,实际上您应该使用表示层来做到这一点。我已经使用STRING_SPLIT编写了以下内容,但是,您也可以使用DelimitedSplit8k_LEAD来实现此目的。 (更正后,在使用SQL Server 2012时,您将需要这样做。)

这不是入门级的东西,所以请问您是否不了解:

--Create the table with all the items
CREATE TABLE dbo.Item (ItemID int IDENTITY (1,1),
                       ItemName varchar(2));

INSERT INTO dbo.Item(ItemName)
VALUES('a'),
      ('b'),
      ('c'),
      ('d'),
      ('e'),
      ('f'),
      ('g'),
      ('h'),
      ('i'),
      ('j'),
      ('k'),
      ('l'),
      ('m'),
      ('n'),
      ('o'),
      ('p'),
      ('q');
GO

--And now your sample data
CREATE TABLE dbo.Form (Pk int IDENTITY (1,1),
                       Items varchar(100)); --Really you shouldn't be storing delimited data

INSERT INTO dbo.Form (Items)
VALUES ('a,b,c'),
       ('d,e,f'),
       ('g,h,i'),
       ('j,k,l');

GO

--Declare the SQL variable to store the dynamic SQL
DECLARE @SQL nvarchar(MAX);

--Build the SQL. Use FOR XML PATH to create the delimited list of columns,
--STRING_SPLIT in the dynamic SQL to split the data
--and a cross tab to pivot the data
--YUCK (sorry)
SET @SQL = N'SELECT F.Pk,' + NCHAR(13) + NCHAR(10) + 
           STUFF((SELECT N',' + NCHAR(13) + NCHAR(10) + 
                         N'       CASE WHEN COUNT(CASE WHEN SS.[value] = ' + QUOTENAME(I.ItemName,'''') + N'THEN 1 END) = 0 THEN 0 ELSE 1 END AS ' + QUOTENAME(I.ItemName)
                  FROM dbo.Item I
                  ORDER BY I.ItemName ASC
                  FOR XML PATH(N''),TYPE).value('.','nvarchar(MAX)'),1,3,N'') + NCHAR(13) + NCHAR(10) + 
           N'FROM dbo.Form F' + NCHAR(13) + NCHAR(10) + 
           N'     CROSS APPLY STRING_SPLIT(F.Items,'','') SS' + NCHAR(13) + NCHAR(10) + 
           N'GROUP BY Pk;';
PRINT @SQL; --Your best friend for debugging
EXEC sp_executesql @SQL;

GO
DROP TABLE dbo.Form;
DROP TABLE dbo.Item;

db<>fiddle