使用自连接在 LINQ 中查询
Query in LINQ with self join
我有一个 table,其中包含其他列,例如作为主键的 Id 和 LastMeterReadingId(引用相同 table 的外键)- 类似于 Parent Meter Reading。
我想获取所有尚未使用的行,如 Parent。我想避免仪表读数超过一个仪表读数的情况。
我知道如何加入相同的 table,但我不知道如何只选择那些还不是父级的记录。这就是没有条件语句的查询的样子。
return (from m in uow.MeterReadingReadWriteRepository.Query()
join parent in uow.MeterReadingReadWriteRepository.Query() on m.Id equals parent.LastMeterReadingId
select new MeterReadingDto()
{
(...)
}).ToList();
你知道如何有效地实现它吗?
此致。
您可以添加
where !(from child in uow.MeterReadingReadWriteRepository.Query() where child.Id == m.LastMeterReadingId select child).Any()
虽然不确定这会被优化到什么程度。分解 uow.MeterReadingReadWriteRepository.Query().
也会更好
您的 Meter Reading 实体中没有来自外键约束的子 relationship/collection 吗? - 这将使查询更加直接。
var readings = uow.MeterReadingReadWriteRepository.Query();
var parents = readings
.Join(readings, child => child.Id, parent => parent.LastMeterReadingId,
(child, parent) => new {parent.Id})
.Distinct()
.ToDictionary(a => a.Id);
var result = (from m in readings
where !parents.Contains(m.Id)
select new
{
Id = m.Id
}).ToList();
I would like to get all rows which are not already used like Parent
换句话说,您需要所有没有子项的行。请注意,查询中的变量名称 parent
具有误导性 - 当您执行 a join b on a.Id equals b.ParentId
时,a
是父项而 b
是子项。
无论如何,至少有 3 种方法可以实现您的目标,从当今数据库查询优化器的角度来看,IMO 是等效的(即应该同样有效):
(1) 使用 !Any(...)
相当于 SQL NOT EXISTS(...)
:
from m in uow.MeterReadingReadWriteRepository.Query()
where !uow.MeterReadingReadWriteRepository.Query().Any(child => m.Id == child.LastMeterReadingId)
select ...
(2) 使用 group join:
from m in uow.MeterReadingReadWriteRepository.Query()
join child in uow.MeterReadingReadWriteRepository.Query()
on m.Id equals child.LastMeterReadingId into children
where !children.Any()
select ...
(3) 使用左外反连接:
from m in uow.MeterReadingReadWriteRepository.Query()
join child in uow.MeterReadingReadWriteRepository.Query()
on m.Id equals child.LastMeterReadingId into children
from child in children.DefaultIfEmpty()
where child == null
select ...
如果这是 EF (LINQ to Entities),前两个将转换为一个相同的基于 SQL NOT EXISTS
的查询。而最后一个被翻译成基于"traditional" SQL LEFT JOIN ... WHERE right.PK IS NULL
的查询。
感谢@Ben Jackson
public class MeterReading : EntityBase
{
public long PropertyId { get; set; }
public long? LastMeterReadingId { get; set; }
public long? PaymentId { get; set; }
public Property Property { get; set; }
public MeterReading LastReading { get; set; }
public Payment Payment { get; set; }
}
这就是大多数价值属性的样子。也许我应该使用 T-SQL 查询和 JOIN 到条件语句之前提到的 CTE?我会尽快尝试您的解决方案。
我有一个 table,其中包含其他列,例如作为主键的 Id 和 LastMeterReadingId(引用相同 table 的外键)- 类似于 Parent Meter Reading。
我想获取所有尚未使用的行,如 Parent。我想避免仪表读数超过一个仪表读数的情况。
我知道如何加入相同的 table,但我不知道如何只选择那些还不是父级的记录。这就是没有条件语句的查询的样子。
return (from m in uow.MeterReadingReadWriteRepository.Query()
join parent in uow.MeterReadingReadWriteRepository.Query() on m.Id equals parent.LastMeterReadingId
select new MeterReadingDto()
{
(...)
}).ToList();
你知道如何有效地实现它吗?
此致。
您可以添加
where !(from child in uow.MeterReadingReadWriteRepository.Query() where child.Id == m.LastMeterReadingId select child).Any()
虽然不确定这会被优化到什么程度。分解 uow.MeterReadingReadWriteRepository.Query().
也会更好您的 Meter Reading 实体中没有来自外键约束的子 relationship/collection 吗? - 这将使查询更加直接。
var readings = uow.MeterReadingReadWriteRepository.Query();
var parents = readings
.Join(readings, child => child.Id, parent => parent.LastMeterReadingId,
(child, parent) => new {parent.Id})
.Distinct()
.ToDictionary(a => a.Id);
var result = (from m in readings
where !parents.Contains(m.Id)
select new
{
Id = m.Id
}).ToList();
I would like to get all rows which are not already used like Parent
换句话说,您需要所有没有子项的行。请注意,查询中的变量名称 parent
具有误导性 - 当您执行 a join b on a.Id equals b.ParentId
时,a
是父项而 b
是子项。
无论如何,至少有 3 种方法可以实现您的目标,从当今数据库查询优化器的角度来看,IMO 是等效的(即应该同样有效):
(1) 使用 !Any(...)
相当于 SQL NOT EXISTS(...)
:
from m in uow.MeterReadingReadWriteRepository.Query()
where !uow.MeterReadingReadWriteRepository.Query().Any(child => m.Id == child.LastMeterReadingId)
select ...
(2) 使用 group join:
from m in uow.MeterReadingReadWriteRepository.Query()
join child in uow.MeterReadingReadWriteRepository.Query()
on m.Id equals child.LastMeterReadingId into children
where !children.Any()
select ...
(3) 使用左外反连接:
from m in uow.MeterReadingReadWriteRepository.Query()
join child in uow.MeterReadingReadWriteRepository.Query()
on m.Id equals child.LastMeterReadingId into children
from child in children.DefaultIfEmpty()
where child == null
select ...
如果这是 EF (LINQ to Entities),前两个将转换为一个相同的基于 SQL NOT EXISTS
的查询。而最后一个被翻译成基于"traditional" SQL LEFT JOIN ... WHERE right.PK IS NULL
的查询。
感谢@Ben Jackson
public class MeterReading : EntityBase
{
public long PropertyId { get; set; }
public long? LastMeterReadingId { get; set; }
public long? PaymentId { get; set; }
public Property Property { get; set; }
public MeterReading LastReading { get; set; }
public Payment Payment { get; set; }
}
这就是大多数价值属性的样子。也许我应该使用 T-SQL 查询和 JOIN 到条件语句之前提到的 CTE?我会尽快尝试您的解决方案。