将一个列值拆分为多个列值

时间:2017-05-04 17:46:10

标签: sql sql-server tsql split

我有订阅表,订阅号实际上是一个存储在一列中的值。我们有以下样本值

SC 5-1395-174-25P 
SC 1-2134-123-ABC C1-2
SC 12-5245-1247-14&P
SC ABCD-2525-120

因此我们需要将其拆分为单独的列。所以上面四个应该按照以下方式分割

**Col1**   **Col2**   **Col3**   **Col4**  **Col5**  **Col6**   **Col7**
**SC**      **5**     **1395**   **174**   **25P** 
**SC**      **1**     **2134**   **123**   **ABC**   **C1**      **2**
**SC**      **12**    **5245**   **1247**  **14&P**
**SC**      **ABCD**  **2525**   **120**

4 个答案:

答案 0 :(得分:5)

这是一种在线方法

示例

Declare @YourTable table (SomeCol varchar(max))
Insert Into @YourTable values
('SC 5-1395-174-25P'),
('SC 1-2134-123-ABC C1-2'),
('SC 12-5245-1247-14&P'),
('SC ABCD-2525-120')


Select B.*
 From  @YourTable A
 Cross Apply (
                Select Pos1 = ltrim(rtrim(xDim.value('/x[1]','varchar(max)')))
                      ,Pos2 = ltrim(rtrim(xDim.value('/x[2]','varchar(max)')))
                      ,Pos3 = ltrim(rtrim(xDim.value('/x[3]','varchar(max)')))
                      ,Pos4 = ltrim(rtrim(xDim.value('/x[4]','varchar(max)')))
                      ,Pos5 = ltrim(rtrim(xDim.value('/x[5]','varchar(max)')))
                      ,Pos6 = ltrim(rtrim(xDim.value('/x[6]','varchar(max)')))
                      ,Pos7 = ltrim(rtrim(xDim.value('/x[7]','varchar(max)')))
                From  (Select Cast('<x>' + replace((Select replace(replace(A.SomeCol,' ','-'),'-','§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>' as xml) as xDim) as A 
             ) B

<强>返回

enter image description here

  

修改

1)创建表

CREATE TABLE MyNewPubTable (PUB_FORM_NUM NVARCHAR(50) , COL1 NVARCHAR(10) , COL2 NVARCHAR(10) , COL3 NVARCHAR(10) , COL4 NVARCHAR(10) , COL5 NVARCHAR(10) , COL6 NVARCHAR(10) , COL7 NVARCHAR(10)) 

2)执行查询

Declare @YourTable table (PUB_FORM_NUM varchar(max))
Insert Into @YourTable values
('SC 5-1395-174-25P'),
('SC 1-2134-123-ABC C1-2'),
('SC 12-5245-1247-14&P'),
('SC ABCD-2525-120')

Insert Into MyNewPubTable
Select A.PUB_FORM_NUM
      ,B.*
 From  @YourTable A
 Cross Apply (
                Select Pos1 = ltrim(rtrim(xDim.value('/x[1]','varchar(max)')))
                      ,Pos2 = ltrim(rtrim(xDim.value('/x[2]','varchar(max)')))
                      ,Pos3 = ltrim(rtrim(xDim.value('/x[3]','varchar(max)')))
                      ,Pos4 = ltrim(rtrim(xDim.value('/x[4]','varchar(max)')))
                      ,Pos5 = ltrim(rtrim(xDim.value('/x[5]','varchar(max)')))
                      ,Pos6 = ltrim(rtrim(xDim.value('/x[6]','varchar(max)')))
                      ,Pos7 = ltrim(rtrim(xDim.value('/x[7]','varchar(max)')))
                From  (Select Cast('<x>' + replace((Select replace(replace(A.PUB_FORM_NUM,' ','-'),'-','§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>' as xml) as xDim) as A 
             ) B

3)查看结果

Select * From MyNewPubTable
  

修改2

或者您可以动态创建表格

Declare @YourTable table (PUB_FORM_NUM varchar(max))
Insert Into @YourTable values
('SC 5-1395-174-25P'),
('SC 1-2134-123-ABC C1-2'),
('SC 12-5245-1247-14&P'),
('SC ABCD-2525-120')

Select A.PUB_FORM_NUM
      ,B.*
 Into  MyNewPubTable
 From  @YourTable A
 Cross Apply (
                Select Pos1 = ltrim(rtrim(xDim.value('/x[1]','varchar(max)')))
                      ,Pos2 = ltrim(rtrim(xDim.value('/x[2]','varchar(max)')))
                      ,Pos3 = ltrim(rtrim(xDim.value('/x[3]','varchar(max)')))
                      ,Pos4 = ltrim(rtrim(xDim.value('/x[4]','varchar(max)')))
                      ,Pos5 = ltrim(rtrim(xDim.value('/x[5]','varchar(max)')))
                      ,Pos6 = ltrim(rtrim(xDim.value('/x[6]','varchar(max)')))
                      ,Pos7 = ltrim(rtrim(xDim.value('/x[7]','varchar(max)')))
                From  (Select Cast('<x>' + replace((Select replace(replace(A.PUB_FORM_NUM,' ','-'),'-','§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>' as xml) as xDim) as A 
             ) B

Select * From MyNewPubTable

答案 1 :(得分:2)

在SQL Server 2016+中,您可以使用string_split()

在2016年之前的SQL Server中,使用Jeff Moden的CSV Splitter表值函数:

select 
    id, Col1 = [1], Col2 = [2], Col3 = [3], Col4 = [4], Col5 = [5], Col6 = [6], Col7 = [7] 
from t
  cross apply dbo.DelimitedSplit8k(replace(col,'-',' '),' ') s
  pivot (max(Item) for ItemNumber in ([1], [2], [3], [4], [5], [6], [7])) p
order by id

rextester演示:http://rextester.com/PZYAF36892

返回:

+----+------+------+------+------+------+------+------+
| id | Col1 | Col2 | Col3 | Col4 | Col5 | Col6 | Col7 |
+----+------+------+------+------+------+------+------+
|  1 | SC   | 5    | 1395 |  174 | 25P  | NULL | NULL |
|  2 | SC   | 1    | 2134 |  123 | ABC  | C1   | 2    |
|  3 | SC   | 12   | 5245 | 1247 | 14&P | NULL | NULL |
|  4 | SC   | ABCD | 2525 |  120 | NULL | NULL | NULL |
+----+------+------+------+------+------+------+------+

拆分字符串参考:

答案 2 :(得分:1)

更新:包含最终查询

由于数据存在于表中,您可以使用索引视图来分割值。什么?!?!是的,我要告诉你的是比DelimitedSplit8K,string_split或任何CLR更快。以下是我们如何实现这一目标。

<强> 1。创建/填充您的表格和真实的Tally Table(又名“数字表格”)

请注意,“cte tally / numbers表”不适用于此)。

USE tempdb
GO

-- 1. Create/Populate your table and a real Tally Table
IF OBJECT_ID('dbo.yourView') IS NOT NULL DROP VIEW dbo.yourView;
IF OBJECT_ID('dbo.tally') IS NOT NULL DROP TABLE dbo.tally;
CREATE TABLE dbo.tally
(
  N int NOT NULL,
  CONSTRAINT pk_tally PRIMARY KEY CLUSTERED(N) ,
  CONSTRAINT uq_tally UNIQUE NONCLUSTERED(N)
);
GO
IF OBJECT_ID('dbo.yourTable') IS NOT NULL DROP TABLE dbo.yourTable;
CREATE TABLE dbo.yourTable
(
  SomeId  int identity NOT NULL,
  SomeCol varchar(8000),
  CONSTRAINT pk_yourTable PRIMARY KEY (SomeId)
);
GO

INSERT dbo.tally
SELECT TOP (10000) ROW_NUMBER() OVER (ORDER BY (SELECT 1))-1
FROM sys.all_columns a, sys.all_columns b;

INSERT dbo.YourTable VALUES
('SC 5-1395-174-25P'),
('SC 1-2134-123-ABC C1-2'),
('SC 12-5245-1247-14&P'),
('SC ABCD-2525-120');
GO

<强> 2。使用dbo.tally创建一个用于分割值的索引视图

CREATE VIEW dbo.yourView
WITH SCHEMABINDING AS
SELECT 
 SomeId,
 SomeCol,
 position = N+1,
 Item =
 SUBSTRING
 (
   REPLACE(REPLACE(somecol, '-', '|'), ' ', '|'),
   N+1,
   ISNULL(NULLIF(CHARINDEX('|',REPLACE(REPLACE(somecol,'-','|'),' ','|'),N+1),0)-(N+1),8000)
 )
FROM dbo.YourTable
CROSS JOIN dbo.tally
WHERE N <= LEN(SomeCol) -- Use this predicate first to ensure we get a nonclustered index seek
AND (N=0 OR SUBSTRING(REPLACE(REPLACE(somecol,'-','|'),' ','|'),N,1) = '|');
GO

CREATE UNIQUE CLUSTERED INDEX uq_cl_yourView ON dbo.yourView(SomeId, position);
GO

让我们回顾一下到目前为止:

SELECT * FROM dbo.yourView;

SomeId      SomeCol              position    item
----------- -------------------- ----------- -----
1           SC 5-1395-174-25P    1           SC
1           SC 5-1395-174-25P    4           5
1           SC 5-1395-174-25P    6           1395
1           SC 5-1395-174-25P    11          174
1           SC 5-1395-174-25P    15          25P
2           SC 1-2134-123-ABC C1 1           SC
2           SC 1-2134-123-ABC C1 4           1
2           SC 1-2134-123-ABC C1 6           2134
2           SC 1-2134-123-ABC C1 11          123
2           SC 1-2134-123-ABC C1 15          ABC
2           SC 1-2134-123-ABC C1 19          C1
2           SC 1-2134-123-ABC C1 22          2
3           SC 12-5245-1247-14&P 1           SC
3           SC 12-5245-1247-14&P 4           12
3           SC 12-5245-1247-14&P 7           5245
3           SC 12-5245-1247-14&P 12          1247
3           SC 12-5245-1247-14&P 17          14&P
4           SC ABCD-2525-120     1           SC
4           SC ABCD-2525-120     4           ABCD
4           SC ABCD-2525-120     9           2525
4           SC ABCD-2525-120     14          120

第3。使用Jeff Moden的“cross tab”方法进行“pivot”

 WITH getItemNumber AS
    (
      SELECT 
        SomeId,
        ItemNumber = ROW_NUMBER() OVER (PARTITION BY SomeId ORDER BY position),
        Item 
      FROM dbo.yourView WITH (NOEXPAND)
    )
    SELECT 
      SomeId ,
      pos1 = MAX(CASE ItemNumber WHEN 1 THEN item END),
      pos2 = MAX(CASE ItemNumber WHEN 2 THEN item END),
      pos3 = MAX(CASE ItemNumber WHEN 3 THEN item END),
      pos4 = MAX(CASE ItemNumber WHEN 4 THEN item END),
      pos5 = MAX(CASE ItemNumber WHEN 5 THEN item END),
      pos6 = MAX(CASE ItemNumber WHEN 6 THEN item END), 
      pos7 = MAX(CASE ItemNumber WHEN 7 THEN item END)
    FROM getItemNumber
    GROUP BY SomeId;

**结果**

SomeId  pos1    pos2   pos3    pos4  pos5   pos6   pos7
------- ------- ------ ------- ----- ------ ------ -----
1       SC      5      1395    174   25P    NULL   NULL
2       SC      1      2134    123   ABC    C1     2
3       SC      12     5245    1247  14&P   NULL   NULL
4       SC      ABCD   2525    120   NULL   NULL   NULL

<强> 4。审查执行计划时微笑

enter image description here

答案 3 :(得分:0)

试试吧

Declare @YourTable table (ID int,SomeCol varchar(500))
Insert Into @YourTable values
(1,'5-1395-174-25P'),
(2,'1-2134-123-ABC C1-2'),
(3,'SC 12-5245-1247-14&P'),  
(4,'SC ABCD-2525-120*')

Select A.ID
      ,B.RetVal
 From  @YourTable A
 Cross Apply (
                Select RetSeq = Row_Number() over (Order By (Select null))
                      ,RetVal = LTrim(RTrim(B.i.value('(./text())[1]', 'varchar(max)')))
                From  (Select x = Cast('<x>' + replace((Select replace(replace(A.SomeCol,' ','§§Split§§'),'-','§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>' as xml).query('.')) as A 
                Cross Apply x.nodes('x') AS B(i)
             ) B
 Where RetSeq=1

根据需要进行更改