2013-09-07 9 views
5

W EF, jeśli mam listę primatives (lista), „łączących”, który przeciwko stół jest proste:Dołącz do listy w pamięci efektywnie

var ids = int[]{1,4,6}; //some random values 
var rows = context.SomeTable.Where(r => ids.Contains(r.id)) 

ten staje się znacznie bardziej skomplikowany moment ty chcesz dołączyć na wielu kolumnach:

var keys = something.Select(s => new { s.Field1, s.Field2 }) 
var rows = context.SomeTable.Where(r => keys.Contains(r => new { s.Field1, s.Field2 })); // this won't work 

Znalazłem dwa sposoby, aby go dołączyć, ale nie jest wielki:

  1. świry w całej tabeli, a filtrowanie to bas inne dane. (To robi się powoli, jeśli stół jest naprawdę duże)
  2. Dla każdego klawisza, kwerendy tabeli (to dostaje powolny jeśli masz przyzwoitą liczbę wierszy do załadowania)

Czasami kompromis ja już udało się dokonać to zmodyfikowana # 1: ciągnięcie w podzbioru tabeli na podstawie dość unikalny klucz

var keys = something.Select(s => s.Field1) 
var rows = context.SomeTable.Where(r => keys.Contains(s.Field1)).ToList(); 
foreach (var sRow in something) 
{ 
    var joinResult = rows.Where(r => r.Field1 == sRow.Field1 && r.Field2 == sRow.Field2); 
    //do stuff 
} 

Ale nawet to może przyhamować zbyt wiele danych.

Wiem, że istnieją sposoby, aby przekonać parametry o wartości w tabeli do ADO.Net i sposoby, jak zbudować serię klauzul .Where(), które są razem. Czy ktoś ma jakieś magiczne kule?

+0

Aby uzyskać wydajność, użyj 'context.ExecuteQuery()' do wysłania dostosowanej instrukcji SQL. – Andomar

Odpowiedz

0

Możesz spróbować spłaszczyć klucze, a następnie użyć tego samego wzoru Contains. To prawdopodobnie nie wystąpi wielki na dużych zapytań, choć można użyć indeksów funkcyjnych do przechowywania spłaszczoną klucz w bazie danych ...

mam test tabeli z kolumnami K1 int, K2 int, Name varchar(50)

var l = new List<Tuple<int, int>>(); 
l.Add(new Tuple<int, int>(1, 1)); 
l.Add(new Tuple<int, int>(1, 2)); 
var s = l.Select(k => k.Item1.ToString() + "," + k.Item2.ToString()); 
var q = Tests.Where(t => s.Contains(t.K1.ToString() + "," + t.K2.ToString())); 
foreach (var y in q) { 
    Console.WriteLine(y.Name); 
} 

Przetestowałem to w LINQPad z LINQ do SQL

pierwsza próba, która nie działa:

myślę drogę do napisania go jako pojedynczego zapytania jest coś takiego

var keys = something.Select(s => new { s.Field1, s.Field2 }) 
var rows = context.SomeTable.Where(r => keys.Any(k => r.Field1 == k.Field1 && r.Field2 == k.Field2)); 

Niestety, nie mam EF na tym laptopie i nie mogę nawet sprawdzić, czy jest to poprawne pod względem składniowym.

mam też pojęcia, jak to jest wydajnych czy działa w ogóle ...

+0

Nie sądzę, że to zadziała, ponieważ 'keys' jest zbiorem obiektów (anonimowych w tym przykładzie), a nie typów pierwotnych (int, string, Guid, DateTime itd.). Najprawdopodobniej wyrzuci niesławny * "Nie można utworzyć stałej wartości typu xyz. Obsługiwane są tylko typy pierwotne ..." * wyjątek. – Slauma

+0

@Slauma Wystarczy uruchomić prostszy przykład w Linqpad i jestem prawie pewien, że masz rację. – Laurence

0

Zamiast .Contains(), jak o użyć sprzężenia wewnętrznego i „filtr” w ten sposób:

from s in context.SomeTable 
join k in keys on new {k.Field1, k.Field2} equals new {s.Field1, s.Field2} 

Może być literówka w powyższym, ale masz pomysł ...

+1

O ile nie mylę się, to generowałem "SELECT ... From SomeTable" bez klauzuli where. Co było okropne, gdy miałem 10 milionów wierszy w tym stole. –

0
var rows = 
from key in keys 
join thingy in context.SomeTable 
on 1 = 1 
where thingy.Field1 == key && thingy.Field2 == key 
select thingy 

powinien działać i generować wystarczającą SQL

+0

Nie próbowałem "na 1 = 1", ale przypuszczam, że załadowałoby to całą SomeTable w tym momencie –

+0

Generator zapytań bierze całe zapytanie na konto, łącznie z klauzulą ​​where, i przejdzie tylko do datastore, gdy musi (tj. gdy wynik jest wyliczany), co może być nawet późniejsze, po grupie innych wyrażeń zapytania. Powiedziałbym, daj mu wir, zobacz, gdzie to przynosi. Jeśli wynikowy kod SQL jest okropny, zawsze możesz się od niego odrzucić. – Martijn

0

dostałem dokładnie ten sam problem, a rozwiązania wymyśliłem to:

  • Naive: zrobić oddzielne kwerendy dla każdego lokalnego rekordu
  • Smarter: Utwórz 2 listy unikatowych wartości Filed1 i unikatowych wartości Fiels2, zapytanie za pomocą 2 zawiera wyrażenia, a następnie będziesz musiał podwoić wyniki filtra, ponieważ mogą one nie być dokładne.

wygląda następująco:

var unique1 = something.Select(x => x.Field1).Distinct().ToList(); 
var unique2 = something.Select(x => x.Field2).Distinct().ToList(); 
var priceData = rows.Where(x => unique1.Contains(x.Field1) && unique2.Contains(x.Field2)); 
  • Następny jeden jest moje własne rozwiązanie, które nazwałem BulkSelect, idea za nim jest tak:

    • Tworzenie tabeli temp przy użyciu bezpośredniego Polecenie SQL
    • Prześlij dane dla polecenia SELECT do tej tabeli temp.
    • Przechwytuj i modyfikuj SQL, który został wygenerowany przez EF.

Zrobiłem to dla PostgreSQL, ale to może być przeniesiony do MSSQL jest potrzebne. To ładnie opisane here i kod źródłowy to here

Powiązane problemy