【问题标题】:t-sql find specific value with a csv stringt-sql 使用 csv 字符串查找特定值
【发布时间】:2012-08-29 23:33:38
【问题描述】:

我需要一些关于 SQL 查询的帮助。我有一列将值存储为逗号分隔值。

我需要编写一个查询来查找列中每个值中的第三个分隔项。

这可以在 Select 语句中执行吗? 例如:列值:josh,Reg01,False,a0-t0,22/09/2010

所以我需要从上面的字符串中获取第三个值(即)False

【问题讨论】:

  • 嗯,你有解决症状的答案,但真正的问题是有一个多值列,当你想使用它的一部分时。这种设计就像充气飞镖一样面向未来。
  • 我同意。数据由流行的 cms 存储,我正在尝试从特定列中提取信息
  • 感谢您提供的所有出色解决方案。我使用了 podiluska 的回答,因此我接受了 podiluska 的回答。

标签: sql tsql parsing csv


【解决方案1】:

试试这个(假设 SQL Server 2005+)

DECLARE @t TABLE(ColumnValue VARCHAR(50))
INSERT INTO @t(ColumnValue) SELECT 'josh,Reg01,False,a0-t0,22/09/2010'
INSERT INTO @t(ColumnValue) SELECT 'mango,apple,bannana,grapes'
INSERT INTO @t(ColumnValue) SELECT 'stackoverflow'

SELECT ThirdValue = splitdata
FROM(
        SELECT 
            Rn = ROW_NUMBER() OVER(PARTITION BY ColumnValue ORDER BY (SELECT 1))
            ,X.ColumnValue
            ,Y.splitdata 
        FROM
         (
            SELECT *,
            CAST('<X>'+REPLACE(F.ColumnValue,',','</X><X>')+'</X>' AS XML) AS xmlfilter FROM @t F
         )X
         CROSS APPLY
         ( 
            SELECT fdata.D.value('.','varchar(50)') AS splitdata 
            FROM X.xmlfilter.nodes('X') as fdata(D)
         ) Y
    )X WHERE X.Rn = 3

//结果

第三值

False
bannana

您的问题也不是很清楚您使用的是什么版本的 SQL Server。如果您使用的是 SQL SERVER 2000,则可以继续使用以下方法。

第 1 步:创建数字表

CREATE TABLE dbo.Numbers
(
   N INT NOT NULL PRIMARY KEY
);
GO

DECLARE @rows AS INT;
SET @rows = 1;

INSERT INTO dbo.Numbers VALUES(1);
WHILE(@rows <= 10000)
BEGIN
   INSERT INTO dbo.Numbers SELECT N + @rows FROM dbo.Numbers;
   SET @rows = @rows * 2;
END 

第 2 步:应用下面的查询

DECLARE @t TABLE(ColumnValue VARCHAR(50))
INSERT INTO @t(ColumnValue) SELECT 'josh,Reg01,False,a0-t0,22/09/2010'
INSERT INTO @t(ColumnValue) SELECT 'mango,apple,bannana,grapes'
INSERT INTO @t(ColumnValue) SELECT 'stackoverflow'

--Declare a table variable to put the identity column and store the indermediate results
DECLARE @tempT TABLE(Id INT IDENTITY,ColumnValue VARCHAR(50),SplitData VARCHAR(50))

-- Insert the records into the table variable
INSERT INTO @tempT
SELECT  
    ColumnValue
    ,SUBSTRING(ColumnValue, Numbers.N,CHARINDEX(',', ColumnValue + ',', Numbers.N) - Numbers.N) AS splitdata 
FROM @t 
JOIN Numbers ON Numbers.N <= DATALENGTH(ColumnValue) + 1  
AND SUBSTRING(',' + ColumnValue, Numbers.N, 1) = ','  

--Project the filtered records

SELECT ThirdValue = X.splitdata
FROM
--The co-related subquery does the ROW_NUMBER() OVER(PARTITION BY ColumnValue)
(SELECT 
  Rn = (SELECT COUNT(*) 
        FROM @tempT t2 
        WHERE t2.ColumnValue=t1.ColumnValue 
        AND t2.Id<=t1.Id)
 ,t1.ColumnValue
 ,t1.splitdata
FROM @tempT t1)X
WHERE X.Rn =3

-- 结果

第三值

False
bannana

您也可以将 Master..spt_Values 用于您的数字表

DECLARE @t TABLE(ColumnValue VARCHAR(50))
INSERT INTO @t(ColumnValue) SELECT 'josh,Reg01,False,a0-t0,22/09/2010'
INSERT INTO @t(ColumnValue) SELECT 'mango,apple,bannana,grapes'
INSERT INTO @t(ColumnValue) SELECT 'stackoverflow'

--Declare a table variable to put the identity column and store the indermediate results
DECLARE @tempT TABLE(Id INT IDENTITY,ColumnValue VARCHAR(50),SplitData VARCHAR(50))

-- Insert the records into the table variable
INSERT INTO @tempT
SELECT  
    ColumnValue
    ,SUBSTRING(ColumnValue, Number ,CHARINDEX(',', ColumnValue + ',', Number ) - Number) AS splitdata 
FROM @t 
JOIN master..spt_values ON Number <= DATALENGTH(ColumnValue) + 1  AND type='P'
AND SUBSTRING(',' + ColumnValue, Number , 1) = ','  

--Project the filtered records
SELECT ThirdValue = X.splitdata
FROM
--The co-related subquery does the ROW_NUMBER() OVER(PARTITION BY ColumnValue)
(SELECT 
  Rn = (SELECT COUNT(*) 
        FROM @tempT t2 
        WHERE t2.ColumnValue=t1.ColumnValue 
        AND t2.Id<=t1.Id)
 ,t1.ColumnValue
 ,t1.splitdata
FROM @tempT t1)X
WHERE X.Rn =3

您可以从

阅读有关此内容的信息

1) What is the purpose of system table table master..spt_values and what are the meanings of its values?

2)Why (and how) to split column using master..spt_values?

【讨论】:

    【解决方案2】:

    您可以使用此解决方案和其他解决方案进行一些测试,但是,我相信在这种情况下使用 XML 几乎总是可以为您提供最佳性能并确保更少的编码:

    DECLARE @InPutCSV NVARCHAR(2000)= 'josh,Reg01,False,a0-t0,22/09/2010'
    DECLARE @ValueIndexToGet INT=3
    DECLARE @XML XML =  CAST ('<d>' + REPLACE(@InPutCSV, ',', '</d><d>') + '</d>' AS XML);
    
    WITH CTE(RecordNumber,Value) AS
    (
         SELECT  ROW_NUMBER() OVER(ORDER BY T.v.value('.', 'NVARCHAR(100)') DESC) AS RecordNumber
                 ,T.v.value('.', 'NVARCHAR(100)') AS Value
         FROM @XML.nodes('/d') AS T(v)
    )
    SELECT Value
    FROM CTE WHERE RecordNumber=@ValueIndexToGet
    

    我可以确认从具有 100 000 个值的 CSV 字符串中获取值需要 1 秒。

    【讨论】:

      【解决方案3】:

      是的。

      @s 是你的字符串...

      select 
          SUBSTRING (@s,
          CHARINDEX(',',@s,CHARINDEX(',',@s)+1)+1,
          CHARINDEX(',',@s,CHARINDEX(',',@s,CHARINDEX(',',@s)+1)+1)
                -CHARINDEX(',',@s,CHARINDEX(',',@s)+1)-1)
      

      或者更笼统地说……

      ;with cte as 
      (
          select 1 as Item, 1 as Start, CHARINDEX(',',@s, 1) as Split
          union all
          select cte.Item+1, cte.Split+1, nullif(CHARINDEX(',',@s, cte.Split+1),0) as Split
          from cte
          where cte.Split<>0  
      )   
      select SUBSTRING(@s, start,isnull(split,len(@s)+1)-start) 
      from cte 
      where Item = 3
      

      现在正确存储您的数据:)

      【讨论】:

        【解决方案4】:

        您确实需要类似 String.Split(',')(2) 之类的东西,不幸的是 SQL 中不存在它,但 this 可能对您有帮助

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2019-01-15
          • 1970-01-01
          • 1970-01-01
          • 2015-02-15
          • 2013-07-15
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多