Linq скомпилированный запрос с использованием Contains (как оператор SQLs IN) - PullRequest
5 голосов
/ 05 июня 2009

Я нахожусь в процессе ускорения запросов Linq к SQL в веб-приложении путем преобразования их в скомпилированные запросы, и я столкнулся с проблемой преобразования очень простого оператора.

Я хочу составить простое утверждение, которое использует следующие параметры для получения действительных сотрудников из базы данных:

TestParams myParams = new TestParams
{
    ValidEmps = new int[] { 1, 2, 3 }
};

Вот рабочий запрос:

IQueryable<Employees> MySelectedEmps =
    from emps in db.Employees
    where myParams.ValidEmps.Contains(emps.EmployeeID)
    select emps;

Вот моя попытка его собрать:

private static Func<MyDataContext, TestParams, IQueryable<Employee>> myCompiledQuery =
    CompiledQuery.Compile((MyDataContext db, TestParams myParams) =>
        from emps in db.Employees
        where myParams.ValidEmps.Contains(emps.EmployeeID)
        select emps);

Это утверждение с компиляцией и сборкой, но когда я его запускаю, я получаю следующую ошибку времени выполнения:

Операторы сравнения не поддерживаются для введите 'System.Int32 []'

Я также попытался передать список и IEnumerable с тем же сообщением об ошибке.

Я также пытался заменить оператор .Contains оператором .Any (valemp => valemp == emps.EmployeeID), но все равно получаю ту же ошибку.

Возможно ли иметь скомпилированный запрос, который использует эквивалент оператора SQL "IN"? Что я делаю не так?

Ответы [ 3 ]

5 голосов
/ 05 июня 2009

вызов хранимой процедуры и передача списка значений CVS, используя этот метод:

Прежде чем использовать мою функцию, вам нужно настроить вспомогательную таблицу, вам нужно сделать это только один раз для базы данных:

CREATE TABLE Numbers
(Number int  NOT NULL,
    CONSTRAINT PK_Numbers PRIMARY KEY CLUSTERED (Number ASC)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]
DECLARE @x int
SET @x=0
WHILE @x<8000
BEGIN
    SET @x=@x+1
    INSERT INTO Numbers VALUES (@x)
END

используйте эту функцию для разделения вашей строки, которая не зацикливается и очень быстра:

CREATE FUNCTION [dbo].[FN_ListToTable]
(
     @SplitOn              char(1)              --REQUIRED, the character to split the @List string on
    ,@List                 varchar(8000)        --REQUIRED, the list to split apart
)
RETURNS
@ParsedList table
(
    ListValue varchar(500)
)
AS
BEGIN

/**
Takes the given @List string and splits it apart based on the given @SplitOn character.
A table is returned, one row per split item, with a column name "ListValue".
This function workes for fixed or variable lenght items.
Empty and null items will not be included in the results set.


Returns a table, one row per item in the list, with a column name "ListValue"

EXAMPLE:
----------
SELECT * FROM dbo.FN_ListToTable(',','1,12,123,1234,54321,6,A,*,|||,,,,B')

    returns:
        ListValue  
        -----------
        1
        12
        123
        1234
        54321
        6
        A
        *
        |||
        B

        (10 row(s) affected)

**/



----------------
--SINGLE QUERY-- --this will not return empty rows
----------------
INSERT INTO @ParsedList
        (ListValue)
    SELECT
        ListValue
        FROM (SELECT
                  LTRIM(RTRIM(SUBSTRING(List2, number+1, CHARINDEX(@SplitOn, List2, number+1)-number - 1))) AS ListValue
                  FROM (
                           SELECT @SplitOn + @List + @SplitOn AS List2
                       ) AS dt
                      INNER JOIN Numbers n ON n.Number < LEN(dt.List2)
                  WHERE SUBSTRING(List2, number, 1) = @SplitOn
             ) dt2
        WHERE ListValue IS NOT NULL AND ListValue!=''



RETURN

END --Function FN_ListToTable

Вы можете использовать эту функцию в качестве таблицы в соединении:

SELECT
    Col1, COl2, Col3...
    FROM  YourTable
        INNER JOIN FN_ListToTable(',',@YourString) s ON  YourTable.ID = s.ListValue

поэтому для Linq создайте хранимую процедуру:

СОЗДАНИЕ ПРОЦЕДУРЫ YourProcedure (

 @param1  int
,@param2  varchar(8000) --csv list is here

) а

SELECT
    Col1, COl2, Col3...
    FROM YourTable
        INNER JOIN FN_ListToTable(',',@param2  ) s ON  YourTable.ID = s.ListValue
    WHERE Col1=@param1

1020 * идти *

0 голосов
/ 11 августа 2009

Однажды я столкнулся с этой же проблемой, и я почти уверен (мне придется исследовать), что я смог решить эту проблему, создав класс с методом, который возвращает логическое значение, которое я затем передать элемент из строки данных и вернуть true / false.

0 голосов
/ 18 июня 2009

не

CompiledQuery.Compile 

функция должна быть

CompiledQuery.Compile<MyDataContext, TestParams, IQueryable<Employee>>

вместо

А вы пытались использовать расширение для Int32 (я полагаю, EmployeeID является Int32) с функцией In?

public static class Int32Extensions
{
    public static bool In(this int input, List<int> list)
    {
        if (list == null || list.Count == 0) return false;
        return list.Contains(input);
    }
}

private static Func<MyDataContext, TestParams, IQueryable<Employee>> myCompiledQuery =  
  CompiledQuery.Compile<MyDataContext, TestParams, IQueryable<Employee>>((MyDataContext db, TestParams myParams) =>
    from emps in db.Employees
    where emps.EmployeeID.In(myParams.ValidEmps)
    select emps);

Grtz,

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