2010-05-12 1 views
2

В настоящее время я просматриваю способы передачи списков целых чисел в SQL-запросе и пытаюсь решить, какие из них лучше всего в какой ситуации, каковы преимущества каждого из них, и каковы подводные камни, чего следует избегать :)Передача целых списков в sql-запросе, лучшие практики

Сейчас я знаю 3 способа, которыми мы в настоящее время используем в нашем приложении.

1) Таблица значных параметр: Создать новую таблицу оцененная параметр в SQL Server:

CREATE TYPE [dbo].[TVP_INT] AS TABLE(
    [ID] [int] NOT NULL 
) 

Затем выполнить запрос против него:

using (var conn = new SqlConnection(DataContext.GetDefaultConnectionString)) 
{ 
    var comm = conn.CreateCommand(); 
    comm.CommandType = CommandType.Text; 
    comm.CommandText = @" 
UPDATE DA 
    SET [tsLastImportAttempt] = CURRENT_TIMESTAMP 
FROM [Account] DA 
JOIN @values IDs ON DA.ID = IDs.ID"; 
    comm.Parameters.Add(new SqlParameter("values", downloadResults.Select(d => d.ID).ToDataTable()) { TypeName = "TVP_INT" }); 
    conn.Open(); 
    comm.ExecuteScalar(); 
} 

Основными недостатками этого метода заключается в том, что Linq не поддерживает табличные значения params (если вы создаете SP с параметром TVP, linq не сможет его запустить) :(

2) Преобразуйте список в двоичный файл и используйте его в Linq! Это немного лучше .. Создание SP, и вы можете запустить его в LINQ :)

Для этого СП будет иметь параметр IMAGE, и мы будем использовать определенную пользователем функцию (UDF), чтобы преобразовать это в таблицу. В настоящее время у нас есть реализации этой функции, написанные на C++ и в сборке, обе имеют почти такую ​​же производительность :) В принципе, каждое целое число представлено 4 байтами и передается в SP. В .NET есть метод расширения, который CONVERS IEnumerable в массив байт

Метод расширения: общественности статических байт [] ToBinary (это IEnumerable IntList) { возврата ToBinaryEnum (IntList) .ToArray(); }

private static IEnumerable<Byte> ToBinaryEnum(IEnumerable<Int32> intList) 
{ 
    IEnumerator<Int32> marker = intList.GetEnumerator(); 
    while (marker.MoveNext()) 
    { 
     Byte[] result = BitConverter.GetBytes(marker.Current); 
     Array.Reverse(result); 
     foreach (byte b in result) 
      yield return b; 
    } 
} 

СП:

CREATE PROCEDURE [Accounts-UpdateImportAttempts] 
    @values IMAGE 
AS 
BEGIN 

UPDATE DA 
    SET [tsLastImportAttempt] = CURRENT_TIMESTAMP 
FROM [Account] DA 
JOIN dbo.udfIntegerArray(@values, 4) IDs ON DA.ID = IDs.Value4 

END 

И мы можем использовать его, запустив SP непосредственно, или в любой Linq запрос нам нужно

using (var db = new DataContext()) 
{ 
    db.Accounts_UpdateImportAttempts(downloadResults.Select(d => d.ID).ToBinary()); 
    // or 
    var accounts = db.Accounts 
     .Where(a => db.udfIntegerArray(downloadResults.Select(d => d.ID).ToBinary(), 4) 
      .Select(i => i.Value4) 
      .Contains(a.ID)); 
} 

Этот метод имеет преимущество используя скомпилированные запросы в linq (который будет иметь такое же определение sql, и план запроса, а также будет кэшироваться) и может также использоваться в SP.

Оба эти методы теоретически неограничен, так что вы можете передать миллионы Интс в то время :)

3) Простой .Contains LINQ() Это более простой подход, и идеально подходит в простой сценарии. Но, конечно, это ограничивается этим.

using (var db = new DataContext()) 
{ 
    var accounts = db.Accounts 
     .Where(a => downloadResults.Select(d => d.ID).Contains(a.ID)); 
} 

Самым большим недостатком этого способа является то, что каждое целое число в переменной downloadResults будет передана как отдельный Int .. В этом случае запрос ограничен SQL (максимально допустимый параметров в запросе SQL, который это пара тысяч, если я правильно помню).


Так что я хотел бы спросить ..Как вы думаете, что из этого лучше, и какие другие методы и подходы я пропустил?

ответ

2

Вы можете использовать XML-тип данных в качестве параметра

DECLARE @input xml 
SET @input = '<Inputs><Input>1</Input><Input>2</Input></Inputs>' 

      SELECT 
       Inputs.Input.value('.','int') Input 
      FROM 
       @input.nodes('/Inputs/Input) as Inputs(Input) 
+0

Ах да .. Это правда .. Что о производительности? Использует синтаксический анализ xml, создавая большой успех в запросе, exp с большим количеством переданных значений.? –

+0

Когда я использовал это в прошлом. Первое, что я делаю с xml, - это вставить его в индексированную временную таблицу. Затем присоединитесь к этому в основных запросах. Обычно это очень хорошо. –

+0

Поскольку вы единственный, кто ответил здесь, я думаю, что помету ваш ответ как ответ: P Еще раз спасибо за xml tip! –