【问题标题】:Is it possible to compare comma delimited string in T-SQL without looping?是否可以在不循环的情况下比较 T-SQL 中的逗号分隔字符串?
【发布时间】:2018-03-20 12:25:39
【问题描述】:

假设我有 2 个表,其中两个表都有名为 Brand 的列。该值以逗号分隔,例如,如果其中一个表具有

ACER,ASUS,HP  
AMD,NVIDIA,SONY

作为价值。然后另一个表有

HP,GIGABYTE  
MICROSOFT  
SAMSUNG,PHILIPS

作为价值观。

我想比较这些表以获取所有匹配的记录,在我的示例中 ACER,ASUS,HPHP,GIGABYTE 匹配,因为两者都有 HP。现在我正在使用循环来实现这一点,我想知道是否可以在单个查询语法中做到这一点。

【问题讨论】:

  • 没有优雅或有效的方法来做到这一点,因为它根本不是 RDBMS 的设计方式。这些值中的每一个都应该在表中的自己的行中单独存在 - 然后所有这些都变得非常容易查询。如果可能的话,你应该相应地重新设计。
  • 它可以在没有循环和单个查询的情况下实现,但是您需要一个字符串拆分功能才能在两端应用并随后加入。如果您使用的是 SQL Server 2016+,则可以使用 STRING_SPLIT 函数。
  • 互联网上有很多不使用 while 循环的碎片。有些使用 Tally Tables,有些使用 XML,并且您也有 CLR 函数。此外,正如@EzequielLópezPetrucci 刚才所说(并且打败了我),SQL Server 2016 有一个内置的STRING_SPLIT 函数。
  • 好吧,正确的解决方案是规范化您的数据库。阅读Is storing a delimited list in a database column really that bad?,您会看到很多理由说明为什么这个问题的答案是绝对是的!如果这是不可能的,请使用字符串拆分功能。
  • @SalmanA:不,我使用的是 2012。而且我不能简单地更改设计并删除整个逗号分隔的列。

标签: sql sql-server tsql split


【解决方案1】:

你想摆脱循环是正确的。

自从您进入 2012 年以来,String_Split() 就不再适用了。但是,有任意数量的拆分/解析 TVF 函数。

示例 1 - 没有 TVF

Declare @T1 table (Brand varchar(50))
Insert Into @T1 values 
('ACER,ASUS,HP'),
('AMD,NVIDIA,SONY')

Declare @T2 table (Brand varchar(50))
Insert Into @T2 values 
('HP,GIGABYTE'),
('MICROSOFT'),
('SAMSUNG,PHILIPS')


Select Distinct
       T1_Brand = A.Brand
      ,T2_Brand = B.Brand
 From ( 
        Select Brand,B.*
         From  @T1
         Cross Apply (
                        Select RetVal = LTrim(RTrim(B.i.value('(./text())[1]', 'varchar(max)')))
                        From  (Select x = Cast('<x>' + replace(Brand,',','</x><x>')+'</x>' as xml)) as A 
                        Cross Apply x.nodes('x') AS B(i)
                     ) B
      ) A
 Join ( 
        Select Brand,B.*
         From  @T2
         Cross Apply (
                        Select RetVal = LTrim(RTrim(B.i.value('(./text())[1]', 'varchar(max)')))
                        From  (Select x = Cast('<x>' + replace(Brand,',','</x><x>')+'</x>' as xml)) as A 
                        Cross Apply x.nodes('x') AS B(i)
                     ) B
      ) B
 on A.RetVal=B.RetVal

示例 2 - 使用 TVF

Select Distinct
       T1_Brand = A.Brand
      ,T2_Brand = B.Brand
 From ( 
        Select Brand,B.*
         From  @T1
         Cross Apply [dbo].[tvf-Str-Parse](Brand,',') B
      ) A
 Join ( 
        Select Brand,B.*
         From  @T2
         Cross Apply [dbo].[tvf-Str-Parse](Brand,',') B
      ) B
 on A.RetVal=B.RetVal

两者都会回归

T1_Brand        T2_Brand
ACER,ASUS,HP    HP,GIGABYTE

UDF(如果有兴趣)

CREATE FUNCTION [dbo].[tvf-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].[tvf-Str-Parse]('Dog,Cat,House,Car',',')
--Select * from [dbo].[tvf-Str-Parse]('John Cappelletti was here',' ')
--Select * from [dbo].[tvf-Str-Parse]('this,is,<test>,for,< & >',',')

【讨论】:

    【解决方案2】:

    比较“,”分隔字符串时遇到同样的问题

    您可以使用“XML”来执行此操作并比较输出并返回相同/不同的值:

    declare  @TestInput nvarchar(255)
    , @TestInput2 nvarchar(255)
    
    set @TestInput = 'ACER,ASUS,HP'
    set @TestInput2 = 'HP,GIGABYTE'
    
    
    
    ;WITH FirstStringSplit(S1) AS
    (
     SELECT CAST('<x>' + REPLACE(@TestInput,',','</x><x>') + '</x>' AS XML)
    )
    ,SecondStringSplit(S2) AS
    (
    SELECT CAST('<x>' + REPLACE(@TestInput2,',','</x><x>') + '</x>' AS XML)
     )
    
     SELECT STUFF(
     (
    SELECT ',' + part1.value('.','nvarchar(max)')
    FROM FirstStringSplit
    CROSS APPLY S1.nodes('/x') AS A(part1)
    WHERE part1.value('.','nvarchar(max)') IN(SELECT B.part2.value('.','nvarchar(max)')
                                                  FROM SecondStringSplit 
                                                  CROSS APPLY S2.nodes('/x') AS B(part2)
                                                  ) 
    FOR XML PATH('')
    ),1,1,'') as [Same Value]
    

    编辑:

    将“东西”更改为“XML”

    【讨论】:

    • FOR XML PATH('') 在这个查询中做了什么?它有效地获取SELECT 子句定义的结果集中的每一行结果,并将它们连接在一起。 STUFF(...,1,1,'') 是做什么的?它会从该结果中删除前导逗号。调用此方法STUFF 是完全归功于错误的工具。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-16
    • 2021-06-29
    • 1970-01-01
    • 2014-05-15
    • 1970-01-01
    相关资源
    最近更新 更多