LINQ, производящий SQL с вложенным дубликатом, выбирает

Я очень плохо знаком с.NET Entity Framework, и я думаю, что это потрясающее, но так или иначе я получаю эту странную проблему (извините для испанцев, но моя программа находится на том языке, так или иначе это не имеет большого значения, просто колонка или имущественные имена): я делаю нормальный LINQ К вопросу Предприятий, чтобы получить список UltimaConsulta, как это:

var query = from uc in bd.UltimasConsultas
            select uc;

UltimasConsultas - представление, btw. Вещь состоит в том, что LINQ производит этот SQL для вопроса:

SELECT 
[Extent1].[IdPaciente] AS [IdPaciente], 
[Extent1].[Nombre] AS [Nombre], 
[Extent1].[PrimerApellido] AS [PrimerApellido], 
[Extent1].[SegundoApellido] AS [SegundoApellido], 
[Extent1].[Fecha] AS [Fecha]
FROM (SELECT 
      [UltimasConsultas].[IdPaciente] AS [IdPaciente], 
      [UltimasConsultas].[Nombre] AS [Nombre], 
      [UltimasConsultas].[PrimerApellido] AS [PrimerApellido], 
      [UltimasConsultas].[SegundoApellido] AS [SegundoApellido], 
      [UltimasConsultas].[Fecha] AS [Fecha]
      FROM [dbo].[UltimasConsultas] AS [UltimasConsultas]) AS [Extent1]

Почему LINQ производит вложенное Избранное? Я думал от видео и примеров, что это производит нормальный SQL, выбирает для этого вида вопросов. Я должен настроить что-то (модель предприятия производила от волшебника, таким образом, это - конфигурация по умолчанию)? Заранее спасибо за Ваши ответы.

16
задан Alfonso Vasquez 21 January 2010 в 22:13
поделиться

2 ответа

, чтобы быть понятным, Linq к объектам не генерируют SQL. Вместо этого он генерирует каноническое дерево ADO.Net, а поставщик Ado.net для вашей базы данных, предположительно SQL Server в этом случае, генерирует SQL.

Итак, почему он генерирует эту полученную таблицу (я думаю, что «полученная таблица» - это более правильный срок для функции SQL здесь)? Поскольку код, который генерирует SQL, должен генерировать SQL для широкого спектра запросов LINQ, большинство из которых находятся не так тривиально, как и вы показываете. Эти запросы часто будут выбирать данные для нескольких типов (многие из которых могут быть анонимными, а не названными типами), а также для того, чтобы сохранить сравнительно здание SQL, они сгруппированы в экстенцию для каждого типа.

Другой вопрос: почему вы должны заботиться? Легко продемонстрировать, что использование производной таблицы в этом утверждении «бесплатно» с точки зрения производительности.

Я выбрал таблицу случайным образом из заполненной базы данных и выполнял следующий запрос:

SELECT [AddressId]
      ,[Address1]
      ,[Address2]
      ,[City]
      ,[State]
      ,[ZIP]
      ,[ZIPExtension]
  FROM [VertexRM].[dbo].[Address]

Давайте посмотрим на стоимость:

<StmtSimple StatementCompId="1" StatementEstRows="7900" StatementId="1" StatementOptmLevel="TRIVIAL" StatementSubTreeCost="0.123824" StatementText="/****** Script for SelectTopNRows command from SSMS  ******/&#xD;&#xA;SELECT [AddressId]&#xD;&#xA;      ,[Address1]&#xD;&#xA;      ,[Address2]&#xD;&#xA;      ,[City]&#xD;&#xA;      ,[State]&#xD;&#xA;      ,[ZIP]&#xD;&#xA;      ,[ZIPExtension]&#xD;&#xA;  FROM [VertexRM].[dbo].[Address]" StatementType="SELECT">
  <StatementSetOptions ANSI_NULLS="false" ANSI_PADDING="false" ANSI_WARNINGS="false" ARITHABORT="true" CONCAT_NULL_YIELDS_NULL="false" NUMERIC_ROUNDABORT="false" QUOTED_IDENTIFIER="false" />
  <QueryPlan CachedPlanSize="9" CompileTime="0" CompileCPU="0" CompileMemory="64">
    <RelOp AvgRowSize="246" EstimateCPU="0.008847" EstimateIO="0.114977" EstimateRebinds="0" EstimateRewinds="0" EstimateRows="7900" LogicalOp="Clustered Index Scan" NodeId="0" Parallel="false" PhysicalOp="Clustered Index Scan" EstimatedTotalSubtreeCost="0.123824">

Теперь давайте сравним это на запрос с полученной таблицей:

SELECT 
       [Extent1].[AddressId]
      ,[Extent1].[Address1]
      ,[Extent1].[Address2]
      ,[Extent1].[City]
      ,[Extent1].[State]
      ,[Extent1].[ZIP]
      ,[Extent1].[ZIPExtension]
  FROM (SELECT [AddressId]
          ,[Address1]
          ,[Address2]
          ,[City]
          ,[State]
          ,[ZIP]
          ,[ZIPExtension]
  FROM[VertexRM].[dbo].[Address]) AS [Extent1]

и стоимость :

<StmtSimple StatementCompId="1" StatementEstRows="7900" StatementId="1" StatementOptmLevel="TRIVIAL" StatementSubTreeCost="0.123824" StatementText="/****** Script for SelectTopNRows command from SSMS  ******/&#xD;&#xA;SELECT &#xD;&#xA;       [Extent1].[AddressId]&#xD;&#xA;      ,[Extent1].[Address1]&#xD;&#xA;      ,[Extent1].[Address2]&#xD;&#xA;      ,[Extent1].[City]&#xD;&#xA;      ,[Extent1].[State]&#xD;&#xA;      ,[Extent1].[ZIP]&#xD;&#xA;      ,[Extent1].[ZIPExtension]&#xD;&#xA;  FROM (SELECT [AddressId]&#xD;&#xA;          ,[Address1]&#xD;&#xA;          ,[Address2]&#xD;&#xA;          ,[City]&#xD;&#xA;          ,[State]&#xD;&#xA;          ,[ZIP]&#xD;&#xA;          ,[ZIPExtension]&#xD;&#xA;  FROM[VertexRM].[dbo].[Address]) AS [Extent1]" StatementType="SELECT">
  <StatementSetOptions ANSI_NULLS="false" ANSI_PADDING="false" ANSI_WARNINGS="false" ARITHABORT="true" CONCAT_NULL_YIELDS_NULL="false" NUMERIC_ROUNDABORT="false" QUOTED_IDENTIFIER="false" />
  <QueryPlan CachedPlanSize="9" CompileTime="0" CompileCPU="0" CompileMemory="64">
    <RelOp AvgRowSize="246" EstimateCPU="0.008847" EstimateIO="0.114977" EstimateRebinds="0" EstimateRewinds="0" EstimateRows="7900" LogicalOp="Clustered Index Scan" NodeId="0" Parallel="false" PhysicalOp="Clustered Index Scan" EstimatedTotalSubtreeCost="0.123824">

В обоих случаях SQL Server просто сканирует кластерный индекс. Неудивительно, что стоимость почти точно такой же.

Давайте посмотрим на слегка более сложный запрос. Я выпустил LinqPAD и вошел в следующие запросы против одной таблицы, плюс одна связанная таблица:

from a in Addresses
select new
{
    Id = a.Id,
    Address1 = a.Address1,
    Address2 = a.Address2,
    City = a.City,
    State = a.State,
    ZIP = a.ZIP,
    ZIPExtension = a.ZIPExtension,
    PersonCount = a.EntityAddresses.Count()
}

Это генерирует следующие SQL:

SELECT 
1 AS [C1], 
[Project1].[AddressId] AS [AddressId], 
[Project1].[Address1] AS [Address1], 
[Project1].[Address2] AS [Address2], 
[Project1].[City] AS [City], 
[Project1].[State] AS [State], 
[Project1].[ZIP] AS [ZIP], 
[Project1].[ZIPExtension] AS [ZIPExtension], 
[Project1].[C1] AS [C2]
FROM ( SELECT 
    [Extent1].[AddressId] AS [AddressId], 
    [Extent1].[Address1] AS [Address1], 
    [Extent1].[Address2] AS [Address2], 
    [Extent1].[City] AS [City], 
    [Extent1].[State] AS [State], 
    [Extent1].[ZIP] AS [ZIP], 
    [Extent1].[ZIPExtension] AS [ZIPExtension], 
    (SELECT 
        COUNT(cast(1 as bit)) AS [A1]
        FROM [dbo].[EntityAddress] AS [Extent2]
        WHERE [Extent1].[AddressId] = [Extent2].[AddressId]) AS [C1]
    FROM [dbo].[Address] AS [Extent1]
)  AS [Project1]

, анализируя это, мы видим, что Project1 - это Проекция на анонимный тип. Extent1 - это адрес таблица / объекта. И Extent2 - это таблица для ассоциации. Сейчас нет производного стола для адреса , но есть один для проекции.

Я не знаю, если вы когда-либо написали систему поколений SQL, но это не легко. Я считаю, что общая проблема доказывает, что запрос LINQ к объектам и запрос SQL эквивалентен, является NP-Hard, хотя определенные конкретные случаи, очевидно, намного проще. SQL намеренно является неполным, потому что его дизайнеры хотели, чтобы все запросы SQL выполняются в ограниченном времени. Linq, не так.

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

13
ответ дан 30 November 2019 в 23:00
поделиться

Монитор и базовый «синхронизм», который может быть связан с любым эталонным объектом - основным механизмом под блокировкой C # . Только один нить может иметь замок. Это просто и эффективно.

ReaderWriterLock (или, в V3.5, тем лучше ReaderWriterLockSlim ) обеспечивает более сложную модель. Избегайте, если вы не знаете , это будет более эффективным (то есть есть измерения производительности для поддержки себя).

Лучший вид блокировки - это запирание, вам не нужно (то есть не делитесь данными между потоками).

-121--921034-

В основном он определяет, какой Extent1 состоит из и какие переменные будут относиться к каждой записи. Затем его отображение фактической таблицы базы данных в Extent1, чтобы она мог вернуть все записи для этой таблицы.

Это то, что ваш запрос просит. Это просто, что LINQ не может добавить в персонаж подстановки, как вы, если вы сделали его вручную.

0
ответ дан 30 November 2019 в 23:00
поделиться
Другие вопросы по тегам:

Похожие вопросы: