使用mssql将逗号分隔值从表列拆分为行?

时间:2014-08-28 16:29:39

标签: sql sql-server tsql

我想通过拆分它们的源来获取一些数据进入mssql视图。我有一些列,其中电话号码存储为逗号分隔值(每个包含一个电话联系人)。我希望能够与每个“电话联系人”合作,所以我希望每个人都能看到它们。而且每一行都必须包含分裂的联系顺序。

来源:

Department   | SaleMngrs   | Operators      | Secretary
----------------------------------------------------------
'Technics'   | '123,456,77'| '+122,Line 1'  | '77889,112'
'Development'| '123,3366'  | null           | 'Lines 7-8'

如您所见,逗号分隔值是一个混乱,但分词符是,(逗号)。

通缉结果:

Department   | TypeOfContact  | Contact    | ContactOrder
------------------------------------------------------
'Technics'   | 'SalesManagers'| '123'      | 1
'Technics'   | 'SalesManagers'| '456'      | 2
'Technics'   | 'SalesManagers'| '77'       | 3
'Technics'   | 'Operators'    | '+122'     | 1
'Technics'   | 'Operators'    | 'Line 1'   | 2
'Technics'   | 'Secretary'    | '77889'    | 1
'Technics'   | 'Secretary'    | '112'      | 2
'Development'| 'SalesManagers'| '123'      | 1
'Development'| 'SalesManagers'| '3366'     | 2
'Development'| 'Secretary'    | 'Lines 7-8'| 1

没有UDF或SP想要的。只需SELECT

2 个答案:

答案 0 :(得分:1)

请尝试以下方法(它与数据结构一样漂亮) - 使用UNPIVOT优化:

set nocount on

declare @source table (Department varchar(50), SaleMngrs varchar(50), Operators varchar(50), Secretary varchar(50));

insert into @source values ('Technics'   , '123,456,77', '+122,Line 1'  , '77889,112');
insert into @source values ('Development', '123,3366'  , null           , 'Lines 7-8');

;WITH cte (Department, TypeOfContact, Contact)
AS
(
   SELECT Department, TypeOfContact, cast('<Contact><c>' + replace(Contact,',','</c><c>') + '</c></Contact>' as xml) AS Contact
     FROM (SELECT Department, SaleMngrs AS SalesManagers, Operators, Secretary FROM @source) p
     UNPIVOT (Contact FOR TypeOfContact IN (SalesManagers, Operators, Secretary)) AS unpvt
)
Select Department
     , TypeOfContact
     , Contact.c.value('.','varchar(20)') AS Contact
     , ROW_NUMBER() OVER (PARTITION BY Department, TypeOfContact ORDER BY Department, TypeOfContact) AS ContactOrder
FROM cte CROSS APPLY Contact.nodes('/Contact/c') as Contact(c);

<强>输出

Department   TypeOfContact Contact              ContactOrder
------------ ------------- -------------------- --------------------
Development  SalesManagers 123                  1
Development  SalesManagers 3366                 2
Development  Secretary     Lines 7-8            1
Technics     Operators     +122                 1
Technics     Operators     Line 1               2
Technics     SalesManagers 123                  1
Technics     SalesManagers 456                  2
Technics     SalesManagers 77                   3
Technics     Secretary     112                  1
Technics     Secretary     77889                2

编辑:使用UNPIVOT进行优化查询(原文如下):

set nocount on

declare @source table (Department varchar(50), SaleMngrs varchar(50), Operators varchar(50), Secretary varchar(50));

insert into @source values ('Technics'   , '123,456,77', '+122,Line 1'  , '77889,112');
insert into @source values ('Development', '123,3366'  , null           , 'Lines 7-8');

;WITH cte (Department, SalesMngrs, Operators, Secretary)
AS
(
   select Department
        , cast('<SaleMngrs><c>' + replace(SaleMngrs,',','</c><c>') + '</c></SaleMngrs>' as xml) AS SalesMngrs
        , cast('<Operators><c>' + replace(Operators,',','</c><c>') + '</c></Operators>' as xml) AS Operators
        , cast('<Secretary><c>' + replace(Secretary,',','</c><c>') + '</c></Secretary>' as xml) AS Secretary
     from @source
)
Select Department
     , TypeOfContact
     , Contact
     , ROW_NUMBER() OVER (PARTITION BY Department, TypeOfContact ORDER BY Department, TypeOfContact) AS ContactOrder
FROM (
   Select Department, 'SalesManagers' AS TypeOfContact, SaleMngrs.c.value('.','varchar(20)') as Contact
   from cte CROSS APPLY SalesMngrs.nodes('/SaleMngrs/c') as SaleMngrs(c)
   union
   Select Department, 'Operators', Operators.c.value('.','varchar(20)')
   from cte CROSS APPLY Operators.nodes('/Operators/c') as Operators(c)
   union
   Select Department, 'Secretary', Secretary.c.value('.','varchar(20)')
   from cte CROSS APPLY Secretary.nodes('/Secretary/c') as Secretary(c)
) AS q;

答案 1 :(得分:0)

马丁,

我知道你想在一个SQL语句中使用它,但如果你创建一个函数,那么SQL就不会那么不合适。

ALTER FUNCTION dbo.Split (  @InputString  VARCHAR(8000),  @Delimiter VARCHAR(50))  
RETURNS @Items TABLE ( Item VARCHAR(8000), Rowid INT)  
AS  
BEGIN  
      IF @Delimiter = ' '  
      BEGIN  
            SET @Delimiter = ','  
            SET @InputString = REPLACE(@InputString, ' ', @Delimiter)  
      END  

      IF (@Delimiter IS NULL OR @Delimiter = '')  
            SET @Delimiter = ','  

      DECLARE @Item                 VARCHAR(8000)  
      DECLARE @ItemList       VARCHAR(8000)  
      DECLARE @DelimIndex     INT  
      declare @rowseq         INT

      SET @rowseq = 0

      SET @ItemList = @InputString  
      SET @DelimIndex = CHARINDEX(@Delimiter, @ItemList, 0)  
      WHILE (@DelimIndex != 0)  
      BEGIN  
            SET @Item = SUBSTRING(@ItemList, 0, @DelimIndex)
            SET @rowseq = @rowseq + 1  
            INSERT INTO @Items VALUES (@Item, @rowseq)  

            -- Set @ItemList = @ItemList minus one less item  
            SET @ItemList = SUBSTRING(@ItemList, @DelimIndex+1, LEN(@ItemList)-@DelimIndex)  
            SET @DelimIndex = CHARINDEX(@Delimiter, @ItemList, 0)  
      END -- End WHILE  

      IF @Item IS NOT NULL -- At least one delimiter was encountered in @InputString  
      BEGIN  
            SET @Item = @ItemList  
            SET @rowseq = @rowseq + 1
            INSERT INTO @Items VALUES (@Item, @rowseq)  
      END  

      -- No delimiters were encountered in @InputString, so just return @InputString  
      ELSE INSERT INTO @Items VALUES (@InputString, 1)  

      RETURN  

END

上面的功能我从这个问题中找到了,但我为你的场景做了一些改动。 How to split a comma-separated value to rows

然后你将SQL ...

SELECT department, 'SalesManager' as TypeOfContract, s.Item as Contact , s.rowId 
FROM <YOUR TABLE> t 
     CROSS APPLY 
       (SELECT * FROM dbo.Split(t.SalesMngrs, ',') where item is not null) S
UNION ALL 
SELECT department, 'Operators' as TypeOfContract, s.Item as Contact , s.rowId  
FROM <YOUR TABLE> t

     CROSS APPLY 
       (SELECT * FROM dbo.Split(t.Operators, ',') where item is not null ) S   
UNION ALL
SELECT department, 'Secretary' as TypeOfContract, s.Item as Contact , s.rowId  
FROM <YOUR TABLE> t 
     CROSS APPLY 
       (SELECT * FROM dbo.Split(t.Secretary, ',') where item is not null) S      

我希望这会有所帮助。

相关问题