Linq to SQL дает разные результаты при использовании TOP и Between - PullRequest
1 голос
/ 05 января 2010

Я использую Linq to Sql (на самом деле это Dynamic Linq to SQL, который позволяет передавать строки во время выполнения для операторов where, orderby и т. Д.) Но я получаю некоторые другие результаты, и кажется, что они основаны на том, базовый T-SQL использует ключевое слово TOP или использует BETWEEN.

Я попытался разбить проблему на небольшой пример, вот сценарий:

Я использую шаблон репозитория и следующий метод, который просто объединяет 2 таблицы с левым внешним соединением.

   public IQueryable<TestGalleryViewModel> FetchGalleryItems()
    {

        var galleryItems = from painting in Gallery
                           join artist in Artists
                               on painting.ArtistID equals artist.ArtistID

                           into paintingArtists
                           from artist in paintingArtists.DefaultIfEmpty()

                           select new TestGalleryViewModel
                           {

                               Id = painting.PaintingID,
                               ArtistName = artist == default(Artist) ? "" : artist.Surname + " " + artist.Forenames,
                           };

        return galleryItems;
    }

У меня есть небольшой метод тестирования, использующий метод FetchGalleryItems:

        var query = respository.Test_FetchGalleryItems().Where("ArtistName.Contains(\"Adams Charles James\")");

        var orderedlist = query.OrderBy("ArtistName asc");
        var page1 = orderedlist.Skip(0).Take(5);
        var page2 = orderedlist.Skip(5).Take(5);

orderList содержит следующие базовые значения:

176 ADAMS Charles James
620 ADAMS Charles James
621 ADAMS Charles James
660 ADAMS Charles James
683 ADAMS Charles James
707 ADAMS Charles James
735 ADAMS Charles James
739 ADAMS Charles James
740 ADAMS Charles James
741 ADAMS Charles James

Что я и ожидал. Но страница1 содержит

707 ADAMS Charles James
683 ADAMS Charles James
660 ADAMS Charles James
621 ADAMS Charles James
620 ADAMS Charles James

Что, как вы видите, НЕ первые 5 пунктов. Страница2 содержит

707 ADAMS Charles James
735 ADAMS Charles James
739 ADAMS Charles James
740 ADAMS Charles James
741 ADAMS Charles James

То, что я ожидал, это пункты с 6 по 10.

Базовым T-SQL для page1 является

SELECT TOP (5) [t3].[PaintingID] AS [Id], [t3].[value] AS [ArtistName]
FROM (
    SELECT [t0].[PaintingID], 
        (CASE 
            WHEN [t2].[test] IS NULL THEN CONVERT(NVarChar(101),'')
            ELSE ([t2].[Surname] + ' ') + [t2].[Forenames]
         END) AS [value]
    FROM [dbo].[Gallery] AS [t0]
    LEFT OUTER JOIN (
        SELECT 1 AS [test], [t1].[ArtistID], [t1].[Surname], [t1].[Forenames]
        FROM [dbo].[Artists] AS [t1]
        ) AS [t2] ON [t0].[ArtistID] = ([t2].[ArtistID])
    ) AS [t3]
WHERE [t3].[value] LIKE '%Adams Charles James%'
ORDER BY [t3].[value]

Обратите внимание, что используется TOP (5)

Базовый T-SQL для page2:

SELECT [t4].[PaintingID] AS [Id], [t4].[value] AS [ArtistName]
FROM (
    SELECT ROW_NUMBER() OVER (ORDER BY [t3].[value], [t3].[Surname], [t3].[Forenames]) AS [ROW_NUMBER], [t3].[PaintingID], [t3].[value]
    FROM (
        SELECT [t0].[PaintingID], 
            (CASE 
                WHEN [t2].[test] IS NULL THEN CONVERT(NVarChar(101),'')
                ELSE ([t2].[Surname] + ' ') + [t2].[Forenames]
             END) AS [value], [t2].[Surname], [t2].[Forenames]
        FROM [dbo].[Gallery] AS [t0]
        LEFT OUTER JOIN (
            SELECT 1 AS [test], [t1].[ArtistID], [t1].[Surname], [t1].[Forenames]
            FROM [dbo].[Artists] AS [t1]
            ) AS [t2] ON [t0].[ArtistID] = ([t2].[ArtistID])
        ) AS [t3]
    WHERE [t3].[value] LIKE '%Adams Charles James%'
    ) AS [t4]
WHERE [t4].[ROW_NUMBER] BETWEEN 5 + 1 AND 5 + 5
ORDER BY [t4].[ROW_NUMBER]

Обратите внимание, что он использует МЕЖДУ

Когда я вставляю команды T-SQL в SQL Express Management Studio, я получаю результаты, которые я описал. Если бы я использовал T2-SQL page2 и изменил строку

 WHERE [t4].[ROW_NUMBER] BETWEEN 5 + 1 AND 5 + 5

будет

WHERE [t4].[ROW_NUMBER] BETWEEN 1 AND 5

Я получил результаты, которые я ожидал для страницы1. первые 5 элементов.

176 ADAMS Charles James
620 ADAMS Charles James
621 ADAMS Charles James
660 ADAMS Charles James
683 ADAMS Charles James

Итак, в двух словах, когда T-SQL использует Между вместо TOP Я получаю ожидаемые результаты.

Я использую фильтрацию (предложение where), сортировку (orderBy) и разбиение по страницам (пропустить и принять) во всем приложении, и мне нужно обрабатывать это довольно обобщенно.

  • Есть ли способ заставить LINQ использовать МЕЖДУ синтаксисом вместо TOP ?
  • Должен ли я приближаться к этому иначе?

Извинения за длинный пост.

С уважением, Simon

1 Ответ

0 голосов
/ 05 января 2010

Независимо от того, как генерируется SQL (LINQ или иным образом), если вы ORDER BY столбец с дублирующимися значениями, вы можете получать разные результаты при каждом запуске запроса.

Когда вы ORDER BY [t3].[value] сортируете столбец, содержащий много повторяющихся значений.

Вы можете проверить это, запустив очень простой SQL SELECT из Management Studio. Каждый раз, когда вы запускаете его, вы получаете другой результат.

Один из способов получения согласованных результатов - использовать ROW_NUMBER, как вы уже сделали. Кроме того, добавление любого другого столбца к уникальному ORDER BY приведет к тому, что результаты всегда будут возвращаться в одном и том же порядке. Не имеет значения, связан ли этот другой столбец с вашим запросом, просто он уникален.

...