Как передать список int в инструкции Dapper Where IN? [Дубликат]

Вызов read() читает весь файл и оставляет курсор чтения в конце файла (ничего больше не читать). Если вы хотите прочитать определенное количество строк за раз, вы можете использовать readline(), readlines() или перебирать строки с помощью for line in handle:.

Чтобы ответить на ваш вопрос напрямую, был прочитан, с read() вы можете использовать seek(0), чтобы вернуть курсор чтения к началу файла (docs здесь ). Если вы знаете, что файл не будет слишком большим, вы также можете сохранить вывод read() в переменной, используя его в выражениях поиска.

Ps. Не забудьте закрыть файл после его завершения;)

159
задан Zsolt Botykai 5 December 2011 в 18:44
поделиться

8 ответов

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

string sql = "SELECT * FROM SomeTable WHERE id IN @ids"
var results = conn.Query(sql, new { ids = new[] { 1, 2, 3, 4, 5 }});
241
ответ дан matcheek 19 August 2018 в 04:48
поделиться
  • 1
    Я думаю, что важно отметить, что существует конечный предел того, сколько элементов вы можете отправить в свой массив. Я осознал это с трудом, когда прошел слишком много идов. Я не помню точное число, но из моей памяти я думаю, что это 200 элементов, прежде чем Dapper перестанет работать / выполнять запрос. – Marko 5 March 2013 в 22:43
  • 2
    Марко, это важно. И, если вы это делаете, вы можете подумать о поиске другого способа запроса ваших данных, таких как объединение или антисоединение, а не передача списка идентификаторов. Предложение IN не является самым высокопроизводительным запросом и часто может быть заменено предложением exist, которое будет быстрее. – Don Rolling 18 September 2013 в 16:25
  • 3
    FYI - SQL Server 2008 R2 имеет предел в 2100 записей в предложении IN. – Jesse 20 May 2014 в 19:09
  • 4
    И SQLite имеет предел по умолчанию для 999 переменных. – Cameron 26 August 2014 в 16:24
  • 5
    У кого-нибудь есть решение заменить это TVP, чтобы преодолеть предел, в sql server 2008? – cs0815 20 October 2014 в 16:39

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

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

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

SELECT Name from [USER] WHERE [UserId] in @ids
2
ответ дан Brian Ogden 19 August 2018 в 04:48
поделиться

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

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

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

3
ответ дан Cesar 19 August 2018 в 04:48
поделиться

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

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

var conditions = new { listOfIntegers };

var results = connection.Query(SQL, conditions);
3
ответ дан Coder Absolute 19 August 2018 в 04:48
поделиться

Возможно, это самый быстрый способ запросить большое количество строк с помощью Dapper, используя список идентификаторов. Я обещаю, что это быстрее, чем любой другой способ, о котором вы можете думать (за исключением, возможно, использования TVP, как указано в другом ответе, и который я не тестировал, но я подозреваю, что это может быть медленнее, потому что вы все еще должны заполнять TVP). Это планет быстрее, чем Dapper, используя синтаксис IN и юниверсы быстрее, чем Entity Framework по очереди. И это даже континенты быстрее, чем передача в список пунктов 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;
   }
}

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

10
ответ дан ErikE 19 August 2018 в 04:48
поделиться
  • 1
    Да, я согласен с вашей общей идеей создания таблицы temp с идентификаторами и последующим внутренним присоединением к этой таблице. Мы сделали это внутренне, и это значительно улучшило производительность запросов. Я не уверен, что я бы использовал класс DataTable для чего-либо, но ваше решение полностью допустимо. Это намного быстрее. – Marko 26 January 2017 в 20:16
  • 2
    Для объемной вставки требуется DataTable. Как вы вставляете в временную таблицу 50 000 значений? – ErikE 26 January 2017 в 21:23
  • 3
    В кусках 1000, если я правильно помню предел? Во всяком случае, я не знал, что вы можете обойти предел с помощью DataTable, поэтому я узнал что-то новое сегодня ... – Marko 27 January 2017 в 03:39
  • 4
    DataTable является только предпосылкой для объемной вставки. Массовые вставки являются особыми. Они выполняются в специальном режиме, который делает их намного быстрее, чем обычная вставка. – ErikE 27 January 2017 в 04:10
  • 5
    Это смехотворная работа, когда вы можете использовать параметр значения таблицы вместо этого. Dapper полностью поддерживает передачу DataTable в виде TVP, что позволяет обойтись без создания и уничтожения таблицы temp, а также заполнить эту временную таблицу через BulkCopy. Мы регулярно используем решение на основе TVP в случаях, когда количество параметров для предложения IN будет слишком большим. – Mr. T 28 March 2017 в 20:46
  • 6

Непосредственно с главной страницы проекта 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
47
ответ дан juharr 19 August 2018 в 04:48
поделиться

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

В Интернете есть много функций сплиттера, вы будете легко найти один из них, если ваш вкус 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

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

2
ответ дан MatBailie 19 August 2018 в 04:48
поделиться

Если ваше предложение 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. Измените свой запрос Dapper, чтобы сделать INNER 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")});
    

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

20
ответ дан Might 19 August 2018 в 04:48
поделиться
  • 1
    Это лучший способ, если у вас слишком много параметров. – 1.44mb 11 June 2017 в 09:24
  • 2
    Отличное решение, однако, не работает на .Net Core, см. Этот вопрос: stackoverflow.com/questions/41132350/… . Также см. Эту страницу: github.com/StackExchange/Dapper/issues/603 – pcdev 18 October 2017 в 06:51
  • 3
    Вы также можете подумать о том, чтобы сделать ProviderId на MyTVP равным PRIMARY KEY CLUSTERED, так как это просто решило проблему производительности для нас (значения, которые мы проходили, не содержали дубликатов). – Richardissimo 18 July 2018 в 10:33
Другие вопросы по тегам:

Похожие вопросы: