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

时间:2012-08-09 16:18:35

标签: sql sql-server performance csv

需要有关如何改进SQL脚本以获得更好性能的帮助。 dbo.Products表有一百万行。我对使用动态SQL重写它犹豫不决。谢谢!

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]

下面是我在网上找到的拆分表值函数:

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

3 个答案:

答案 0 :(得分:3)

以下解决方案没有使用功能

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]

答案 1 :(得分:0)

这是我使用的功能。我还有另一个包装它来返回我觉得有用的数值。

编辑:抱歉,至于如何提高查询的性能,我通常将值拆分为表变量并执行我的连接,但这可能不会改变您的性能,只是您的可读性。在性能方面我唯一能看到的就是你仔细检查你的连接是否产生任何东西。在两个表上有两个条件左连接,你真的无法获得更好的性能。它基本上归结为索引。

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

功能:

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

答案 2 :(得分:0)

嘿,我只是尝试在不使用任何while循环的情况下创建的split函数。只需使用它代替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,',')