簡體   English   中英

使用Union時如何防止生成外層Select語句?

[英]How to prevent outer Select statement from being generated when using Union?

我有以下內容:

                var peopleFromPhones = _context.Phones.Where(p => p.PhoneNumber.StartsWith(searchVM.Phone))
                .Join(_context.People,
                    phone => phone.ContactID,
                    person => person.ContactID,
                    (phone, person) => new Person()
                    {
                        FirstName = person.FirstName,
                        LastName = person.LastName
                    })
                    .Select(a => new
                    {
                        Name = a.FirstName + " " + a.LastName
                    })
            ;

            var orgsFromPhones = _context.Phones.Where(p => p.PhoneNumber.StartsWith(searchVM.Phone))
                .Join(_context.Organizations,
                    phone => phone.ContactID,
                    org => org.ContactID,
                    (phone, org) => new Organization()
                    {
                        BusinessName = org.BusinessName
                    })
                    .Select(b => new
                    {
                        Name = b.BusinessName
                    })


        var results = peopleFromPhones.Union(orgsFromPhones) //UNION HERE

        var items = results.Skip(skip).Take(searchVM.Rows).AsEnumerable()
                            .Select(x => new SearchContactResultVM()
                            {
                                Name = x.Name
                            })
                            .ToList();

但是當我查看生成的 SQL 時:

SELECT TOP(@__p_1) [t].[MemberNumber], [t].[Email], [t].[ContactId], [t].[PersOrgNbr], [t].[Name], [t].[DOB], [t].[SIN]
FROM (
    SELECT [c].[MemberNumber], [c].[Email], [p0].[ContactID] AS [ContactId], [p0].[PersNbr] AS [PersOrgNbr], ([p0].[FirstName] + N' ') + [p0].[LastName] AS [Name], CASE
        WHEN [p0].[DateOfBirth] IS NOT NULL THEN CONVERT(VARCHAR(100), [p0].[DateOfBirth])
        ELSE N''
    END AS [DOB], [p0].[SIN]
    FROM [Phones] AS [p]
    INNER JOIN [People] AS [p0] ON [p].[ContactID] = [p0].[ContactID]
    INNER JOIN [Contacts] AS [c] ON [p0].[ContactID] = [c].[ContactID]
    WHERE (@__searchVM_Phone_0 = N'') OR ([p].[PhoneNumber] IS NOT NULL AND (LEFT([p].[PhoneNumber], LEN(@__searchVM_Phone_0)) = @__searchVM_Phone_0))
    UNION ALL
    SELECT [c0].[MemberNumber], [c0].[Email], [o].[ContactID] AS [ContactId], [o].[OrgNbr] AS [PersOrgNbr], [o].[BusinessName] AS [Name], N'' AS [DOB], N'' AS [SIN]
    FROM [Phones] AS [p1]
    INNER JOIN [Organizations] AS [o] ON [p1].[ContactID] = [o].[ContactID]
    INNER JOIN [Contacts] AS [c0] ON [o].[ContactID] = [c0].[ContactID]
    WHERE (@__searchVM_Phone_0 = N'') OR ([p1].[PhoneNumber] IS NOT NULL AND (LEFT([p1].[PhoneNumber], LEN(@__searchVM_Phone_0)) = @__searchVM_Phone_0))
) AS [t]
ORDER BY [t].[Name]

有一個不必要的 Select 語句會妨礙 ORDER BY 的性能。 我發現如果我刪除外部 Select 查詢運行得更快。

有什么方法可以防止實體框架生成這個不必要的 Select 語句? 我相信它來自 Union()

編輯:這是我想要的更快的查詢:

SELECT TOP(@__p_1) [c].[MemberNumber], [c].[Email], [p0].[ContactID] AS [ContactId], [p0].[PersNbr] AS [PersOrgNbr], ([p0].[FirstName] + N' ') + [p0].[LastName] AS [Name], CASE
        WHEN [p0].[DateOfBirth] IS NOT NULL THEN CONVERT(VARCHAR(100), [p0].[DateOfBirth])
        ELSE N''
    END AS [DOB], [p0].[SIN]
    FROM [Phones] AS [p]
    INNER JOIN [People] AS [p0] ON [p].[ContactID] = [p0].[ContactID]
    INNER JOIN [Contacts] AS [c] ON [p0].[ContactID] = [c].[ContactID]
    WHERE (@__searchVM_Phone_0 = N'') OR ([p].[PhoneNumber] IS NOT NULL AND (LEFT([p].[PhoneNumber], LEN(@__searchVM_Phone_0)) = @__searchVM_Phone_0))
    UNION ALL
    SELECT [c0].[MemberNumber], [c0].[Email], [o].[ContactID] AS [ContactId], [o].[OrgNbr] AS [PersOrgNbr], [o].[BusinessName] AS [Name], N'' AS [DOB], N'' AS [SIN]
    FROM [Phones] AS [p1]
    INNER JOIN [Organizations] AS [o] ON [p1].[ContactID] = [o].[ContactID]
    INNER JOIN [Contacts] AS [c0] ON [o].[ContactID] = [c0].[ContactID]
    WHERE (@__searchVM_Phone_0 = N'') OR ([p1].[PhoneNumber] IS NOT NULL AND (LEFT([p1].[PhoneNumber], LEN(@__searchVM_Phone_0)) = @__searchVM_Phone_0))
ORDER BY [Name]

我在 SSMS 中對其進行了測試,它似乎正在排序結果列表,而不僅僅是聯盟的后半部分。

您還確定您的第二個查詢(編輯后)按預期工作嗎?

我在 SQL 中簡化並復制了相同的邏輯:

DECLARE @A AS TABLE ([Name] NVARCHAR(50));
DECLARE @B AS TABLE ([Name] NVARCHAR(50));

INSERT INTO @A VALUES ('a'), ('c'), ('e'), ('g');
INSERT INTO @B VALUES ('b'), ('d'), ('f'), ('h');

SELECT TOP(3) [Name]
    FROM @A
    UNION ALL
    SELECT [NAME]
    FROM @B
ORDER BY [Name]

您可以假設該值為:

Name
a
b
c

但實際上在回報:


Name
a
b
c
d
e
f
h

它從@A獲得前 3 名,然后應用了UNION ALL ,然后進行了排序,總體上它返回 7 行

您所說的“不必要的圍繞 Select”是什么意思。

您使用results.Skip(skip).Take(searchVM.Rows)調用進行的分頁查詢需要外部 select。

如果 SQL Select 部分在此查詢中是不必要的一件事:

SELECT TOP(@__p_1) [t].[MemberNumber], [t].[Email], [t].[ContactId], [t].[PersOrgNbr], [t].[Name], [t].[DOB], [t].[SIN]

您不需要這里的全部結果,您只需要為您獲取[t].[Name] C# SearchContactResultVM model。

要擺脫 rest SELECT列,請嘗試以下代碼:

     var items = results.Skip(skip).Take(searchVM.Rows)
                            .Select(n => n.Name).AsEnumerable()
                            .Select(name => new SearchContactResultVM()
                            {
                                Name = name
                            })
                            .ToList();

我擺脫了.AsEnumerable()調用,因此Select將應用於 SQL 查詢本身

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM