2012-02-10 9 views
12

Potrzebuję zamówić artykuły przechowywane w bazie danych według malejącej daty publikacji, a następnie pierwsze 20 rekordów po artykule z Id == 100.Jak zaimplementować plik SkipWhile za pomocą Linq do Sql bez wcześniejszego załadowania całej listy do pamięci?

To, co chciałbym zrobić z Linq:

IQueryable<Article> articles = 
    db.Articles 
    .OrderByDescending(a => a.PublicationDate) 
    .SkipWhile(a => a.Id != 100) 
    .Take(20); 

Jednak ta generuje NotSupportedException ponieważ SkipWhile nie jest obsługiwana w LINQ do SQL (patrz here).

Możliwym rozwiązaniem jest wykonanie zapytania, a następnie zastosować SkipWhile użyciu LINQ do obiektu:

IEnumerable<ArticleDescriptor> articles = 
    db.Articles 
    .OrderByDescending(a => a.PublicationDate) 
    .ToList() 
    .SkipWhile(a => a.Article.Id != 100) 
    .Take(20); 

Ale to znaczy, że trzeba załadować całość uporządkowaną listę do pamięci, a następnie wziąć 20 artykułów po jednej z Id == 100.

Czy istnieje sposób na uniknięcie tego ogromnego zużycia pamięci?

Bardziej ogólnie, jaki jest najlepszy sposób osiągnięcia tego w SQL?

+1

Ciekawe wymogu. Czy istnieje jakiś związek między 'Id' i' PublicationDate'? Czy to, co chcesz zrobić, aby je zamówić według daty, przejdź do tej listy, aż dojdziesz do "Id" 100, a * następnie * weź 20 kolejnych? – AakashM

+0

Tak, właśnie tego chcę. Nie ma związku pomiędzy 'Id' i' PublicationDate'. Powodem tego wymogu jest to, że muszę pobrać pewną liczbę artykułów opublikowanych po określonym artykule, z których wiem tylko "Id". Oczywiście, gdybym znał 'PublicationDate' tego artykułu, byłoby to znacznie łatwiejsze. –

Odpowiedz

5

Jeżeli, jak zgaduję z nazwy kolumny, PublicationDate nie zmienia, można to zrobić w dwóch oddzielnych zapytań:

  • Ustalić PublicationDate z Article z Id == 100
  • Pobierz 20 artykułów z tego dnia

coś takiego:

var thresholdDate = db.Articles.Single(a => a.Id == 100).PublicationDate; 
var articles = 
    db.Articles 
    .Where(a => a.PublicationDate <= thresholdDate) 
    .OrderByDescending(a => a.PublicationDate) 
    .Take(20); 

Może być nawet, że LINQ to SQL można przetłumaczyć następująco:

var articles = 
    db.Articles 
    .Where(a => a.PublicationDate 
      <= db.Articles.Single(aa => aa.Id == 100).PublicationDate) 
    .OrderByDescending(a => a.PublicationDate) 
    .Take(20); 

ale to może być zbyt skomplikowane dla niego. Spróbuj i zobacz.

+0

Myślę, że jest to dobre rozwiązanie (również opublikowane przez @Atzoya), które mogą być również przeniesione do procedury przechowywanej w bazie danych. Przypuszczam, że nie możemy tego zrobić za pomocą pojedynczego zapytania SQL.Jedynym problemem jest tutaj (w mało prawdopodobnym) zdarzeniu, że mamy wiele artykułów o różnych identyfikatorach wysłanych w tym samym czasie. Ale aby to rozwiązać, musimy uzgodnić kolejną regułę zamawiania podczas pobierania artykułów, np. Przez malejące identyfikatory: 'var articles = db.Artykuły .W przypadku (a => a. PublikacjaDate <= thresholdDate && a. Id <100) .OrderByDescending (a => a. PublikacjaDate) .ThenByDescending (a => a.Id) .Take (20); ' –

+0

Próbowałem i to działa. LINQ to SQL potrafi nawet przetłumaczyć całe zapytanie LINQ na jedno zapytanie SQL. –

+0

Czy mogę zaproponować zastąpienie pojedynczego (a => a.Id == 100) pojedynczym (aa => aa.Id == 100), aby zaakceptować to jako odpowiedź? –

0

Czy nie jest rozwiązaniem, aby dodać instrukcję where?

IQueryable<Article> articles = db.Articles.Where(a => a.id != 100).OrderByDescending(a => a.PublicationDate).Take(20); 
+0

Tak, ja też tak pomyślałem, ale spójrz na rzeczywisty wymóg: zamów je według daty, przejdź do tej listy, aż dojdziesz do id 100, ** następnie ** weź 20 kolejnych. Dziwne. – AakashM

+1

To nie zadziała, jeśli artykuł 5 został opublikowany _ po artykule 10 (z powodu zasiadania w wersji roboczej) –

+0

Tak, nie rozumiem tego pytania. – Tan

1

Można spróbować jak to

var articles = 
    db.Articles 
    .Where(a => a.PublicationDate < db.Articles 
            .Where(aa => aa.Id==100) 
            .Select(aa => aa.PublicationDate) 
            .SingleOrDefault()) 
    .OrderByDescending(a => a.PublicationDate) 
    .Take(20); 
+0

+1, ponieważ jest to to samo rozwiązanie, co w przypadku AakashM, ale drugie ma jaśniejsze wyjaśnienie –

Powiązane problemy