0

需要有关如何改进我的 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
4

3 回答 3

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]
于 2012-11-27T17:23:53.033 回答
0

嘿,只需尝试我创建的 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,',')
于 2012-08-09T17:18:08.210 回答
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
于 2012-08-09T16:22:41.840 回答