如何加速这个linq到sql函数?

时间:2008-12-23 08:49:17

标签: linq-to-sql optimization

我有一个函数(称为“powersearch”,具有讽刺意味!),它在一堆(~5)个字段中搜索一组字符串。 单词以一个字符串形式出现,并以空格分隔 某些字段可以具有完全匹配,其他字段应具有“包含”。

(简短地说)

//Start with all colors
IQueryable<Color> q = db.Colors;
//Filter by powersearch
if (!string.IsNullOrEmpty(searchBag.PowerSearchKeys)){
    foreach (string key in searchBag.SplitSearchKeys(searchBag.PowerSearchKeys)
                                    .Where(k=> !string.IsNullOrEmpty(k))){
        //Make a local copy of the var, otherwise it gets overwritten
        string myKey = key;
        int year;
        if (int.TryParse(myKey, out year) && year > 999){
            q = q.Where(c => c.Company.Name.Contains(myKey)
                || c.StockCode.Contains(myKey)                                
                || c.PaintCodes.Any(p => p.Code.Equals(myKey))
                || c.Names.Any(n => n.Label.Contains(myKey))
                || c.Company.CompanyModels.Any(m => m.Model.Name.Contains(myKey))
                || c.UseYears.Any(y => y.Year.Equals(year))
            );
        }
        else{
            q = q.Where(c => c.Company.Name.Contains(myKey)
                || c.StockCode.Contains(myKey)
                || c.PaintCodes.Any(p => p.Code.Contains(myKey))
                || c.Names.Any(n => n.Label.Contains(myKey))                                
                || c.Company.CompanyModels.Any(m => m.Model.Name.Equals(myKey))
            );
        }
    }
}

因为useYear计数相当大,所以我试图通过输出所有在这种情况下永远不会成为数字的数字来尽可能少地检查它。其他领域也不可能进行类似的检查,因为它们几乎可以包含任何可以想到的字符串。

目前,对于单个非年份字符串,此查询大约需要15秒。这太多了。 我能做些什么来改善这个?

- 编辑 -
Profiler向我显示了字符串不是一年的部分的以下信息:

exec sp_reset_connection 审核登录

exec sp_executesql N'
SELECT COUNT(*) AS [value]
FROM [dbo].[CLR] AS [t0]
INNER JOIN [dbo].[CO] AS [t1] ON [t1].[CO_ID] = [t0].[CO_ID]
WHERE 
    ([t1].[LONG_NM] LIKE @p0)
    OR ([t0].[EUR_STK_CD] LIKE @p1)
    OR (EXISTS(
        SELECT NULL AS [EMPTY]
        FROM [dbo].[PAINT_CD] AS [t2]
        WHERE ([t2].[PAINT_CD] LIKE @p2)
            AND ([t2].[CLR_ID] = [t0].[CLR_ID])
            AND ([t2].[CUSTOM_ID] = [t0].[CUSTOM_ID])
        )
    )OR (EXISTS(
        SELECT NULL AS [EMPTY]
        FROM [dbo].[CLR_NM] AS [t3]
        WHERE ([t3].[CLR_NM] LIKE @p3)
            AND ([t3].[CLR_ID] = [t0].[CLR_ID])
            AND ([t3].[CUSTOM_ID] = [t0].[CUSTOM_ID])
        )
    ) OR (EXISTS(
        SELECT NULL AS [EMPTY]
        FROM [dbo].[CO_MODL] AS [t4]
        INNER JOIN [dbo].[MODL] AS [t5] ON [t5].[MODL_ID] = [t4].[MODL_ID]
        WHERE ([t5].[MODL_NM] = @p4)
            AND ([t4].[CO_ID] = [t1].[CO_ID])
        )
    )
',N'@p0 varchar(10),@p1 varchar(10),@p2 varchar(10),@p3 varchar(10),@p4 varchar(8)',@p0='%mercedes%',@p1='%mercedes%',@p2='%mercedes%',@p3='%mercedes%',@p4='mercedes'

(花了3626毫秒) 审核注销(3673 msecs) exec sp_reset_connection(0msecs) 审核登录

exec sp_executesql N'
SELECT TOP (30) 
[t0].[CLR_ID] AS [Id],
[t0].[CUSTOM_ID] AS [CustomId],
[t0].[CO_ID] AS [CompanyId], 
[t0].[EUR_STK_CD] AS [StockCode], 
[t0].[SPCL_USE_CD] AS [UseCode], 
[t0].[EFF_IND] AS [EffectIndicator]
FROM [dbo].[CLR] AS [t0]
INNER JOIN [dbo].[CO] AS [t1] ON [t1].[CO_ID] = [t0].[CO_ID]
WHERE 
    ([t1].[LONG_NM] LIKE @p0)
    OR ([t0].[EUR_STK_CD] LIKE @p1)
    OR (EXISTS(
        SELECT NULL AS [EMPTY]
        FROM [dbo].[PAINT_CD] AS [t2]
        WHERE ([t2].[PAINT_CD] LIKE @p2)
            AND ([t2].[CLR_ID] = [t0].[CLR_ID])
            AND ([t2].[CUSTOM_ID] = [t0].[CUSTOM_ID])
        )
    )
    OR (EXISTS(
        SELECT NULL AS [EMPTY]
        FROM [dbo].[CLR_NM] AS [t3]
        WHERE ([t3].[CLR_NM] LIKE @p3)
            AND ([t3].[CLR_ID] = [t0].[CLR_ID])
            AND ([t3].[CUSTOM_ID] = [t0].[CUSTOM_ID])
        )
    )
    OR (EXISTS(
        SELECT NULL AS [EMPTY]
        FROM [dbo].[CO_MODL] AS [t4]
        INNER JOIN [dbo].[MODL] AS [t5] ON [t5].[MODL_ID] = [t4].[MODL_ID]
        WHERE ([t5].[MODL_NM] = @p4)
            AND ([t4].[CO_ID] = [t1].[CO_ID])
        )
    )'
,N'@p0 varchar(10),@p1 varchar(10),@p2 varchar(10),@p3 varchar(10),@p4 varchar(8)',@p0='%mercedes%',@p1='%mercedes%',@p2='%mercedes%',@p3='%mercedes%',@p4='mercedes'

(花了3368毫秒)

遗憾的是,数据库结构不在我的控制之下。它来自美国,出于兼容性原因必须保持完全相同的格式。虽然大多数重要字段确实已编入索引,但它们会在(不必要的)聚簇主键中编入索引。我无能为力。

2 个答案:

答案 0 :(得分:1)

好吧,让我们打破这个 - 你首先感兴趣的测试用例是一年非,所以我们得到的就是:

q = q.Where(c => c.Company.Name.Contains(myKey)
            || c.StockCode.Contains(myKey)
            || c.PaintCodes.Any(p => p.Code.Contains(myKey))
            || c.Names.Any(n => n.Label.Contains(myKey))
            || c.Company.CompanyModels.Any(m => m.Model.Name.Equals(myKey))

我是对的吗?如果是这样,SQL看起来像什么? 只是在SQL事件探查器中执行SQL语句需要多长时间?剖析器说执行计划是什么样的?您是否在所有相应的列上都有索引?

答案 1 :(得分:1)

使用已编译的查询。

如果不这样做,您将失去高达5-10倍的性能,因为LINQ-to-SQL必须在每次调用它时从查询生成SQL。

当你在LINQ-to-SQL中使用非常量时,事情会变得更糟,因为它们的值非常慢。

这假设您已经拥有索引和理智的数据库架构。

顺便说一下,我开玩笑的时候不是5-10倍。