繁体   English   中英

如何从具有多行的SQL Server表中将值(分隔符分隔)为不同的列?

[英]How to separate values (delimiter separated) as different columns from SQL Server table having multiple rows?

我有一个SQL Server表,其中包含多行,如下所示。 每行都有一个定界符“ ^”。 我想从每一行中创建一个单独的列。

假设这是原始表:

源表 (仅一列)

StringVal
-------------------------------------------------
57^H:\ ^ 200^Test ^2018-09-19 08:20:01.000
8^T:\ ^ 88^Test1 ^2018-09-1 08:00:01.000
33^D:\ ^ 40^Test2 ^2018-10-1 08:10:01.000

我的要求是使用上表在输出中选择以下列

DestinationTable (5列)

FreeSpace | Total    | Drive | Server | Date
----------+----------+-------+--------+--------------------------
 57       |   200    | H:\   |  Test  | 2018-09-19 08:20:01.000
  8       |    88    | T:\   |  Test1 | 2018-09-1 08:00:01.000
 33       |    40    | D:\   |  Test2 | 2018-10-1 08:10:01.000

注意:源表中的字符串也包含空值。 该字符串还包含许多空白位置,没有任何顺序。 因此,这也需要处理。

我尝试使用字符串函数,但是它只给我提供第一个定界符的值并进一步跳过。

SELECT
    Substring(string, 1, Charindex('^', string) - 1) as Name,
    Substring(string, 4, Charindex('^', n) + 3) as Name1
FROM
    Sourcetable

我期望以下5个不同列的输出

FreeSpace | Total    | Drive | Server | Date
----------+----------+-------+--------+--------------------------
 57       |   200    | H:\   |  Test  | 2018-09-19 08:20:01.000
  8       |    88    | T:\   |  Test1 | 2018-09-1 08:00:01.000
 33       |    40    | D:\   |  Test2 | 2018-10-1 08:10:01.000

您应该将当前的单列表转储到文本文件,然后使用SQL Server的导入向导重新导入。 您要使用的定界符为:

\s*^\s*

如果向导不接受该分隔符,则可能必须预处理文件。 您可以对\\s*^\\s*进行正则表达式替换,并仅用逗号替换。 然后,使用逗号作为分隔符,通过向导导入到SQL Server。

怎么样

WITH CTE AS
(
SELECT *
FROM Strings S CROSS APPLY
    (
      SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) RN,
             Value
      FROM STRING_SPLIT(Str, '^')
    ) SP
)
SELECT ID,
       Str,
       MAX(CASE WHEN RN = 1 THEN Value END) FreeSpace,
       MAX(CASE WHEN RN = 2 THEN Value END) DriveLetter,
       MAX(CASE WHEN RN = 3 THEN Value END) Total,
       MAX(CASE WHEN RN = 4 THEN Value END) Server,
       MAX(CASE WHEN RN = 5 THEN Value END) [Date]
FROM CTE
GROUP BY ID,
         Str;

演示

我的输入

select * from StringVal

StringVal
57^H:\ ^ 200^Test ^2018-09-19 08:20:01.000
8^T:\ ^ 88^Test1 ^2018-09-1 08:00:01.000
33^D:\ ^ 40^Test2 ^2018-10-1 08:10:01.000

询问

;with cte 
as (
 select 
   CONVERT (varchar (255), StringVal) StringVal
 , convert (varchar (255), StringVal) want -- 'want' means wanted column.
 , ROW_NUMBER () over (partition by StringVal order by (select null)) id  
 -- Row id. Based on original value. for track/count the '^'.
 from StringVal
 union all
 select 
   CONVERT (varchar (255), StringVal) 
 , convert ( varchar (255)
  , stuff (
     want, CHARINDEX ('^', want),1
     ,choose (id, '</FreeSpace ><Drive>','</Drive><Total>', '</Total><Server>','</Server><Date>')
     -- Replace the '^' by XML tags based on column's order.
  )
 ) 
 , id + 1
 from cte
 where want like '%^%'
)
select 
  FreeSpace.value('.', 'varchar (255)') FreeSpace
, Total.value('.', 'varchar (255)') Total
, Drive.value('.', 'varchar (255)') Drive
, Server.value('.', 'varchar (255)') Server
, Date.value('.', 'varchar (255)') Date
from (
 select convert(xml, '<StringVal><FreeSpace>' + want + '</Date></StringVal>') StringVal 
 from cte where id = 5
) xml
cross apply -- I'm not good in XML. so I need lot xml.nodes.
xml.StringVal.nodes('/StringVal/FreeSpace') FreeSpace(FreeSpace)
cross apply 
xml.StringVal.nodes('/StringVal/Total') Total(Total)
cross apply 
xml.StringVal.nodes('/StringVal/Drive') Drive(Drive)
cross apply 
xml.StringVal.nodes('/StringVal/Server') Server(Server)
cross apply 
xml.StringVal.nodes('/StringVal/Date') Date(Date)

最终输出

FreeSpace   Total   Drive   Server  Date
8           88      T:\     Test1   2018-09-1 08:00:01.000
57          200     H:\     Test    2018-09-19 08:20:01.000
33          40      D:\     Test2   2018-10-1 08:10:01.000

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM