【发布时间】:2020-11-09 07:31:28
【问题描述】:
我对 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 倍。
【问题讨论】:
-
你在使用
entity framework吗?如果是,是哪个版本? & 也分享相应的Models。 -
现在我只在 Linqpad 中使用 linq 表达式直接针对上面的表格工作,因此没有适用于它们的 EFcontexts 或模型。
-
有,是Linqpad生成的。看看类属性,尤其是。导航属性并做一些初步尝试。这里没有真正的问题。你只说你想要的。
-
@GertArnold,我已经更新了未分组的 linq 表达式,但我不知道如何对数据进行分组
-
正如经常发生的那样,您使用 SQL 思维攻击它并立即键入连接。首先设计一个具有主键和外键的适当数据库模型。然后,Linq-to-sql(Linqpad 使用)将生成具有导航属性的模型。如果你达到了这一点,请尝试使用这些属性编写一些查询并看看它们做了什么。然后你会发现你可以简单地做
p.Addresses.Count()之类的事情。
标签: c# sql-server linq