将sql语句转换回lambda表达式

时间:2019-06-20 18:52:45

标签: entity-framework tsql lambda

我有以下查询及其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));

any子句在下面的sql where子句中确实存在并且有些时髦的东西

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
			)
	)

1 个答案:

答案 0 :(得分:0)

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

为了能够帮助确定性能问题的根源,我们需要查看完整的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从实际需要的结果结构中检索列。将它们填充到ViewModels或在代码中使用它们。拉回的数据越少,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)