2012-08-09 113 views
0

需要有關如何改進我的SQL腳本以獲得更好性能的幫助。 dbo.Products表有百萬行。我很猶豫要用動態SQL重寫它。謝謝!逗號分隔值(CSV)參數過濾

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 
+0

幾件事情:(1)你真** ** **需要ALL **從'Products'列?如果不是,請不要使用SELECT *,而是明確指定列的列表。這*可能會爲性能調整打開一個機會; (2)確保'JOIN'中涉及的所有列都被正確編入索引; – 2012-08-09 16:30:43

+1

我最近記錄了一些更有效的方法來分割字符串:http://www.sqlperformance.com/2012/07/t-sql-queries/split-strings&http://www.sqlperformance.com/2012/ 08/t-sql-queries/splitting-strings-follow-up – 2012-08-09 16:45:38

+0

@marc_s我修改了我的示例腳本。我在現實生活中不使用「SELECT *」...謝謝。 – peng 2012-08-09 16:56:42

回答

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])+',', ','[email protected]+',')> 0 
For XML Path('')),1,1,''),'') As [ColumnName] 
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 
0

嘿只是嘗試我都沒有使用任何while循環here.And只是用這個來代替你的分割功能,並使用關口,以匹配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,',') 
+0

我試過你的分裂功能,它比我使用的更快。問題是,如果我有這個PARAM值: DECLARE @ItemCategory VARCHAR(MAX)= 'IC1 A,IC2 B,IC3 C,IC4 d' SELECT * FROM dbo.SplitString(@ItemCategory, '') OUTPUT: IC1一個 IC3ç IC2乙 IC2 B,IC3 C,IC4 d – peng 2012-08-09 23:00:43

+0

嘿檢查最新更新function.I是做小的失誤在前面,而返回output.So這將很好地工作。 – AnandPhadke 2012-08-10 03:58:07

+0

謝謝,現在好了。 – peng 2012-08-10 11:56:12