【问题标题】:converting sql statement back to lambda expression将sql语句转换回lambda表达式
【发布时间】:2019-06-20 18:52:45
【问题描述】:

我有下面的查询,以及它的 sql 代码。运行速度很慢,所以用sql重新编写,现在我只是不确定如何将sql转换回lambda表达式。

这是表达式中给我问题的部分,在

r.RecordProducts.Any()

                records = records
                .Include(r => r.Employer)
                .Include(r => r.Contractor)
                .Include(r => r.RecordProducts)
                .ThenInclude(rp => rp.ProductDefendant.Defendant)
                .Where(r => EF.Functions.Like(r.Employer.DefendantCode, "%" + input.DefendantCode + "%")
                    || EF.Functions.Like(r.Contractor.DefendantCode, "%" + input.DefendantCode + "%")
                    || r.RecordProducts.Any(rp => EF.Functions.Like(rp.ProductDefendant.Defendant.DefendantCode, "%" + input.DefendantCode + "%") && rp.IsActive == true));

下面的 sql where 子句中存在 any 子句和一些时髦的东西

SELECT [t].[Id], [t].[StartDate], [t].[EndDate], [t].[WitnessName], [t].[SourceCode], [t].[JobsiteName], [t].[ShipName], [t].[EmployerCode]
FROM (
    SELECT DISTINCT [r].[RecordID] AS [Id], [r].[StartDate], [r].[EndDate], [r.Witness].[FullName] AS [WitnessName], CASE
        WHEN [r].[SourceID] IS NOT NULL
        THEN [r.Source].[SourceCode] ELSE N'zzzzz'
    END AS [SourceCode], CASE
        WHEN [r].[JobsiteID] IS NOT NULL
        THEN [r.Jobsite].[JobsiteName] ELSE N'zzzzz'
    END AS [JobsiteName], CASE
        WHEN [r].[ShipID] IS NOT NULL
        THEN [r.Ship].[ShipName] ELSE N'zzzzz'
    END AS [ShipName], CASE
        WHEN [r].[EmployerID] IS NOT NULL
        THEN [r.Employer].[DefendantCode] ELSE N'zzzzz'
    END AS [EmployerCode]
    FROM [Records] AS [r]
    LEFT JOIN [Ships] AS [r.Ship] ON [r].[ShipID] = [r.Ship].[ShipID]
    LEFT JOIN [Jobsites] AS [r.Jobsite] ON [r].[JobsiteID] = [r.Jobsite].[JobsiteID]
    LEFT JOIN [Sources] AS [r.Source] ON [r].[SourceID] = [r.Source].[SourceID]
    LEFT JOIN [Witnesses] AS [r.Witness] ON [r].[WitnessID] = [r.Witness].[WitnessID]
    LEFT JOIN [Defendants] AS [r.Contractor] ON [r].[ContractorID] = [r.Contractor].[DefendantID]
    LEFT JOIN [Defendants] AS [r.Employer] ON [r].[EmployerID] = [r.Employer].[DefendantID]
    WHERE ([r].[IsActive] = 1) AND (([r.Employer].[DefendantCode] LIKE (N'%' + 'cert') + N'%' OR [r.Contractor].[DefendantCode] LIKE (N'%' + 'cert') + N'%') OR EXISTS (
        SELECT 1
        FROM [Records_Products] AS [rp]
        INNER JOIN [Product_Defendant] AS [rp.ProductDefendant] ON [rp].[DefendantProductID] = [rp.ProductDefendant].[DefendantProductID]
        INNER JOIN [Defendants] AS [rp.ProductDefendant.Defendant] ON [rp.ProductDefendant].[DefendantID] = [rp.ProductDefendant.Defendant].[DefendantID]
        WHERE ([rp.ProductDefendant.Defendant].[DefendantCode] LIKE (N'%' + 'cert') + N'%' AND ([rp].[IsActive] = 1)) AND ([r].[RecordID] = [rp].[RecordID])))
) AS [t]
ORDER BY [t].[SourceCode]
OFFSET 0 ROWS FETCH NEXT 500 ROWS ONLY

这是新的 sql 效果更好,只是不知道如何将其转换回 lambda 表达式

SELECT [t].[Id]
	,[t].[StartDate]
	,[t].[EndDate]
	,[t].[WitnessName]
	,[t].[SourceCode]
	,[t].[JobsiteName]
	,[t].[ShipName]
	,[t].[EmployerCode]
FROM (
	SELECT DISTINCT [r].[RecordID] AS [Id]
		,[r].[StartDate]
		,[r].[EndDate]
		,[r.Witness].[FullName] AS [WitnessName]
		,CASE 
			WHEN [r].[SourceID] IS NOT NULL
				THEN [r.Source].[SourceCode]
			ELSE N'zzzzz'
			END AS [SourceCode]
		,CASE 
			WHEN [r].[JobsiteID] IS NOT NULL
				THEN [r.Jobsite].[JobsiteName]
			ELSE N'zzzzz'
			END AS [JobsiteName]
		,CASE 
			WHEN [r].[ShipID] IS NOT NULL
				THEN [r.Ship].[ShipName]
			ELSE N'zzzzz'
			END AS [ShipName]
		,CASE 
			WHEN [r].[EmployerID] IS NOT NULL
				THEN [r.Employer].[DefendantCode]
			ELSE N'zzzzz'
			END AS [EmployerCode]
	FROM [Records] AS [r]
	LEFT JOIN [Ships] AS [r.Ship] ON [r].[ShipID] = [r.Ship].[ShipID]
	LEFT JOIN [Jobsites] AS [r.Jobsite] ON [r].[JobsiteID] = [r.Jobsite].[JobsiteID]
	LEFT JOIN [Sources] AS [r.Source] ON [r].[SourceID] = [r.Source].[SourceID]
	LEFT JOIN [Witnesses] AS [r.Witness] ON [r].[WitnessID] = [r.Witness].[WitnessID]
	LEFT JOIN [Defendants] AS [r.Contractor] ON [r].[ContractorID] = [r.Contractor].[DefendantID]
	LEFT JOIN [Defendants] AS [r.Employer] ON [r].[EmployerID] = [r.Employer].[DefendantID]
	LEFT JOIN (
		SELECT [rp].[RecordID]
		FROM [Records_Products] AS [rp]
		INNER JOIN [Product_Defendant] AS [rp.ProductDefendant] ON [rp].[DefendantProductID] = [rp.ProductDefendant].[DefendantProductID]
		INNER JOIN [Defendants] AS [rp.ProductDefendant.Defendant] ON [rp.ProductDefendant].[DefendantID] = [rp.ProductDefendant.Defendant].[DefendantID]
		WHERE (
				[rp.ProductDefendant.Defendant].[DefendantCode] LIKE (N'%' + 'cert') + N'%'
				AND ([rp].[IsActive] = 1)
				)
		) AS RecordProduct ON [r].[RecordID] = RecordProduct.[RecordID]
	WHERE ([r].[IsActive] = 1)
		AND (
			(
				[r.Employer].[DefendantCode] LIKE (N'%' + 'cert') + N'%'
				OR [r.Contractor].[DefendantCode] LIKE (N'%' + 'cert') + N'%'
				)
			OR RecordProduct.RecordID IS NOT NULL --OR EXISTS ( --    SELECT 1 --    FROM [Records_Products] AS [rp] --    INNER JOIN [Product_Defendant] AS [rp.ProductDefendant] ON [rp].[DefendantProductID] = [rp.ProductDefendant].[DefendantProductID] --    INNER JOIN [Defendants] AS [rp.ProductDefendant.Defendant] ON [rp.ProductDefendant].[DefendantID] = [rp.ProductDefendant.Defendant].[DefendantID] --    WHERE ([rp.ProductDefendant.Defendant].[DefendantCode] LIKE (N'%' + 'cert') + N'%'  -- AND ([rp].[IsActive] = 1)) AND ([r].[RecordID] = [rp].[RecordID]) -- )  )) AS [t]ORDER BY [t].[SourceCode]OFFSET 0 ROWS FETCH NEXT 500 ROWS ONLY
			)
	)

【问题讨论】:

    标签: entity-framework tsql lambda


    【解决方案1】:

    您提供的 linq 表达式与生成的 SQL 不匹配。一方面,linq 表达式正在对各种相关表执行Include,这些表将包含顶级 SELECT 中的所有实体列,这些列在您的示例 SQL 中不存在。我也没有在 Linq 表达式中看到 Take 500 & OrderBy 或记录上的 IsActive 断言的条件。

    为了能够帮助确定任何性能问题的根源,我们需要查看完整的 Linq 表达式和生成的 SQL。

    查看您提供的 Linq 表达式的基础:

    records = records
        .Include(r => r.Employer)
        .Include(r => r.Contractor)
        .Include(r => r.RecordProducts)
        .ThenInclude(rp => rp.ProductDefendant.Defendant)
        .Where(r => EF.Functions.Like(r.Employer.DefendantCode, "%" + input.DefendantCode + "%")
                    || EF.Functions.Like(r.Contractor.DefendantCode, "%" + input.DefendantCode + "%")
                    || r.RecordProducts.Any(rp => EF.Functions.Like(rp.ProductDefendant.Defendant.DefendantCode, "%" + input.DefendantCode + "%") && rp.IsActive == true));
    

    我可以提出一些建议:

    1. 不需要Functions.Like。您应该能够使用Contains 实现相同的目标。
    2. 避免使用Include,而是使用Select 从结果结构中检索您实际需要的列。将这些填充到 ViewModel 中或在代码中使用它们。您拉回的数据越少,SQL 对索引的优化就越好,通过网络拉取的数据就越少。随着系统成熟并且有人忘记Include 一个新关系,消费实体还会导致意外的延迟加载场景。

    .

    records = records
        .Where(r => r.IsActive
            && (r.Employer.DefendantCode.Contains(input.DefendantCode)
              || r.Contractor.DefendantCode.Contains(input.DefendantCode)
              || r.RecordProducts.Any(rp => rp.IsActive 
                  && rp.ProductDefendant.Defendant.DefendantCode.Contains(input.DefendantCode))
       .OrderBy(r => r.SourceCode)
       .Select(r => new RecordViewModel
       {
          // Populate the data you want here.
       }).Take(500).ToList();
    

    这还会根据您的示例 SQL 添加 IsActive 检查、OrderByTake(500)

    【讨论】:

    • 嗨史蒂夫。我几乎按照您所说的做了一切,但是当我必须通过“ProductDefendant”时,我的问题出在“rp.ProductDefendant.Defendant.DefendantCode”这一行上,与之前的性能问题一样,我从“rp”开始.Defendant”(直接发给被告)所以现在我正在浏览另一个表,我的性能要高得多~40 秒/查询
    • 所以在查看了生成的 SQL 之后,我的同事重写了它,现在我想将它转换回 lambda 表达式。我只是不知道如何
    • 如果 ProductDefendant 表的引入与性能下降有关,那么我怀疑数据库级别可能存在索引问题。您是否捕获了已执行的 SQL 并使用执行计划在 SSMS(假设为 SQL Server)中运行它们?如果 RecordProduct 最初有被告,为什么要引入 ProductDefendant 表?这样的事情会暗示产品和被告之间存在多对多的连接,但模型似乎与此不匹配。
    • 复杂关系的一个选项是创建一个视图来绑定一个新实体。您可以构造视图中返回的数据,然后为视图输出定义匹配实体。这适用于您想要汇总数据但不适用于编辑的情况。对于编辑,您将按 ID 加载适用的基于表格的实体。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-08-22
    • 2017-09-25
    • 1970-01-01
    • 2018-05-23
    • 1970-01-01
    • 1970-01-01
    • 2019-09-19
    相关资源
    最近更新 更多