创建一个子查询以过滤窗口函数值

Create a subquery to filter on windowed function value

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

添加自定义函数后,它在 Select 中工作正常但在 Where 中不起作用,因为格式错误 SQL.

林克:

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 中执行此操作的文章。

Probably, the easiest way is to introduce a method that gives EF a hint that the previous query should be a sub query. Fortunately, we don't have do much because internally the method AsQueryable (or rather the expression associated with it) does just that.

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

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

有没有办法创建子查询?

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

从两个可能的fake limit operators(Skip(0)Take(int.MaxValue))来看,选后者比较好,因为前者也需要一定的排序(甚至是fake)

所以解决方法是插入

.Take(int.MaxValue)

.Where(...) 之前。

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