我可能不是SQL专家,但在进行性能测试时,我看到EF6(Code-First)生成以下语句:
SELECT
[UnionAll3].[C2] AS [C1],
[UnionAll3].[C3] AS [C2],
[UnionAll3].[C4] AS [C3],
[UnionAll3].[C5] AS [C4],
[UnionAll3].[C6] AS [C5],
[UnionAll3].[C7] AS [C6],
[UnionAll3].[C8] AS [C7],
[UnionAll3].[C1] AS [C8],
[UnionAll3].[C9] AS [C9],
[UnionAll3].[C10] AS [C10],
[UnionAll3].[C11] AS [C11],
[UnionAll3].[C12] AS [C12],
[UnionAll3].[C13] AS [C13],
[UnionAll3].[C14] AS [C14],
[UnionAll3].[C15] AS [C15],
[UnionAll3].[C16] AS [C16],
[UnionAll3].[C17] AS [C17],
[UnionAll3].[C18] AS [C18],
[UnionAll3].[C19] AS [C19],
[UnionAll3].[C20] AS [C20],
[UnionAll3].[C21] AS [C21],
[UnionAll3].[C22] AS [C22],
[UnionAll3].[C23] AS [C23],
[UnionAll3].[C24] AS [C24],
[UnionAll3].[C25] AS [C25],
[UnionAll3].[C26] AS [C26],
[UnionAll3].[C27] AS [C27],
[UnionAll3].[C28] AS [C28],
[UnionAll3].[C29] AS [C29]
FROM (SELECT
[UnionAll2].[C1] AS [C1],
[UnionAll2].[C2] AS [C2],
[UnionAll2].[C3] AS [C3],
[UnionAll2].[C4] AS [C4],
[UnionAll2].[C5] AS [C5],
[UnionAll2].[C6] AS [C6],
[UnionAll2].[C7] AS [C7],
[UnionAll2].[C8] AS [C8],
[UnionAll2].[C9] AS [C9],
[UnionAll2].[C10] AS [C10],
[UnionAll2].[C11] AS [C11],
[UnionAll2].[C12] AS [C12],
[UnionAll2].[C13] AS [C13],
[UnionAll2].[C14] AS [C14],
[UnionAll2].[C15] AS [C15],
[UnionAll2].[C16] AS [C16],
[UnionAll2].[C17] AS [C17],
[UnionAll2].[C18] AS [C18],
[UnionAll2].[C19] AS [C19],
[UnionAll2].[C20] AS [C20],
[UnionAll2].[C21] AS [C21],
[UnionAll2].[C22] AS [C22],
[UnionAll2].[C23] AS [C23],
[UnionAll2].[C24] AS [C24],
[UnionAll2].[C25] AS [C25],
[UnionAll2].[C26] AS [C26],
[UnionAll2].[C27] AS [C27],
[UnionAll2].[C28] AS [C28],
[UnionAll2].[C29] AS [C29]
FROM (SELECT
[UnionAll1].[C1] AS [C1],
[UnionAll1].[Id] AS [C2],
[UnionAll1].[Id1] AS [C3],
[UnionAll1].[Ident] AS [C4],
[UnionAll1].[DescriptionLong] AS [C5],
[UnionAll1].[DescriptionShort1] AS [C6],
[UnionAll1].[DescriptionShort2] AS [C7],
[UnionAll1].[ArticleGroup] AS [C8],
[UnionAll1].[Id2] AS [C9],
[UnionAll1].[Id3] AS [C10],
[UnionAll1].[Barcode] AS [C11],
[UnionAll1].[Amount] AS [C12],
[UnionAll1].[StorageLocation] AS [C13],
[UnionAll1].[Article_Id] AS [C14],
[UnionAll1].[C2] AS [C15],
[UnionAll1].[C3] AS [C16],
[UnionAll1].[C4] AS [C17],
[UnionAll1].[C5] AS [C18],
[UnionAll1].[C6] AS [C19],
[UnionAll1].[C7] AS [C20],
[UnionAll1].[C8] AS [C21],
[UnionAll1].[C9] AS [C22],
[UnionAll1].[C10] AS [C23],
[UnionAll1].[C11] AS [C24],
[UnionAll1].[C12] AS [C25],
[UnionAll1].[C13] AS [C26],
[UnionAll1].[C14] AS [C27],
[UnionAll1].[C15] AS [C28],
[UnionAll1].[C16] AS [C29]
FROM (SELECT
CASE WHEN ([Extent2].[Id] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C1],
[Extent1].[Id] AS [Id],
[Extent1].[Id] AS [Id1],
[Extent1].[Ident] AS [Ident],
[Extent1].[DescriptionLong] AS [DescriptionLong],
[Extent1].[DescriptionShort1] AS [DescriptionShort1],
[Extent1].[DescriptionShort2] AS [DescriptionShort2],
[Extent1].[ArticleGroup] AS [ArticleGroup],
[Extent2].[Id] AS [Id2],
[Extent2].[Id] AS [Id3],
[Extent2].[Barcode] AS [Barcode],
[Extent2].[Amount] AS [Amount],
[Extent2].[StorageLocation] AS [StorageLocation],
[Extent2].[Article_Id] AS [Article_Id],
CAST(NULL AS int) AS [C2],
CAST(NULL AS int) AS [C3],
CAST(NULL AS int) AS [C4],
CAST(NULL AS decimal(18,2)) AS [C5],
CAST(NULL AS int) AS [C6],
CAST(NULL AS int) AS [C7],
CAST(NULL AS int) AS [C8],
CAST(NULL AS int) AS [C9],
CAST(NULL AS decimal(18,2)) AS [C10],
CAST(NULL AS datetime2) AS [C11],
CAST(NULL AS int) AS [C12],
CAST(NULL AS int) AS [C13],
CAST(NULL AS int) AS [C14],
CAST(NULL AS varchar(1)) AS [C15],
CAST(NULL AS int) AS [C16]
FROM [dbo].[Articles] AS [Extent1]
LEFT OUTER JOIN [dbo].[Batches] AS [Extent2] ON [Extent1].[Id] = [Extent2].[Article_Id]
UNION ALL
SELECT
2 AS [C1],
[Extent3].[Id] AS [Id],
[Extent3].[Id] AS [Id1],
[Extent3].[Ident] AS [Ident],
[Extent3].[DescriptionLong] AS [DescriptionLong],
[Extent3].[DescriptionShort1] AS [DescriptionShort1],
[Extent3].[DescriptionShort2] AS [DescriptionShort2],
[Extent3].[ArticleGroup] AS [ArticleGroup],
CAST(NULL AS int) AS [C2],
CAST(NULL AS int) AS [C3],
CAST(NULL AS varchar(1)) AS [C4],
CAST(NULL AS decimal(18,2)) AS [C5],
CAST(NULL AS int) AS [C6],
CAST(NULL AS int) AS [C7],
[Extent4].[Id] AS [Id2],
[Extent4].[Id] AS [Id3],
[Extent4].[Amount] AS [Amount],
[Extent4].[PricePerUncorrectedUnit] AS [PricePerUncorrectedUnit],
[Extent4].[Type] AS [Type],
[Extent4].[Article_Id] AS [Article_Id],
CAST(NULL AS int) AS [C8],
CAST(NULL AS int) AS [C9],
CAST(NULL AS decimal(18,2)) AS [C10],
CAST(NULL AS datetime2) AS [C11],
CAST(NULL AS int) AS [C12],
CAST(NULL AS int) AS [C13],
CAST(NULL AS int) AS [C14],
CAST(NULL AS varchar(1)) AS [C15],
CAST(NULL AS int) AS [C16]
FROM [dbo].[Articles] AS [Extent3]
INNER JOIN [dbo].[ScalePrices] AS [Extent4] ON [Extent3].[Id] = [Extent4].[Article_Id]) AS [UnionAll1]
UNION ALL
SELECT
3 AS [C1],
[Extent5].[Id] AS [Id],
[Extent5].[Id] AS [Id1],
[Extent5].[Ident] AS [Ident],
[Extent5].[DescriptionLong] AS [DescriptionLong],
[Extent5].[DescriptionShort1] AS [DescriptionShort1],
[Extent5].[DescriptionShort2] AS [DescriptionShort2],
[Extent5].[ArticleGroup] AS [ArticleGroup],
CAST(NULL AS int) AS [C2],
CAST(NULL AS int) AS [C3],
CAST(NULL AS varchar(1)) AS [C4],
CAST(NULL AS decimal(18,2)) AS [C5],
CAST(NULL AS int) AS [C6],
CAST(NULL AS int) AS [C7],
CAST(NULL AS int) AS [C8],
CAST(NULL AS int) AS [C9],
CAST(NULL AS int) AS [C10],
CAST(NULL AS decimal(18,2)) AS [C11],
CAST(NULL AS int) AS [C12],
CAST(NULL AS int) AS [C13],
[Extent6].[Id] AS [Id2],
[Extent6].[Id] AS [Id3],
[Extent6].[Amount] AS [Amount],
[Extent6].[Date] AS [Date],
[Extent6].[Article_Id] AS [Article_Id],
CAST(NULL AS int) AS [C14],
CAST(NULL AS int) AS [C15],
CAST(NULL AS varchar(1)) AS [C16],
CAST(NULL AS int) AS [C17]
FROM [dbo].[Articles] AS [Extent5]
INNER JOIN [dbo].[Demands] AS [Extent6] ON [Extent5].[Id] = [Extent6].[Article_Id]) AS [UnionAll2]
UNION ALL
SELECT
4 AS [C1],
[Extent7].[Id] AS [Id],
[Extent7].[Id] AS [Id1],
[Extent7].[Ident] AS [Ident],
[Extent7].[DescriptionLong] AS [DescriptionLong],
[Extent7].[DescriptionShort1] AS [DescriptionShort1],
[Extent7].[DescriptionShort2] AS [DescriptionShort2],
[Extent7].[ArticleGroup] AS [ArticleGroup],
CAST(NULL AS int) AS [C2],
CAST(NULL AS int) AS [C3],
CAST(NULL AS varchar(1)) AS [C4],
CAST(NULL AS decimal(18,2)) AS [C5],
CAST(NULL AS int) AS [C6],
CAST(NULL AS int) AS [C7],
CAST(NULL AS int) AS [C8],
CAST(NULL AS int) AS [C9],
CAST(NULL AS int) AS [C10],
CAST(NULL AS decimal(18,2)) AS [C11],
CAST(NULL AS int) AS [C12],
CAST(NULL AS int) AS [C13],
CAST(NULL AS int) AS [C14],
CAST(NULL AS int) AS [C15],
CAST(NULL AS decimal(18,2)) AS [C16],
CAST(NULL AS datetime2) AS [C17],
CAST(NULL AS int) AS [C18],
[Extent8].[ProjectUsageId] AS [ProjectUsageId],
[Extent8].[ProjectUsageId] AS [ProjectUsageId1],
[Extent8].[ProjectUsageName] AS [ProjectUsageName],
[Extent8].[Article_Id] AS [Article_Id]
FROM [dbo].[Articles] AS [Extent7]
INNER JOIN [dbo].[ProjectUsages] AS [Extent8] ON [Extent7].[Id] = [Extent8].[Article_Id]) AS [UnionAll3]
ORDER BY [UnionAll3].[C3] ASC, [UnionAll3].[C1] ASC
我不敢相信,当它只是从5个表中获取数据时(这可以很好地连接并且定义了主键+关系),这在任何方面都是有效的。以下是导致此SQL语句的语句:
IQueryable<Article> articles = context.Articles
.Include("Batches")
.Include("ScalePrices")
.Include("Demands")
.Include("ProjectUsages");
var actualQuery = context.Articles.Where(d => d.DescriptionLong.Contains(searchTermList[0]) || d.Ident.Contains(searchTermList[0]));
表格定义如下(示例):
[Table("Articles")]
public class Article
{
[Key]
public int Id { get; set; }
[Display(Name = "Article", ResourceType = typeof(Resources))]
[StringLength(32), Required]
public string Ident { get; set; }
[Display(Name = "Description", ResourceType = typeof(Resources))]
[StringLength(4000)]
public string DescriptionLong { get; set; }
}
[Serializable]
[Table ("Batches")]
public class ArticleBatch
{
[Key]
public int Id { get; set; }
[StringLength(32)]
public string Barcode { get; set; }
public decimal Amount { get; set; }
public ArticleStorageLocation StorageLocation { get; set; }
public override string ToString()
{
return String.Format("{0} ({1}/{2})", Barcode, Amount, StorageLocation);
}
}
DbContext看起来像这样:
public class ArticleDataDbContext : DbContext
{
public ArticleDataDbContext(string connectionString) : base(connectionString)
{
Database.SetInitializer(new CreateDatabaseIfNotExists<ArticleDataDbContext>());
this.Configuration.LazyLoadingEnabled = false;
this.Configuration.ProxyCreationEnabled = false;
}
public DbSet<Article> Articles { get; set; }
public DbSet<ArticleBatch> Batches { get; set; }
public DbSet<ArticleDemand> Demands { get; set; }
public DbSet<ArticleScalePrice> ScalePrices { get; set; }
public DbSet<ProjectUsage> ProjectUsages { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Article>().HasMany(d => d.Batches).WithRequired();
modelBuilder.Entity<Article>().HasMany(d => d.Demands).WithRequired();
modelBuilder.Entity<Article>().HasMany(d => d.ScalePrices).WithRequired();
modelBuilder.Entity<Article>().HasMany(d => d.ProjectUsages).WithRequired();
}
}
这是SQL Management Studio中的ER模型图:
您是否知道此陈述是否“正确”或是否完全无稽之谈。我做错了什么?
答案 0 :(得分:7)
实体框架会创建这样一个复杂的查询,因为简单查询会返回重复数据的批次。假设每个子表包含特定文章的10条记录,并假设您在文章ID上左连接所有四个子表。您现在得到的结果集为10 * 10 * 10 * 10 = 10000行。实体框架的查询仅提供1 + 10 + 10 + 10 + 10 = 41行,但仍然为您提供所需的所有数据。
根据评论,如果查询确实导致性能问题,您可以将其拆分为多个单独的简单查询:
var articles = context.Articles.Where(d =>
d.DescriptionLong.Contains(searchTermList[0])
|| d.Ident.Contains(searchTermList[0])).ToList();
// Either loop, or find one specific article for which
// you want to load the details. I'll use a loop.
foreach (var article in articles) {
article.Batches.Load();
article.ScalePrices.Load();
article.Demands.Load();
article.ProjectUsages.Load();
}
您甚至可以将其包装在可序列化/快照事务中,以确保数据的一致性。