Linq to SQL создает дополнительный суб-выбор при объединении - PullRequest
3 голосов
/ 17 марта 2011

У меня есть простые родительские дочерние отношения, которые я хотел бы загрузить с помощью LINQ to SQL. Я хочу загружать детей одновременно с родителем. Сгенерированный SQL делает слишком много работы. Он пытается сосчитать детей, а также присоединиться к ним. Я не буду обновлять эти объекты. Я не буду добавлять детей к родителю. Я только заинтересован в чтении этого. Я упростил таблицы до минимума. На самом деле у меня есть больше столбцов. LINQ to SQL генерирует следующий SQL

SELECT [t0].[parentId] AS [Id], [t0].[name], [t1].[childId] AS [Id2], 
[t1].[parentId], [t1].[name] AS [name2], 
( SELECT COUNT(*)
    FROM [dbo].[linqchild] AS [t2]
    WHERE [t2].[parentId] = [t0].[parentId]
) AS [value]
FROM [dbo].[linqparent] AS [t0]
LEFT OUTER JOIN [dbo].[linqchild] AS [t1] ON [t1].[parentId] = [t0].[parentId]
ORDER BY [t0].[parentId], [t1].[childId]

Я не знаю, почему SELECT COUNT(*) ... там. Я бы предпочел, чтобы это ушло. И родительская, и дочерняя таблицы будут содержать миллионы строк. Дополнительный запрос стоит очень много времени. Это кажется ненужным. Есть ли способ заставить его уйти? Я также не уверен, откуда взялся ORDER BY.

Классы выглядят так.

[Table(Name = "dbo.linqparent")]
public class LinqParent
{
    [Column(Name = "parentId", AutoSync = AutoSync.OnInsert, IsPrimaryKey = true, IsDbGenerated = true, CanBeNull = false)]
    public long Id { get; set; }

    [ Column( Name = "name", CanBeNull = false ) ]
    public string name { get; set; }

    [Association(OtherKey = "parentId", ThisKey = "Id", IsForeignKey = true)]
    public IEnumerable<LinqChild> Kids { get; set; }
}


[Table(Name = "dbo.linqchild")]
public class LinqChild
{
    [Column(Name = "childId", AutoSync = AutoSync.OnInsert, IsPrimaryKey = true, IsDbGenerated = true, CanBeNull = false)]
    public long Id { get; set; }

    [ Column( Name = "parentId", CanBeNull = false ) ]
    public long parentId { get; set; }

    [Column(Name = "name", CanBeNull = false)]
    public string name { get; set; }
}

Я использую что-то вроде следующего для запроса, в производстве будет предложение where и соответствующий индекс.

using (DataContext context = new DataContext(new DatabaseStringFinder().ConnectionString, new AttributeMappingSource()) { ObjectTrackingEnabled = false, DeferredLoadingEnabled = false })
{
    var loadOptions = new DataLoadOptions();
    loadOptions.LoadWith<LinqParent>(f => f.Kids);
    context.LoadOptions = loadOptions;
    var table = context.GetTable<LinqParent>();
    context.Log = Console.Out;

    // do something with table.
}

Ответы [ 2 ]

0 голосов
/ 17 марта 2011

Сгенерированный запрос не так уж и неэффективен. Если вы посмотрите на примерный план выполнения, то увидите, что затраты на подсчет (*) очень минимальны. Порядок order by должен упорядочиваться по первичному ключу, который, вероятно, является вашим кластеризованным индексом, поэтому он также должен оказывать очень незначительное влияние на производительность.

При проверке производительности ваших запросов LINQ необходимо убедиться в том, что context.Log не задан. Установка этого параметра в Console.Out приведет к огромному падению производительности.

Надеюсь, это поможет.

Edit:

Посмотрев немного ближе к плану выполнения, я вижу, что, хотя мой счетчик (*) был просто сканированием кластеризованного индекса, он все равно составлял 33% моего выполнения, поэтому я согласен, что это немного раздражает дополнительный суб-выбор в sql. Если это действительно узкая часть производительности, то вы можете рассмотреть возможность создания представления или сохраненного процесса для возврата ваших результатов.

0 голосов
/ 17 марта 2011

К сожалению, нет.ORM никогда не являются наиболее эффективным решением;вы всегда будете получать лучшую производительность, если будете писать свой собственный SQL (или использовать хранимые процедуры), но это компромисс, который достигается.

То, что вы видите, является стандартной практикой с ORM;вместо использования запроса с несколькими результатами (который мне кажется наиболее эффективным, но я не являюсь автором библиотеки ORM), ORM сведет весь график в один запрос и вернет все * 1004.* необходимой ему информации, включая информацию, которая помогает определить, какие биты данных дублируются, для перестройки графа.

Это также откуда взято ORDER BY, так как оно требует, чтобы связанные объектыбыть в смежных блоках.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...