ВЫБЕРИТЕ * ОТ X ГДЕ id IN (...) с помощью Dapper ORM - PullRequest
190 голосов
/ 05 декабря 2011

Каков наилучший способ написать запрос с предложением IN, используя Dapper ORM, если список значений для предложения IN исходит из бизнес-логики?Например, допустим, у меня есть запрос:

SELECT * 
  FROM SomeTable 
 WHERE id IN (commaSeparatedListOfIDs)

commaSeparatedListOfIDs передается из бизнес-логики и может быть любого типа IEnumerable(of Integer).Как бы я построить запрос в этом случае?Нужно ли мне делать то, что я делал до сих пор, это в основном конкатенация строк или есть какая-то продвинутая техника отображения параметров, о которой я не знаю?

Ответы [ 8 ]

303 голосов
/ 05 декабря 2011

Dapper поддерживает это напрямую.Например ...

string sql = "SELECT * FROM SomeTable WHERE id IN @ids"
var results = conn.Query(sql, new { ids = new[] { 1, 2, 3, 4, 5 }});
54 голосов
/ 05 декабря 2011

Непосредственно с домашней страницы проекта GitHub :

Dapper позволит вам передать IEnumerable и автоматически параметризует ваш запрос.

connection.Query<int>(
    @"select * 
      from (select 1 as Id union all select 2 union all select 3) as X 
      where Id in @Ids", 
    new { Ids = new int[] { 1, 2, 3 });

Будет переведено на:

select * 
from (select 1 as Id union all select 2 union all select 3) as X 
where Id in (@Ids1, @Ids2, @Ids3)

// @Ids1 = 1 , @Ids2 = 2 , @Ids2 = 3
33 голосов
/ 31 марта 2017

Если ваше предложение IN слишком велико для MSSQL для обработки, вы можете довольно легко использовать TableValueParameter с Dapper.

  1. Создать свой тип TVP в MSSQL:

    CREATE TYPE [dbo].[MyTVP] AS TABLE([ProviderId] [int] NOT NULL)
    
  2. Создайте DataTable с теми же столбцами, что и TVP, и заполните его значениями

    var tvpTable = new DataTable();
    tvpTable.Columns.Add(new DataColumn("ProviderId", typeof(int)));
    // fill the data table however you wish
    
  3. Измените свой запрос DapperINNER JOIN для таблицы TVP:

    var query = @"SELECT * FROM Providers P
        INNER JOIN @tvp t ON p.ProviderId = t.ProviderId";
    
  4. Передать DataTable в вызове Dapper для запроса

    sqlConn.Query(query, new {tvp = tvpTable.AsTableValuedParameter("dbo.MyTVP")});
    

Thisтакже фантастически работает, когда вы хотите выполнить массовое обновление нескольких столбцов - просто создайте TVP и выполните UPDATE с внутренним соединением с TVP.

10 голосов
/ 26 января 2017

Вот, пожалуй, самый быстрый способ запросить большое количество строк с помощью Dapper, используя список идентификаторов. Я обещаю вам, что это быстрее, чем любой другой способ, которым вы можете придумать (за возможным исключением использования TVP, как указано в другом ответе, и которое я не проверял, но я подозреваю, что может быть медленнее, потому что вы все еще ) должны заполнить TVP). Это планет быстрее, чем Dapper с использованием синтаксиса IN и юниверсов быстрее строки за строкой. И это даже континенты быстрее, чем передать список из VALUES или UNION ALL SELECT элементов. Его можно легко расширить, используя ключ из нескольких столбцов, просто добавив дополнительные столбцы в DataTable, временную таблицу и условия соединения.

public IReadOnlyCollection<Item> GetItemsByItemIds(IEnumerable<int> items) {
   var itemList = new HashSet(items);
   if (itemList.Count == 0) { return Enumerable.Empty<Item>().ToList().AsReadOnly(); }

   var itemDataTable = new DataTable();
   itemDataTable.Columns.Add("ItemId", typeof(int));
   itemList.ForEach(itemid => itemDataTable.Rows.Add(itemid));

   using (SqlConnection conn = GetConnection()) // however you get a connection
   using (var transaction = conn.BeginTransaction()) {
      conn.Execute(
         "CREATE TABLE #Items (ItemId int NOT NULL PRIMARY KEY CLUSTERED);",
         transaction: transaction
      );

      new SqlBulkCopy(conn, SqlBulkCopyOptions.Default, transaction) {
         DestinationTableName = "#Items",
         BulkCopyTimeout = 3600 // ridiculously large
      }
         .WriteToServer(itemDataTable);
      var result = conn
         .Query<Item>(@"
            SELECT i.ItemId, i.ItemName
            FROM #Items x INNER JOIN dbo.Items i ON x.ItemId = i.ItemId
            DROP TABLE #Items;",
            transaction: transaction,
            commandTimeout: 3600
         )
         .ToList()
         .AsReadOnly();
      transaction.Rollback(); // Or commit if you like
      return result;
   }
}

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

6 голосов
/ 10 апреля 2018

необязательно для добавления () в предложении WHERE, как мы делаем в обычном SQL. Потому что Даппер делает это автоматически для нас. Вот это syntax: -

const string SQL = "SELECT IntegerColumn, StringColumn FROM SomeTable WHERE IntegerColumn IN @listOfIntegers";

var conditions = new { listOfIntegers };

var results = connection.Query(SQL, conditions);
6 голосов
/ 05 марта 2018

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

SELECT Name from [USER] WHERE [UserId] in (@ids)

У меня была эта причина ошибки синтаксиса SQL с использованием Dapper 1.50.2, исправлена ​​путем удаления скобок

SELECT Name from [USER] WHERE [UserId] in @ids
3 голосов
/ 15 июля 2015

В моем случае я использовал это:

var query = "select * from table where Id IN @Ids";
var result = conn.Query<MyEntity>(query, new { Ids = ids });

моя переменная "идентификаторы" во второй строке представляет собой IEnumerable из строк, я думаю, они также могут быть целыми числами.

2 голосов
/ 05 декабря 2011

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

В Интернете доступно множество функций разветвителя, вы легко найдете их для чего угодно, если вам нравится SQL.

Вы можете сделать ...

SELECT * FROM table WHERE id IN (SELECT id FROM split(@list_of_ids))

Или

SELECT * FROM table INNER JOIN (SELECT id FROM split(@list_of_ids)) AS list ON list.id = table.id

(или аналогичный)

...