【问题标题】:Split comma separated value from table column into rows using mssql?使用mssql将逗号分隔值从表列拆分为行?
【发布时间】:2014-08-28 16:29:39
【问题描述】:

我想通过拆分源将一些数据放入 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

【问题讨论】:

  • 您无法在单个 select 语句中做到这一点。
  • TypeOfContact 在那里。第二列是 SalesManagers,然后是 Operators 和 Secretaries。
  • @Sean Lange 我不明白你的第二条评论是什么意思...
  • 呵呵。其他人删除了他们的评论。他们询问 TypeOfContact 在您的数据中的位置。没有那个评论似乎很奇怪。

标签: sql sql-server tsql


【解决方案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;

【讨论】:

  • 我认为可以使用 UNPIVOT 来优化这个查询。
【解决方案2】:

马丁,

我知道您希望在一个 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

我从这个 SO question 中找到了上述功能,但我针对您的场景对其进行了一些更改。 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      

我希望这会有所帮助。

【讨论】:

  • Thanx @Ed Mendez,你试过表演吗?它比@wdosanjos 上面写的单选好还是差?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多