具有多个联接和组的Linq查询

时间:2020-11-09 07:31:28

标签: c# sql-server linq

我对linq表达式很陌生,并且正在尝试得出此tsql的等效linq版本:

SELECT 
    p.Name, 
    Count(a.Street) [Number of addresses], 
    c.ContractType, 
    COUNT(cv.ContractVersionId) Number, 
    MAX(cv.ChangedDate) [Last change date] 
From Person p 
LEFT JOIN Address a ON p.PersonId = a.PersonId AND a.ChangedDate IS NULL
LEFT JOIN Contract c ON p.PersonId = c.PersonId
LEFT JOIN ContractVersion cv ON c.ContractId = cv.ContractId

GROUP BY p.Name,  c.ContractType

这将产生输出:

Name    Number of addresses ContractType    Number  Last change date
Bob     1                   NULL            0       NULL
Allice  1                   Buy             1       2020-10-31 00:00:00.000
Karen   0                   Buy             3       2020-09-01 00:00:00.000
Peter   3                   Lease           3       2020-07-10 00:00:00.000
Tom     5                   Lease           5       2020-09-14 00:00:00.000
Allice  3                   Rent            3       2020-05-10 00:00:00.000

要复制的数据:

IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[Person]') AND type in (N'U'))
DROP TABLE [dbo].Person
GO
IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[Address]') AND type in (N'U'))
DROP TABLE [dbo].Address
GO
IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[Contract]') AND type in (N'U'))
DROP TABLE [dbo].Contract
GO
IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[ContractVersion]') AND type in (N'U'))
DROP TABLE [dbo].ContractVersion
GO
CREATE TABLE Person (
        PersonId Uniqueidentifier NOT NULL,
        Name NVARCHAR(100) NOT NULL
    )
CREATE TABLE Address(
        AddressId Uniqueidentifier NOT NULL,
        PersonId Uniqueidentifier NOT NULL,
        Street NVARCHAR(100) NOT NULL,
        ChangedDate DATETIME NULL
    )
CREATE TABLE Contract (
        ContractId Uniqueidentifier NOT NULL,
        PersonId Uniqueidentifier NOT NULL,
        ContractType NVARCHAR(100) NOT NULL
    )
CREATE TABLE ContractVersion (
        ContractVersionId Uniqueidentifier NOT NULL,
        ContractId Uniqueidentifier NOT NULL,
        ContractText NVARCHAR(100) NOT NULL,
        ChangedDate DATETIME NULL
    )


INSERT INTO Person
VALUES
    ('{B8A97537-AF09-45FD-A723-1BF43796DADA}','Tom'),
    ('{124A34C0-1AB3-4449-AEE2-04ED2732B8ED}','Allice'),
    ('{E501CC32-C587-43C7-B0DD-096F04EE80AA}','Peter'),
    ('{9D1E37BE-D032-45CC-AA1D-06ECBB9C59EA}','Karen'),
    ('{049B5985-F4D0-448C-9391-8E08495DF61F}','Bob')
INSERT INTO Address
VALUES  
    ('{AF3BE020-6851-46E1-9D04-9959C665A80D}','{B8A97537-AF09-45FD-A723-1BF43796DADA}','Last address 1',NULL),
    ('{A0957F82-8922-452C-AA1B-4EA05A1B121B}','{124A34C0-1AB3-4449-AEE2-04ED2732B8ED}','Last address 2',NULL),
    ('{10D60EDC-75B3-412F-AA0B-962746EB72BD}','{E501CC32-C587-43C7-B0DD-096F04EE80AA}','Last address 3',NULL),
    ('{93A10053-5059-4127-8EA1-C0DF77760E84}','{049B5985-F4D0-448C-9391-8E08495DF61F}','Last address 4',NULL),
    ('{F1E838FE-7074-4AB3-8A4D-0895CEB98362}','{B8A97537-AF09-45FD-A723-1BF43796DADA}','D','2020-01-01'),
    ('{4CB0D80D-7F74-4755-9C1A-08A03EE88B4B}','{124A34C0-1AB3-4449-AEE2-04ED2732B8ED}','D','2020-01-01'),
    ('{B42DE51A-3EAD-4AAC-B5DC-75493FE214E5}','{E501CC32-C587-43C7-B0DD-096F04EE80AA}','D','2020-01-01')

INSERT INTO Contract
VALUES  
    ('{64989F02-793E-4313-9324-4916D6C5D610}','{B8A97537-AF09-45FD-A723-1BF43796DADA}','Lease'),
    ('{0101F8CD-E72E-4D9B-A8BD-A94BBDB39740}','{124A34C0-1AB3-4449-AEE2-04ED2732B8ED}','Rent'),
    ('{D1F94A9C-0718-4E18-B63F-B506C5E2C70E}','{E501CC32-C587-43C7-B0DD-096F04EE80AA}','Lease'),
    ('{A8397E6D-597A-4024-91DC-98EA0D9FF848}','{9D1E37BE-D032-45CC-AA1D-06ECBB9C59EA}','Buy'),
    ('{D47E82B3-2D41-4CCB-BD6F-003BD9C49BEA}','{B8A97537-AF09-45FD-A723-1BF43796DADA}','Lease'),
    ('{5EC4E588-00A5-442B-BD7B-0C9FD27E8076}','{124A34C0-1AB3-4449-AEE2-04ED2732B8ED}','Buy')
    
INSERT INTO ContractVersion
VALUES  
    ('{E3E26DE5-F00A-4BEB-9A48-72AE9EA29F2E}','{64989F02-793E-4313-9324-4916D6C5D610}','Created','2020-01-01'),
    ('{C5EFAB2E-EE41-4A10-A708-44CA19E505A9}','{0101F8CD-E72E-4D9B-A8BD-A94BBDB39740}','Created','2020-05-01'),
    ('{43EDFE20-B1EF-4236-8D4E-478F445EC327}','{D1F94A9C-0718-4E18-B63F-B506C5E2C70E}','Created','2020-06-03'),
    ('{16870733-0EC3-497B-BA51-770C921A245A}','{A8397E6D-597A-4024-91DC-98EA0D9FF848}','Created','2020-08-15'),
    ('{93CAEA22-D796-4237-A706-47C3D6AE8700}','{D47E82B3-2D41-4CCB-BD6F-003BD9C49BEA}','Created','2020-09-12'),
    ('{9D003A79-1376-4512-922F-88A33EC72CCB}','{5EC4E588-00A5-442B-BD7B-0C9FD27E8076}','Created','2020-10-31'),
    ('{8BD9E556-BB56-4404-A7C2-C6C06D321127}','{64989F02-793E-4313-9324-4916D6C5D610}','Reviewed','2020-01-05'),
    ('{E46D1C7E-94BF-43BC-883D-7960CE719C07}','{0101F8CD-E72E-4D9B-A8BD-A94BBDB39740}','Reviewed','2020-05-01'),
    ('{0B5B594C-78BF-49B7-834B-3E9A617A16F9}','{D1F94A9C-0718-4E18-B63F-B506C5E2C70E}','Reviewed','2020-06-08'),
    ('{931C3910-DFD6-4EB4-9826-9518B3C6BC54}','{A8397E6D-597A-4024-91DC-98EA0D9FF848}','Reviewed','2020-08-16'),
    ('{6F97D07E-9AAD-488E-87D3-D2A886547B1C}','{D47E82B3-2D41-4CCB-BD6F-003BD9C49BEA}','Reviewed','2020-09-14'),
    ('{BD3B3413-8853-4199-BC93-B23D49F77A9C}','{64989F02-793E-4313-9324-4916D6C5D610}','Finalized','2020-04-01'),
    ('{5DA560DE-69D7-4244-A320-AF3FE4135B22}','{0101F8CD-E72E-4D9B-A8BD-A94BBDB39740}','Finalized','2020-05-10'),
    ('{8392D528-438C-4F82-8BAC-69C1913CF610}','{D1F94A9C-0718-4E18-B63F-B506C5E2C70E}','Finalized','2020-07-10'),
    ('{0DC060B3-12F0-4927-8526-57E87C0150EA}','{A8397E6D-597A-4024-91DC-98EA0D9FF848}','Finalized','2020-09-01')
    

SELECT 
    p.Name, 
    Count(a.Street) [Number of addresses], 
    c.ContractType, 
    COUNT(cv.ContractVersionId) Number, 
    MAX(cv.ChangedDate) [Last change date] 
From Person p 
LEFT JOIN Address a ON p.PersonId = a.PersonId AND a.ChangedDate IS NULL
LEFT JOIN Contract c ON p.PersonId = c.PersonId
LEFT JOIN ContractVersion cv ON c.ContractId = cv.ContractId

GROUP BY p.Name,  c.ContractType

感谢所有帮助!

更新: 我可以生成此linq表达式,但是字体却使我无法进行分组,因为数据位于不同的表中。

from p in Persons
join a in Addresses on new { p.PersonId} equals new { a.PersonId} into addressGroup from a in addressGroup.DefaultIfEmpty()
join c  in Contracts on p.PersonId equals c.PersonId into contractGroups from c in contractGroups.DefaultIfEmpty()
join cv in ContractVersions on c.ContractId equals cv.ContractId

select new {p.Name, a.Street, c.ContractType, cv.ChangedDate}

更新2:

我在下面尝试了@jdweng查询,结果生成的sql像这样:

SELECT [t5].[Name] AS [name], (
    SELECT COUNT(*)
    FROM (
        SELECT NULL AS [EMPTY]
        FROM (
            SELECT [t7].[Street] AS [value], [t6].[Name], [t8].[ContractType]
            FROM [Person] AS [t6]
            LEFT OUTER JOIN [Address] AS [t7] ON [t6].[PersonId] = [t7].[PersonId]
            LEFT OUTER JOIN [Contract] AS [t8] ON [t6].[PersonId] = [t8].[PersonId]
            INNER JOIN [ContractVersion] AS [t9] ON [t8].[ContractId] = [t9].[ContractId]
            ) AS [t10]
        WHERE ([t5].[Name] = [t10].[Name]) AND ((([t5].[value] IS NULL) AND ([t10].[ContractType] IS NULL)) OR (([t5].[value] IS NOT NULL) AND ([t10].[ContractType] IS NOT NULL) AND ((([t5].[value] IS NULL) AND ([t10].[ContractType] IS NULL)) OR (([t5].[value] IS NOT NULL) AND ([t10].[ContractType] IS NOT NULL) AND ([t5].[value] = [t10].[ContractType])))))
        GROUP BY [t10].[value]
        ) AS [t11]
    ) AS [numberOfAddresses], [t5].[value] AS [contractType], (
    SELECT COUNT(*)
    FROM (
        SELECT NULL AS [EMPTY]
        FROM [Person] AS [t12]
        LEFT OUTER JOIN [Address] AS [t13] ON [t12].[PersonId] = [t13].[PersonId]
        LEFT OUTER JOIN [Contract] AS [t14] ON [t12].[PersonId] = [t14].[PersonId]
        INNER JOIN [ContractVersion] AS [t15] ON [t14].[ContractId] = [t15].[ContractId]
        WHERE ([t5].[Name] = [t12].[Name]) AND ((([t5].[value] IS NULL) AND ([t14].[ContractType] IS NULL)) OR (([t5].[value] IS NOT NULL) AND ([t14].[ContractType] IS NOT NULL) AND ((([t5].[value] IS NULL) AND ([t14].[ContractType] IS NULL)) OR (([t5].[value] IS NOT NULL) AND ([t14].[ContractType] IS NOT NULL) AND ([t5].[value] = [t14].[ContractType])))))
        GROUP BY [t15].[ContractVersionId]
        ) AS [t16]
    ) AS [number]
FROM (
    SELECT [t4].[Name], [t4].[value]
    FROM (
        SELECT [t0].[Name], [t2].[ContractType] AS [value]
        FROM [Person] AS [t0]
        LEFT OUTER JOIN [Address] AS [t1] ON [t0].[PersonId] = [t1].[PersonId]
        LEFT OUTER JOIN [Contract] AS [t2] ON [t0].[PersonId] = [t2].[PersonId]
        INNER JOIN [ContractVersion] AS [t3] ON [t2].[ContractId] = [t3].[ContractId]
        ) AS [t4]
    GROUP BY [t4].[Name], [t4].[value]
    ) AS [t5]

几乎可以产生我想要的结果,并且花费的时间是原始tsql的20倍。

2 个答案:

答案 0 :(得分:1)

尝试关注

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            Context db = new Context();

            var results = (from p in db.Person
                           join a in db.Address on p.PersonId equals a.PersonId
                           join c in db.Contract on p.PersonId equals c.PersonId
                           join cv in db.ContractVersion on c.ContractId equals cv.ContractId
                           select new { p = p, a = a, c = c, cv = cv}
                           ).GroupBy(x => new { name = x.p.Name, contractType = x.c.ContractType })
                           .Select(x => new { 
                               name = x.Key.name, 
                               numberOfAddresses = x.GroupBy(y => y.a.Street).Count(),
                               contractType = x.Key.contractType,
                               number = x.GroupBy(y => y.cv.ContractVersionId).Count(),
                               lastChangeDate = x.OrderByDescending(y => y.cv.ChangedDate).FirstOrDefault().cv.ChangedDate
                           })
                           .ToList();
        }
    }
    public class Context
    {
        public List<Person> Person { get; set; }
        public List<Address> Address { get; set; }
        public List<Contract> Contract { get; set; }
        public List<ContractVersion> ContractVersion { get; set; }

    }
    public class Person
    {
        public string Name { get; set; }
        public string PersonId { get; set; }
    }
    public class Address
    {
        public string PersonId { get; set; }
        public string Street { get; set; }
        public DateTime ChangedDate { get; set; }

    }
    public class Contract
    {
        public string PersonId { get; set; }
        public string ContractType { get; set; }
        public string ContractId { get; set; }
    }
    public class ContractVersion
    {
        public string ContractId { get; set; }
        public DateTime ChangedDate { get; set; }
        public string ContractVersionId { get; set; }
    }
}

这是第二条查询,运行速度可能更快:

            var results =  db.Person.Select(p => new {
                Person = p,
                Address = db.Address.Where(a => p.PersonId == a.PersonId),
                Contract = db.Contract.Where(c => p.PersonId == c.PersonId),
                ContractVersion = db.ContractVersion.Where(cv => p.PersonId == cv.ContractId)
            }).GroupBy(x => new { name = x.Person.Name, contractType = x.Contract.FirstOrDefault().ContractType}) 
                           .Select(x => new
                           {
                               name = x.Key.name,
                               numberOfAddresses = x.Select(y => y.Address.SelectMany(z => z.Street)).Count(),
                               contractType = x.Key.contractType,
                               number = x.GroupBy(y => y.ContractVersion.SelectMany(z => z.ContractVersionId)).Count(),
                               lastChangeDate = x.SelectMany(y => y.ContractVersion.Select(z => z.ChangedDate)).OrderByDescending(y => y).FirstOrDefault()
                           })
                           .ToList();

答案 1 :(得分:1)

不确定EF Core如何有条件地支持Count,因此仅使用Sum进行仿真。

var query =
   from p in Persons
   join a in Addresses on p.PersonId equals a.PersonId into addressGroup 
   from a in addressGroup.DefaultIfEmpty()
   join c in Contracts on p.PersonId equals c.PersonId into contractGroups 
   from c in contractGroups.DefaultIfEmpty()
   join cv in ContractVersions on c.ContractId equals cv.ContractId in versionsGroup
   from cv in versionsGroup.DefaultIfEmpty()
   group new { a, cv } by new { p.Name,  c.ContractType } into g
   select new 
   {
       g.Key.Name, 
       NumberOfAddresses = g.Sum(x => x.a.Street != null ? 1 : 0), 
       g.Key.ContractType, 
       Number = g.Sum(x => x.cv.ContractVersionId != null ? 1 : 0), 
       LastChangeDate = g.Max(x => x.cv.ChangedDate)
   }