首页 > 解决方案 > 创建子查询以过滤窗口函数值

问题描述

我正在尝试将 ROW_NUMBER 功能添加到 EF Core 并通过它进行过滤。

添加自定义函数后,由于 SQL 格式错误,它可以正常工作,Select但不能正常工作。Where

林克:

var query = dbContext.OrderItems
    .Select(i => new
    {  
        i.Name,
        RowNumber = EF.Functions.RowNumber(i.ProductId)
    })
    .Where(i => i.RowNumber == 1);

翻译成:

SELECT
   i.NAME,
   ROW_NUMBER() OVER(ORDER BY i.ProductId) AS RowNumber
FROM
   OrderItems AS i
WHERE
   ROW_NUMBER() OVER(ORDER BY i.ProductId) = CAST(1 AS bigint)

错误:

Microsoft.Data.SqlClient.SqlException (0x80131904): Windowed functions can only appear in the SELECT or ORDER BY clauses.

要更正此 SQL,我需要创建一个子查询:

SELECT
  t.NAME,
  t.RowNumber
FROM (
   SELECT
      i.NAME,
      ROW_NUMBER() OVER(ORDER BY i.ProductId) AS RowNumber
   FROM
      OrderItems AS i
) t
WHERE
  t.RowNumber = CAST(1 AS bigint)

我在 EF Core 2 中找到了一篇关于如何执行此操作的文章。

可能,最简单的方法是引入一种方法,该方法给 EF 一个提示,即先前的查询应该是子查询。幸运的是,我们没有做太多事情,因为在内部,方法 AsQueryable(或者更确切地说与其关联的表达式)就是这样做的。

https://www.thinktecture.com/en/entity-framework-core/making-rownumber-more-useful-in-2-1/

但是这种方法在 EF Core 3.1 中没有任何作用

有没有办法创建子查询?

标签: c#entity-framework-coreef-core-3.1entity-framework-core-3.1

解决方案


查看 EF Core 3.1 源代码,我看到在应用where过滤器之前强制子查询的唯一方法是引入查询限制(即Skip和/或Take)。

从两个可能的假极限运算符(Skip(0)Take(int.MaxValue))中,看起来选择后者更好,因为前者也需要一些排序(甚至是假的)。

所以解决方法是插入

.Take(int.MaxValue)

之前.Where(...)

生成的 SQL 并不完美(有 fakeTOP子句),但至少是有效的。


推荐阅读