Как я могу ускорить эту функцию linq to sql? - PullRequest
1 голос
/ 23 декабря 2008

У меня есть функция (называемая «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 секунд для одной не годовой строки. Это слишком много. Что я могу сделать, чтобы улучшить это?

- Изменить -
Профилировщик показывает мне следующую информацию для части, где строка не год:

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 мсек) 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 ]

1 голос
/ 23 декабря 2008

Используйте скомпилированные запросы.

Если вы этого не сделаете, вы потеряете производительность в 5-10 раз, поскольку LINQ-to-SQL будет генерировать SQL из запроса каждый каждый раз, когда вы его вызываете.

Ситуация ухудшается, когда вы используете неконстантные значения в LINQ-to-SQL, поскольку получение их значений очень медленное.

Предполагается, что у вас уже есть индексы и правильная схема БД.

Кстати, я не шучу о 5-10х частях.

1 голос
/ 23 декабря 2008

Хорошо, давайте разберемся с этим - первый интересующий вас контрольный пример - это один год, поэтому у нас есть только следующее:

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 Profiler? Как, по словам профилировщика, выглядит план выполнения? У вас есть индексы по всем соответствующим столбцам?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...