【问题标题】:Query in LINQ with self join使用自联接在 LINQ 中查询
【发布时间】:2016-04-26 11:26:15
【问题描述】:

我有一个表,其中包含诸如 Id 作为主键和 LastMeterReadingId(引用同一表的外键)之类的列 - 类似于 Parent Meter Reading。

我想获取所有尚未使用的行,例如 Parent。我想避免当抄表是超过一个抄表的父母的情况。

我知道如何加入同一张表,但我不知道如何只选择那些还不是父记录的记录。这就是没有条件语句的查询的样子。

return (from m in uow.MeterReadingReadWriteRepository.Query()
                join parent in uow.MeterReadingReadWriteRepository.Query() on m.Id equals parent.LastMeterReadingId

                select new MeterReadingDto()
                {
                    (...)
                }).ToList();

您知道如何以有效的方式实现它吗?

问候。

【问题讨论】:

  • 通常像这样的情况我写一个递归方法来枚举表。 Linq 只适合父子加入,但多代 LINQ 效果不佳。
  • 首先我认为这违反了正常形式。因为不是抄表是否是另一个信息的子项,可以简单地通过按抄表日期排序来获得。如果您将它们链接在一起并且有人删除了链中间的记录,也会发生什么情况。在那种情况下你是不是有点搞砸了?

标签: c# sql linq unit-of-work


【解决方案1】:

我想获取所有尚未使用的行,如 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 的查询。而最后一个被转换为基于“传统”SQL LEFT JOIN ... WHERE right.PK IS NULL 的查询。

【讨论】:

    【解决方案2】:

    你可以添加

    where !(from child in uow.MeterReadingReadWriteRepository.Query() where child.Id == m.LastMeterReadingId select child).Any()

    但不确定这会优化到何种程度。排除 uow.MeterReadingReadWriteRepository.Query() 也会更好。

    您的抄表实体中没有外键约束的子关系/集合吗? - 这将使查询更加直接。

    【讨论】:

      【解决方案3】:
      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();
      

      【讨论】:

        【解决方案4】:

        感谢@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?我会尽快尝试您的解决方案。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-07-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多