简体   繁体   English

逗号分隔值(CSV)参数过滤

[英]Comma-separated values (CSV) parameter filtering

Need help on how to improve my SQL script for better performance. 需要有关如何改进我的SQL脚本以获得更好性能的帮助。 dbo.Products table has a million rows. dbo.Products表具有一百万行。 I'm hesitant to rewrite it using dynamic SQL. 我不愿意使用动态SQL重写它。 Thanks! 谢谢!

DECLARE
    @Brand varchar(MAX) = 'Brand 1, Brand 2, Brand 3',
    @ItemCategory varchar(MAX) = 'IC1, IC2, IC3, IC4, IC5'

--will return all records if params where set to @Brand = NULL, @ItemCategory = NULL

SELECT
     [Brand],
     SUM([Amount]) AS [Amount] 
FROM dbo.Products (NOLOCK)
LEFT JOIN [dbo].[Split](@Brand, ',') FilterBrand ON Brand = [FilterBrand].[Items]
LEFT JOIN [dbo].[Split](@ItemCategory, ',') FilterItemCategory ON ItemCategory = [FilterItemCategory].[Items] 
WHERE
    (@Brand IS NULL OR (@Brand IS NOT NULL AND [FilterBrand].[Items]  IS NOT NULL)) AND
    (@ItemCategory IS NULL OR (@ItemCategory IS NOT NULL AND [FilterItemCategory].[Items] IS NOT NULL))
GROUP BY
     [Brand]

Below is the split table-valued function that I found on the web: 以下是我在网上找到的拆分表值函数:

CREATE function [dbo].[Split]
(
    @String     varchar(8000),
    @Delimiter  char(1)
)
RETURNS @Results TABLE (Items varchar(4000))
AS
BEGIN
    IF (@String IS NULL OR @String = '') RETURN

    DECLARE @i int, @j int

    SELECT @i = 1

    WHILE @i <= LEN(@String)
        BEGIN
            SELECT  @j = CHARINDEX(@Delimiter, @String, @i)

            IF @j = 0
                BEGIN
                    SELECT  @j = len(@String) + 1
                END

            INSERT  @Results SELECT RTRIM(SUBSTRING(@String, @i, @j - @i))

            SELECT  @i = @j + LEN(@Delimiter)
        END

    RETURN
END

Following solution are with out using functions 以下解决方案没有使用功能

Declare @IDs Varchar(100)
SET @IDs = '2,4,6'

Select IsNull(STUFF((Select ', '+ CAST([Name] As Varchar(100)) From [TableName]
Where CharIndex(','+Convert(Varchar,[ID])+',', ','+@IDs+',')> 0
For XML Path('')),1,1,''),'') As [ColumnName]

Here is the function I use. 这是我使用的功能。 I also have another that wraps this to return numeric values which I find helpful as well. 我还有另一个将其包装起来以返回数值的值,这对我也很有帮助。

Edit: Sorry, as for how to improve the performance of the query, I usually split the values into table variables and perform my joins to that but that probably won't change your performance, just your readability. 编辑:对不起,关于如何提高查询性能,我通常将值拆分为表变量并执行联接,但这可能不会改变您的性能,只会改变您的可读性。 The only thing I can see in terms of performance is your double checking whether your joins produce anything. 就性能而言,我唯一能看到的就是您仔细检查联接是否产生任何结果。 You really can't get much better performance with two conditional left joins on two tables. 通过在两个表上进行两个条件左联接,您确实无法获得更好的性能。 It basically boils down to indexes at that point. 在这一点上,它基本上可以归结为索引。

(@Brand IS NULL OR [FilterBrand].[Items] IS NOT NULL)

Function: 功能:

ALTER FUNCTION [dbo].[fn_SplitDelimittedList]
(
    @DelimittedList varchar(8000),
    @Delimitter varchar(20)
)
RETURNS 
@List TABLE 
(
    Item varchar(100)
)
AS
BEGIN
    DECLARE @DelimitterLength INT
    SET @DelimitterLength = LEN(@Delimitter)

    -- Tack on another delimitter so we get the last item properly
    set @DelimittedList = @DelimittedList + @Delimitter

    declare @Position int
    declare @Item varchar(500)

    set @Position = patindex('%' + @Delimitter + '%' , @DelimittedList)
    while (@Position <> 0)
    begin
        set @Position = @Position - 1
        set @Item = LTRIM(RTRIM(left(@DelimittedList, @Position)))

        INSERT INTO @List (Item) VALUES (@Item)

        set @DelimittedList = stuff(@DelimittedList, 1, @Position + @DelimitterLength, '')
        set @Position = patindex('%' + @Delimitter + '%' , @DelimittedList)
    end

    RETURN
END

Hey just try the split function I have created without using any while loops here.And just use this in place of your split function and use col to match in LEFT join. 嘿,尝试使用我创建的split函数,而不在这里使用任何while循环,然后使用它代替您的split函数,并使用col在LEFT join中进行匹配。

ALTER function dbo.SplitString(@inputStr varchar(1000),@del varchar(5))
RETURNS @table TABLE(col varchar(100))
As
BEGIN

DECLARE @t table(col1 varchar(100))
INSERT INTO @t
select @inputStr

if CHARINDEX(@del,@inputStr,1) > 0
BEGIN
;WITH CTE as(select ROW_NUMBER() over (order by (select 0)) as id,* from @t)
,CTE1 as (
select id,ltrim(rtrim(LEFT(col1,CHARINDEX(@del,col1,1)-1))) as col,RIGHT(col1,LEN(col1)-CHARINDEX(@del,col1,1)) as rem from CTE
union all
select c.id,ltrim(rtrim(LEFT(rem,CHARINDEX(@del,rem,1)-1))) as col,RIGHT(rem,LEN(rem)-CHARINDEX(@del,rem,1))
from CTE1 c
where CHARINDEX(@del,rem,1)>0
)

INSERT INTO @table 
select col from CTE1
union all
select rem from CTE1 where CHARINDEX(@del,rem,1)=0
END
ELSE
BEGIN
INSERT INTO @table 
select col1 from @t
END


RETURN

END


DECLARE @Brand varchar(MAX) = 'Brand 1,Brand 2,Brand 3',
        @ItemCategory varchar(MAX) = ' IC1 A ,IC2 B , IC3 C, IC4 D' --'IC1, IC2, IC3, IC4, IC5'

select * from dbo.SplitString(@ItemCategory,',')

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

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