使用自连接在 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?我会尽快尝试您的解决方案。