【问题标题】:SQL Split Tab Delimited ColumnSQL 拆分制表符分隔列
【发布时间】:2017-07-04 16:41:51
【问题描述】:

我在包含标题字段及其相关结果的数据库中有一个制表符分隔列。我需要查询的帮助,该查询将根据 IP 列在单独的行中返回该数据。以下是数据示例以及所需的结果。

以下查询产生此结果。

DECLARE @Tmp TABLE (ID int Identity(1,1), IP nvarchar(255),Results NVARCHAR(max));
INSERT into @Tmp select top 1 ip, Results from MyTable where results like 'Package%'
select * from @Tmp

这是想要的结果。

IP              Package      Installed Version          Required Version
10.48.8.28      nss          3.21.0-9.el7_2.x86_64      3.21.3-2.el7_3
10.48.8.28      nss-sysinit  3.21.0-9.el7_2.x86_64      3.21.3-2.el7_3
10.48.8.28      nss-tools    3.21.0-9.el7_2.x86_64      3.21.3-2.el7_3
10.48.8.28      nss-util     3.21.0-2.2.el7_2.x86_64    3.21.3-1.1.el7_3

这是从 SQL 结果窗口复制到剪贴板的数据。

ID  IP          Results
1   10.46.8.28  Package Installed Version   Required Version
                python  2.7.5-39.el7_2.x86_64   2.7.5-48.el7
                python-libs 2.7.5-39.el7_2.x86_64   2.7.5-48.el7

【问题讨论】:

  • 您可以将您的示例数据作为消耗品发布吗?不可能告诉你在那里发生了什么。是两列吗?色谱柱分离在哪里?为什么只有第一行有ip?
  • @SeanLange 我已经用一张照片更新了帖子,希望能更加清晰。

标签: sql-server tsql tabs delimiter csv


【解决方案1】:

如果您使用的是 SQL Server 2016,则可以使用 STRING_SPLIT()

根据 Aaron Bertrand 的说法,这是最快的方式。看到这个https://sqlperformance.com/2016/03/sql-server-2016/string-split

Declare @YourTable table (ID int,IP varchar(50),TabString varchar(max));
Insert Into @YourTable values
(1,'444.333.222.11','Package    Installed Version   Required Version
nss 3.21.0-9.el7_2.x86_64   3.21.3-2.el7_3
nss-sysinit 3.21.0-9.el7_2.x86_64   3.21.3-2.el7_3
nss-tools   3.21.0-9.el7_2.x86_64   3.21.3-2.el7_3
nss-util    3.21.0-2.2.el7_2.x86_64 3.21.3-1.1.el7_3');


;WITH CTE_Lines AS
(
select ID, IP, a.ROWID , a.Lines from @YourTable
    CROSS APPLY (
        SELECT 
           ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS ROWID, value Lines
         FROM STRING_SPLIT(TabString, CHAR(13)) --Splits by new lines
    ) a
), CTE_Columns AS
(
SELECT ID, IP, ROWID, b.ColsID, b.[Cols] FROM CTE_Lines
    CROSS APPLY (
    SELECT 
        ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS ColsID,
        value [Cols] 
        FROM STRING_SPLIT(Lines, CHAR(9)) --Splits by tabs
    ) b 
    WHERE ROWID <> 1 --Removes the headers
)

SELECT ID, IP,  [1] Package, [2] [Installed Version],[3] [Required Version]  FROM 
(
    SELECT ID, IP, ROWID, ColsID, [Cols]  FROM CTE_Columns 
) a
PIVOT (MAX(a.Cols) FOR ColsId in ([1],[2],[3])) as pvt

【讨论】:

  • 你的例子对我来说不太适用,但对我来说是有用的指针。
【解决方案2】:

创建示例数据

Declare @YourTable table (ID int,IP varchar(50),TabString varchar(max))
Insert Into @YourTable values
(1,'444.333.222.11','Package Installed Version  Required Version
nss 3.21.0-9.el7_2.x86_64   3.21.3-2.el7_3
nss-sysinit 3.21.0-9.el7_2.x86_64   3.21.3-2.el7_3
nss-tools   3.21.0-9.el7_2.x86_64   3.21.3-2.el7_3
nss-util    3.21.0-2.2.el7_2.x86_64 3.21.3-1.1.el7_3')

看起来像这样

选项 1(带解析函数)

Select A.ID
      ,A.IP
      ,Package             = Pos1
      ,[Installed Version] = Pos2
      ,[Required Version]  = Pos3
 From  @YourTable A
 Cross Apply [dbo].[udf-Str-Parse](A.TabString,char(13)) B
 Cross Apply [dbo].[udf-Str-Parse-Row](B.RetVal,char(9)) C
 Where B.RetVal is not null and B.RetSeq>1

退货

选项 2(无解析功能)

Select A.ID
      ,A.IP
      ,Package             = Pos1
      ,[Installed Version] = Pos2
      ,[Required Version]  = Pos3
 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(A.TabString,char(13),'§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>' as xml).query('.')) as X
                Cross Apply x.nodes('x') AS B(i)
             ) B
 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)')))
                From  (Select Cast('<x>' + replace((Select replace(B.RetVal,char(9),'§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>' as xml) as xDim) as X 
             ) C
 Where B.RetVal is not null and B.RetSeq>1

退货

感兴趣的解析函数

CREATE FUNCTION [dbo].[udf-Str-Parse] (@String varchar(max),@Delimiter varchar(10))
Returns Table 
As
Return (  
    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(@String,@Delimiter,'§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>' as xml).query('.')) as A 
    Cross Apply x.nodes('x') AS B(i)
);
--Thanks Shnugo for making this XML safe
--Select * from [dbo].[udf-Str-Parse]('Dog,Cat,House,Car',',')
--Select * from [dbo].[udf-Str-Parse]('John Cappelletti was here',' ')
--Select * from [dbo].[udf-Str-Parse]('this,is,<test>,for,< & >',',')

CREATE FUNCTION [dbo].[udf-Str-Parse-Row] (@String varchar(max),@Delimiter varchar(10))
Returns Table 
As
Return (
    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)')))
          ,Pos8 = ltrim(rtrim(xDim.value('/x[8]','varchar(max)')))
          ,Pos9 = ltrim(rtrim(xDim.value('/x[9]','varchar(max)')))
    From  (Select Cast('<x>' + replace((Select replace(@String,@Delimiter,'§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>' as xml) as xDim) as A 
)
--Thanks Shnugo for making this XML safe
--Select * from [dbo].[udf-Str-Parse-Row]('Dog,Cat,House,Car',',')
--Select * from [dbo].[udf-Str-Parse-Row]('John <test> Cappelletti',' ')
--Select * from [dbo].[udf-Str-Parse-Row]('A&B;C;D;E, F;<x>',';')

【讨论】:

  • John 谢谢你,这个解决方案让我很了解,但基于数据库中的原始数据,它有点偏离。我附上了数据库产生的实际结果的图片。
  • @EddieD 轻松修复。给我一点时间。图片很有帮助
  • @EddieD 调整了 WHERE 以排除第一行,无论标题如何
  • @EddieD 很高兴它有帮助
猜你喜欢
  • 1970-01-01
  • 2017-08-11
  • 1970-01-01
  • 2013-02-27
  • 1970-01-01
  • 2018-06-18
  • 2018-03-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多