【问题标题】:What is the Lambda Syntax to limit a subquery to the first row?将子查询限制在第一行的 Lambda 语法是什么?
【发布时间】:2018-08-08 23:04:22
【问题描述】:

我有一个包含 Item、Tax、TaxItem 和 ItemPrice 表的数据库。除了 ItemPrice 表之外,这些关系非常简单。在 ItemPrice 表中,给定的 ItemId 可以有多行,因为 Locations 可以覆盖在区域级别指定的价格。每条记录必须包含 RegionId 或 LocationId 但不能同时包含两者,并且两者都不能为空。

商品价格
身份证
物品编号
LocationId(可为空)
价格
RegionId(可为空)

项目
身份证
姓名

税收
身份证
名称
类型
价值

税项
物品编号
税号

我使用的 ABP 框架利用了存储库模式。我目前的代码如下:

var taxItems = _TaxItemRepository
.GetAllIncluding(ti => ti.Item, ti => ti.Tax, ti => ti.Item.ItemPrice)
.Where(ti =>
    (ti.ItemId == ItemId)
    &&
    (
        (ti.Item.ItemPrice.LocationId.HasValue && ti.Item.ItemPrice.LocationId == LocationId)
        ||
        (!ti.Item.ItemPrice.LocationId.HasValue && ti.Item.ItemPrice.RegionId == locationCacheItem.RegionId)
    )
);

很遗憾,它不等同于以下提供正确结果的 SQL:

declare @ItemId bigint, @LocationId bigint, @RegionId bigint
set @ItemId = 1
set @LocationId = 1
set @RegionId = 1


        select ti.TaxId as TaxId, ti.ApplyTax as ApplyTax, ip.Price, tx.Value as TaxValue, tx.SpecifyItems, tx.TaxType, tx.UseNetPrice
        from TaxItem ti 
        inner join Tax tx on tx.id = ti.TaxId
        inner join Item on ti.ItemId = Item.Id
        inner join 
        (   
            select top 1 locationId, regionId, ItemId, Price 
            from ItemPrice 
            where ItemId = @ItemId and 
            (
                (LocationId is not null and LocationId = @locationId) OR 
                (LocationId is null and RegionId = @RegionId) 
            )
        ) ip on ip.ItemId = Item.Id

ItemID 1 的 ItemPrice 表中有两条记录:

LocationId 价格 RegionId 1 1.25 空 空 1.50 1

如何获取 Lambda 表达式以将子查询限制在第一行?

【问题讨论】:

  • 您是否尝试过使用.First()
  • 是的。它将主要结果集限制为第一行,而不是子查询
  • SQL版本子查询中有“select Top 1”
  • 那么最明智的方法可能是将 C# 版本拆分为两个查询 - 首先执行“子查询”,将 .First() 的结果存储在一个临时变量中,然后在“主要”查询。
  • 只是一个小连接点:如果没有排序顺序,“第一”行的概念是危险的。在某些情况下(创建日期、ID 等)对人类来说可能是显而易见的,但是,您不应该依赖默认返回的顺序,因为它不能保证是确定性的。

标签: c# sql linq


【解决方案1】:

你不需要使用 GetAllIncluding 方法,因为它看起来包括整个集合......

【讨论】:

    【解决方案2】:

    我没有 ABP 框架,这是普通的 Linq:

    var taxItems = from ti in context.TaxItem
                   let ip = ti.Item.ItemPrice
                       .FirstOrDefault(x => x.LocationId == LocationId ||
                                      x.RegionId == locationCacheItem.RegionId)
                   select new {
                      TaxId = ti.TaxId, 
                      ApplyTax = ti.ApplyTax, 
                      Price = ip.Price, 
                      TaxValue = ti.Tax.Value, 
                      SpecifyItems = ti.Tax.SpecifyItems, 
                      TaxType = ti.Tax.TaxType, 
                      UseNetPrice = ti.Tax.UseNetPrice
                   };
    

    【讨论】:

      【解决方案3】:

      我认为 Abion47 的评论是关于钱的,但我通过升级到 EF Core 2.1.1 并使用 Query.FromSQL() 方法执行 SQL 解决了这个问题。我认为这是使用 Lambda 表示法无法轻松表达查询的情况之一,而使用 SQL 更有效地执行查询。

      var taxInfo = Context.Query<TaxItemInfo>().FromSql
        (
          $@"select ti.TaxId, ti.ApplyTax, ip.Price, tx.Value as TaxValue,   tx.SpecifyItems, tx.TaxType, tx.UseNetPrice
             from TaxItem ti 
             inner join Tax tx on tx.id = ti.TaxId 
             inner join Item on ti.ItemId = Item.Id 
             inner join 
             (
               select top 1 locationId,regionId,ItemId,Price from ItemPrice where  ItemId = {ItemId} and 
                 ( 
                   (LocationId is not null and LocationId = {LocationId}) OR 
                   (LocationId is null and RegionId = {RegionId}) 
                  ) 
              ) ip on ip.ItemId = Item.Id"
        );
      

      【讨论】:

      • 但是ti.Item.ItemPrice 的类型是什么? SQL 查询表明 Item 有多个 ItemPrices,因此它应该是一个集合。我想知道ti.Item.ItemPrice.LocationId 是否还能编译。此外,您可能毕竟使用过 LINQ,但声明不同。
      猜你喜欢
      • 2023-03-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-12-05
      相关资源
      最近更新 更多