2013-07-31 17 views
13

Używam LINQ do SQL (nie Entity Framework), biblioteki System.Data.Linq.DataContext, uderzanie bazy danych SQL Server 2005 i korzystania z .Net Framework 4.LINQ to SQL nie wygeneruje zapytaniu sargable

Tabela dbo.Dogs zawiera kolumnę "Aktywny" typu CHAR (1) NULL. Gdybym pisał prosto SQL kwerenda będzie:

SELECT * FROM dbo.Dogs where Active = 'A'; 

Zapytanie LINQ to:

from d in myDataContext.Dogs where d.Active == 'A' select d; 

SQL, który pobiera generowane z powyższego zapytania LINQ konwertuje aktywnym polu Unicode. Oznacza to, że nie można używać indeksu na kolumnie dbo.Dogs.Active, spowalniając znacznie zapytanie:

SELECT [t0].Name, [t0].Active 
FROM [dbo].[Dog] AS [t0] 
WHERE UNICODE([t0].[Active]) = @p1 

Czy jest coś, co mogę zrobić, aby zatrzymać LINQ do SQL z włożeniem że UNICODE() call (a tym samym utraty korzyść z mojego indeksu na temat psów.Active)? Próbowałem owijania parametrów przy użyciu metody EntityFunctions.AsNonUnicode(), ale to nie pomogło (jest włożona CONVERT(), aby NVARCHAR zamiast UNICODE() w wygenerowanym SQL), np:

...where d.Active.ToString() == EntityFunctions.AsNonUnicode('A'.ToString()); 
+0

To jest moja osobista opinia, ale opiera się na moim własnym doświadczeniu - nie używaj Linq2SQL, produkuje straszne Instrukcje SQL, a bardziej złożone zapytania Linq rosną - mniej optymalne instrukcje SQL są wydawane. Nie jestem pewien co do innych ORM-ów, ale ten jest zły. –

+0

Aye. Powinienem dodać, że nie używam Linq do Sql, ponieważ porównałem różne ORMy i uznałem, że Linq to Sql jest najlepszy. Utrzymuję starszą wersję kodu. –

+1

Chociaż ORM są bardzo nieszczelnymi abstrakcjami, nie zgadzam się ze stwierdzeniami krzyżowymi, aby nigdy z nich nie korzystać. Decyzja zależy w dużej mierze od przypadku użycia. Jest tam wyraźna wartość, a także wyraźne wady. – usr

Odpowiedz

1

Możesz zrobić mały hack (jak to często jest wymagane z LINQ do SQL i EF). Zadeklaruj właściwość jako NCHAR w dbml. Mam nadzieję, że usunie to konieczność konwersji UNICODE. W ten sposób pobieramy L2S w sposób łagodny.

Być może trzeba również wstawić wywołanie EntityFunctions.AsNonUnicode, aby po prawej stronie wybrać typ inny niż Unicode. Można również spróbować odwzorować kolumnę jako varchar.

+0

Hakowanie pliku dbml w celu zmiany "CHAR (1)" na "VARCHAR (1)" spowodowało, że trik, dzięki za radę. Oczywiście wadą tego podejścia jest to, że nigdy nie można ponownie użyć projektanta do aktualizacji pliku dbml, ale dla starszej aplikacji, z którą pracuję, to nie jest problem. –

+2

Alternatywnie, użycie '.Equals' zamiast' == 'tworzy SQL bez wywołania funkcji" UNICODE ". Co do tego, nie mam pojęcia. – RoadieRich

2

Nie ma wiele możesz zrobić, aby sposób, w jaki kwerendy LINQ zostały przetłumaczone na instrukcje SQL, ale możesz zapisać procedurę składowaną, która zawiera twoje zapytania i wywołać to SP jako funkcję LINQ2SQL. W ten sposób powinieneś w pełni korzystać z optymalizacji SQL Server

+0

Lub po prostu wpisz zapytanie SQL wbudowane, To odwzoruje dobrze na DTO, podobnie jak instrukcja linq. – Magnus

+0

Nie wiedziałem, że Linq2SQL obsługuje zapytania śródliniowe. Czy istnieje dokumentacja dotycząca tej funkcji? –

+0

Zobacz tutaj na przykład: http://msdn.microsoft.com/en-us/library/bb399403.aspx – Magnus

4

Linq ma na celu ułatwienie pisania zapytań i nie zawsze generuje optymalny kod SQL. Czasami, gdy wymagana jest wysoka wydajność, bardziej wydajne jest zapisywanie surowego SQL bezpośrednio w bazie danych, format danych Linq obsługuje odwzorowywanie wyniku SQL na podmioty takie jak linq. W twoim przypadku proponuję piśmie:

IEnumerable<Dog> results = db.ExecuteQuery<Dog>(
          "SELECT * FROM dbo.Dogs where Active = {0}", 
          'A'); 
+0

Lub zapisz procedurę przechowywaną. – LoztInSpace

+1

@LoztInSpace Oczywiście, że działa również, ale to mniej wysiłku. A procedury składowane są tak 90-te ;-) – Magnus

+0

Chciałbym móc zaznaczyć to jako odpowiedź, zarówno pracę, jak i będę realizować zarówno w różnych miejscach. –

3

To stara sprawa, ale wpadłem na ten niedawno.

Zamiast pisać

from d in myDataContext.Dogs where d.Active == 'A' select d; 

Write

from d in myDataContext.Dogs where d.Active.Equals('A') select d; 

To przyniesie pożądany SQL bez konieczności uciekania się do jakiejkolwiek z "hacków" wspomniano w innych odpowiedzi. Nie mogę powiedzieć dlaczego na pewno.

Napisałem, że as a question, więc zobaczymy, czy otrzymamy jakieś dobre odpowiedzi.