* DAPper ORM ile X NEREDEN GİRİŞ YAPIN (…)


231

IN yan tümcesi için değer listesi iş mantığından geliyorsa Dapper ORM kullanarak IN yan tümcesi ile bir sorgu yazmak için en iyi yolu nedir? Diyelim ki bir sorum var:

SELECT * 
  FROM SomeTable 
 WHERE id IN (commaSeparatedListOfIDs)

commaSeparatedListOfIDsİş mantığı geçirilen ediliyor ve bunun herhangi bir tür olabilir IEnumerable(of Integer). Bu durumda bir sorguyu nasıl oluştururum? Ben şimdiye kadar temelde dize birleştirme olan ya da farkında değilim gelişmiş parametre eşleştirme tekniği bir tür var mı ne yapmak zorunda?

Yanıtlar:


366

Dapper bunu doğrudan destekler. Örneğin...

string sql = "SELECT * FROM SomeTable WHERE id IN @ids"
var results = conn.Query(sql, new { ids = new[] { 1, 2, 3, 4, 5 }});

47
Dizinizde kaç öğe gönderebileceğinize dair sınırlı bir sınır olduğunu belirtmek önemlidir. Çok fazla id'den geçtiğimde bunu zor yoldan fark ettim. Tam sayıyı hatırlamıyorum ama hafızamdan Dapper sorguyu çalıştırmayı / yürütmeyi durdurmadan önce 200 öğe olduğunu düşünüyorum.
Marko

8
Marko, bu önemli. Ve bu şekilde yapıyorsanız, verilerinizi sorgulamanın başka bir yolunu bulmayı düşünebilirsiniz; örneğin, bir kimlik listesini iletmek yerine birleştirme veya katılma önleme yapmak gibi. IN yan tümcesi en yüksek performanslı sorgu değildir ve genellikle daha hızlı olacak varolan bir yan tümce ile değiştirilebilir.
Don Rolling

24
FYI - SQL Server 2008 R2 INyan tümcesinde 2100 giriş sınırı vardır .
Jesse

6
Ve SQLite varsayılan 999 değişken sınırına sahiptir.
Cameron

8
Dikkat: dizinizde birden çok öğe varsa ve parametreyi parantez içine alırsanız SQL Server'da bu başarısız olur. Köşeli parantezleri kaldırmak sorunu çözecektir.
ajbeaven

66

Doğrudan GitHub proje ana sayfasından :

Dapper, IEnumerable'a geçmenize izin verir ve sorgunuzu otomatik olarak parametrelendirir.

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 });

Çevirilecek:

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

43

Senin Eğer INfıkra MSSQL işlemek için çok büyük, oldukça kolaylıkla Dapper ile TableValueParameter kullanabilirsiniz.

  1. TVP türünüzü MSSQL'de oluşturun:

    CREATE TYPE [dbo].[MyTVP] AS TABLE([ProviderId] [int] NOT NULL)
  2. DataTableTVP ile aynı sütun (lar) a sahip bir sütun oluşturun ve değerlerle doldurun

    var tvpTable = new DataTable();
    tvpTable.Columns.Add(new DataColumn("ProviderId", typeof(int)));
    // fill the data table however you wish
  3. INNER JOINTVP tablosunda bir Dapper sorgunuzu değiştirmek için değiştirin :

    var query = @"SELECT * FROM Providers P
        INNER JOIN @tvp t ON p.ProviderId = t.ProviderId";
  4. Dapper sorgu çağrınızda DataTable'ı iletin

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

Bu, birden çok sütunun toplu olarak güncellenmesini istediğinizde de fevkalade çalışır - sadece bir TVP oluşturun ve TVP'ye UPDATEdahili bir birleşim yapın.


Ancak harika bir çözüm .Net Core üzerinde çalışmaz, şu soruya bakın: stackoverflow.com/questions/41132350/… . Ayrıca bu sayfaya da bakınız: github.com/StackExchange/Dapper/issues/603
pcdev

3
Ayrıca yapım düşünebilirsiniz ProviderIdüzerinde MyTVPolması PRIMARY KEY CLUSTEREDbu sadece bizim için bir performans sorununu çözmüş olarak (biz hiçbir çiftleri içerdiği yoldan geçen edildi değerleri).
Richardissimo

@Richardissimo Bunun nasıl yapılacağına dair bir örnek gösterebilir misiniz? Sözdizimini doğru anlayamıyorum.
Mike Cole


14

Muhtemelen bir kimlik listesi kullanarak Dapper ile çok sayıda satırı sorgulamanın en hızlı yolu. Size bunun aklınıza gelebilecek diğer tüm yöntemlerden daha hızlı olduğuna söz veriyorum (başka bir cevapta verildiği gibi bir TVP'yi kullanmak dışında ve test etmediğimden şüpheleniyorum, ancak daha yavaş olabileceğinden şüpheleniyorum çünkü hala doldurmanız gerekiyor TVP). Öyle gezegenler daha hızlı Zarif kullanmaktan daha INsözdizimi ve evreni daha hızlı sıranın tarafından varlık Framework sıradan daha. Ve kıtalar bile bir liste VALUESveya UNION ALL SELECTöğelerden daha hızlıdır . Çok sütunlu bir anahtar kullanmak için kolayca genişletilebilir, sadece DataTablegeçici tabloya, geçici tabloya ve birleştirme koşullarına ekleyin .

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;
   }
}

Toplu Eklemeler hakkında biraz bilgi sahibi olmanız gerektiğini unutmayın. Ateşleme tetikleyicileri (varsayılan değer hayır), kısıtlamalara uyma, tabloyu kilitleme, eşzamanlı eklemelere izin verme vb. Seçenekleri vardır.


Evet, Ids ile geçici bir tablo oluşturma ve daha sonra bu tabloya içsel katılma genel fikrine katılıyorum. Bunu dahili olarak yaptık ve sorgu performansını önemli ölçüde geliştirdi. DataTable sınıfını herhangi bir şey için kullanacağımdan emin değilim ama çözümünüz tamamen geçerli. Bu çok daha hızlı bir yol.
Marko

DataTableToplu ekleme için gereklidir. Nasıl mı sen geçici tabloya 50,000 değerleri eklemek?
ErikE

1
1000'lik parçalarda limiti doğru hatırlarsam? Her neyse, DataTable ile sınırı atlayabileceğinizi bilmiyordum, bu yüzden bugün yeni bir şey öğrendim ...
Marko

1
Bunun yerine bir Tablo Değeri Parametresi kullanabileceğiniz zaman saçma bir iş. Dapper, bir DataTable'ı TVP olarak geçirmeyi açıkça destekler, bu da geçici bir tablonun oluşturulması ve imha edilmesinden vazgeçmenize ve aynı zamanda geçici tabloyu BulkCopy ile doldurmanıza olanak tanır. TVP tabanlı çözümü, IN deyimi için parametre sayısının çok fazla olacağı durumlarda rutin olarak kullanıyoruz.
Bay T

3
Bu çok saçma bir iş değildir, özellikle de bir yardımcı sınıf veya genişletme yöntemi ile biraz soyutlanırsa.
ErikE

11

Ayrıca, parantezleri sorgu dizenizin etrafına şu şekilde sarmadığınızdan emin olun:

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

Bu parantez kaldırarak sabit Dapper 1.50.2 kullanarak bir SQL sözdizimi hatasına neden oldu

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

7

Öyle gerekli değildir eklemek için ()biz düzenli SQL gibi WHERE yan tümcesinde. Çünkü Dapper bunu otomatik olarak bizim için yapıyor. İşte syntax: -

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

var conditions = new { listOfIntegers };

var results = connection.Query(SQL, conditions);

6

Postgres örneği:

string sql = "SELECT * FROM SomeTable WHERE id = ANY(@ids)"
var results = conn.Query(sql, new { ids = new[] { 1, 2, 3, 4, 5 }});

3

Benim durumumda bunu kullandım:

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

benim değişken "kimlikleri" ikinci satırda bir IEnumerable dizeleri, aynı zamanda sanırım tamsayılar olabilir.


List<string>?
Kiquenet

2

Benim tecrübelerime göre, bununla başa çıkmanın en kolay yolu, bir dizeyi değerler tablosuna dönüştüren bir fonksiyona sahip olmaktır.

Web üzerinde birçok splitter işlevi vardır, SQL lezzetiniz ne olursa olsun kolayca bir tane bulacaksınız.

Sonra yapabilirsin ...

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

Veya

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

(Veya benzeri)

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.