【问题标题】:Sql table comma separated values contain any of variable values checkingsql表逗号分隔值包含任何变量值检查
【发布时间】:2018-03-21 12:29:40
【问题描述】:

我有一个变量 @a='1,2,3,4' 和一个表,其中包含一个包含逗号分隔值的列 B

如何检查B 列的值是否包含任何@a 变量值?

【问题讨论】:

  • 永远不要将数据存储为逗号分隔的项目,这只会给您带来很多麻烦。
  • 添加一些样本数据和想要的结果。
  • 为表B提供样本数据

标签: sql sql-server csv tsql c#-4.0


【解决方案1】:

您需要实现一个函数来拆分值。有很多变化,你可以使用这个:

CREATE FUNCTION [dbo].[fn_Analysis_ConvertCsvListToNVarCharTableWithOrder](@List nvarchar(max), @Delimiter nvarchar(10) = ',')
RETURNS @result TABLE 
(   
    [Value] nvarchar(max),
    [SortOrder] bigint NOT NULL
)
AS
BEGIN
    IF @Delimiter is null 
    BEGIN
        SET @Delimiter = ','
    END
    DECLARE @XML xml = N'<r><![CDATA[' + REPLACE(@List, @Delimiter, ']]></r><r><![CDATA[') + ']]></r>'

    DECLARE @BufTable TABLE (Value nvarchar(max), SortOrder bigint NOT NULL IDENTITY(1, 1) PRIMARY KEY)
    INSERT INTO @BufTable (Value)
        SELECT Tbl.Col.value('.', 'nvarchar(max)')
        FROM   @xml.nodes('//r') Tbl(Col)
        OPTION (OPTIMIZE FOR (@xml = NULL)) 

    INSERT INTO @result (Value, SortOrder)
        SELECT Value, SortOrder
        FROM @BufTable

    RETURN
END

有这样的功能,很简单:

DECLARE @DataSource TABLE
(
    [column] VARCHAR(1024)
);

DECLARE @column VARCHAR(1024) = '1,2,3,4';

INSERT INTO @DataSource ([column])
VALUES ('100,200,300')
      ,('100,1,500')
      ,('1,2,3,500')
      ,('200')
      ,('33,32,31,4,30');

SELECT DISTINCT [column]
FROM @DataSource
CROSS APPLY [dbo].[fn_Analysis_ConvertCsvListToNVarCharTableWithOrder] ([column], ',') DSV
INNER JOIN [dbo].[fn_Analysis_ConvertCsvListToNVarCharTableWithOrder] (@column, ',') FV
    ON DSV.[Value] = FV.[Value];

使用CROSS APPLY,我们正在拆分每一列的值。然后我们拆分过滤值并执行INNER JOIN,以便仅匹配过滤值中包含的值的行。之后,我们需要一个DISTINCT,因为列值可能包含来自过滤器的许多值。

【讨论】:

    【解决方案2】:

    你需要一个 t-sql 字符串“splitter”,但我会使用上面推荐的 mTVF,因为它非常效率低下并且会破坏并行性。内联表值函数 (iTVF) 是拆分字符串所需的。

    我建议使用delimitedSplit8kdelimitedSplit8k_lead,它们的执行速度会快约30-90 倍;或 STRING_SPLIT 如果您使用的是 SQL 2016+ 并且只需要速度快几百倍的值。请注意此性能测试:

    -- sample data
    declare @rows int = 10000;
    if object_id('tempdb..#strings') is not null drop table #strings;
    select top (@rows) 
      someid     = identity(int,1,1),
      somestring = replace(right(left(cast(newid() as varchar(36)), 27),21),'-',',')
    into #strings
    from sys.all_columns a, sys.all_columns b;
    
    -- Performance test
    set nocount on;
    print 'fn_Analysis_ConvertCsvListToNVarCharTableWithOrder'+char(10)+replicate('-',50);
    go
    declare @st datetime = getdate(), @item varchar(10);
      select @item = [value]
      from #strings t
      cross apply dbo.fn_Analysis_ConvertCsvListToNVarCharTableWithOrder(t.somestring,',');
    print datediff(ms,@st,getdate());
    go 5
    
    print 'delimitedSplit8K (serial)'+char(10)+replicate('-',50);
    go
    declare @st datetime = getdate(), @item varchar(10);
      select @item = item
      from #strings t
      cross apply dbo.DelimitedSplit8K(t.somestring,',')
      option (maxdop 1);
    print datediff(ms,@st,getdate());
    go 5
    
    print 'delimitedSplit8K (parallel)'+char(10)+replicate('-',50);
    go
    declare @st datetime = getdate(), @item varchar(10);
      select @item = item
      from #strings t
      cross apply dbo.DelimitedSplit8K(t.somestring,',')
      option (recompile, querytraceon 8649);
    print datediff(ms,@st,getdate());
    go 5
    

    结果

    fn_Analysis_ConvertCsvListToNVarCharTableWithOrder
    --------------------------------------------------
    Beginning execution loop
    4183
    4274
    4536
    4294
    4406
    Batch execution completed 5 times.
    delimitedSplit8K (serial)
    --------------------------------------------------
    Beginning execution loop
    50
    50
    50
    54
    53
    Batch execution completed 5 times.
    delimitedSplit8K (parallel)
    --------------------------------------------------
    Beginning execution loop
    133
    134
    133
    140
    136
    Batch execution completed 5 times.
    

    如何解决您的问题

    declare @sometable table(someid int identity, someNbr tinyint);
    insert @sometable values (1),(3),(6),(12),(7),(15),(19);
    
    declare @searchstring varchar(1000) = '1,2,3,4,19';
    select someid, someNbr
    from @sometable t
    cross apply dbo.DelimitedSplit8K(@searchstring,',') s
    where t.someNbr = s.Item;
    

    结果

    someid      someNbr
    ----------- -------
    1           1
    2           3
    7           19
    

    【讨论】:

      猜你喜欢
      • 2022-06-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-08-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-05
      相关资源
      最近更新 更多