преобразование SQL-оператора обратно в лямбда-выражение - PullRequest
0 голосов
/ 20 июня 2019

У меня есть запрос ниже, и его SQL-код. Он работает очень медленно, поэтому он был переписан в SQL, теперь я просто не уверен, как преобразовать SQL обратно в лямбда-выражение.

Это часть выражения, дающая мне проблемы, где-то в

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, который работает лучше, но не уверен, как преобразовать его обратно в лямбда-выражение

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 голосов
/ 21 июня 2019

Указанное вами выражение 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 для извлечения столбцов из результирующей структуры, которая вам действительно нужна.Заполните их в 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();

Это также добавляет проверку IsActive,OrderBy и Take(500) на основе вашего примера SQL.

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