具有 Row_Num 的子 select 能否始终模拟 window 函数?

Can a sub-select with Row_Num always emulate a window function?

如果将 row_number 添加到每一行,是否可以模拟 analytic 函数可以对子选择执行的所有操作?例如:

WITH res(id, year, sales) as (
    SELECT 'Apple', 2010, 100 UNION SELECT 'Apple', 2011, 150 UNION SELECT 'Apple', 2012, 120 UNION
    SELECT 'Google', 2010, 301 UNION SELECT 'Google', 2011, 400 UNION SELECT 'Google', 2011, 450
),
res_with_row AS (
    SELECT *, ROW_NUMBER() OVER () AS row_num FROM res
)
SELECT *
    ,LAG(sales) OVER (PARTITION BY id ORDER BY YEAR) sales_last_year_analytic
    ,(SELECT sales FROM res_with_row as _inner 
           WHERE _inner.row_num=res_with_row.row_num-1 -- emulate LAG
           AND _inner.id=res_with_row.id               -- emulate PARITION BY id
        ) sales_last_year_subselect
FROM res_with_row;

或者,如果用户有权访问该条目的 rowid,是否有任何 analytic 函数可以执行但无法通过相关子查询模拟的操作?


注意:我用三个数据库后端标记了它。我可以访问所有三个,语法应该适用于所有。如果有人认为它更适合删除这些标签,请随时这样做。

可以模拟 window 函数 - Stack Overflow 中有很多针对 SQL Server 2008 R2 及更早版本的问题,没有这些问题就可以解决,因为它们不受支持。

window 函数与性能和更好的可读性有关。如果您比较示例的执行计划(SQL 服务器):

WITH res(id, year, sales) as (
    SELECT 'Apple', 2010, 100 UNION SELECT 'Apple', 2011, 150 UNION SELECT 'Apple', 2012, 120 UNION
    SELECT 'Google', 2010, 301 UNION SELECT 'Google', 2011, 400 UNION SELECT 'Google', 2011, 450
),
res_with_row AS (
    SELECT *, ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS row_num FROM res
)
SELECT *
    ,LAG(sales) OVER (PARTITION BY id ORDER BY YEAR) sales_last_year_analytic
  
FROM res_with_row;



WITH res(id, year, sales) as (
    SELECT 'Apple', 2010, 100 UNION SELECT 'Apple', 2011, 150 UNION SELECT 'Apple', 2012, 120 UNION
    SELECT 'Google', 2010, 301 UNION SELECT 'Google', 2011, 400 UNION SELECT 'Google', 2011, 450
),
res_with_row AS (
    SELECT *, ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS row_num FROM res
)
SELECT *
     ,(SELECT sales FROM res_with_row as _inner 
           WHERE _inner.row_num=res_with_row.row_num-1 -- emulate LAG
           AND _inner.id=res_with_row.id               -- emulate PARITION BY id
        ) sales_last_year_subselect
FROM res_with_row;

看看第二个有多复杂(没有使用 window 函数)。想象一下您需要使用 window 函数计算多个指标的查询。

此外,在某些情况下,无法在单个查询的上下文中模拟它们。我经常看到遗留代码,其中 ordering 是通过单独的 table 完成的。例如:

  1. 我们有 RowID 列显示行排序方式的记录

  2. 我们删除第5条和第8条记录。

  3. 现在,为了添加新的顺序 RowID,开发人员使用了这样的东西:

    DECLARE @DataSource TABLE
    (
        [PrimaryKeyColumn] INT
       ,[RowID] INT IDENTITY(1,1)
    );
    
    INSERT INTO @DataSource ([PrimaryKeyColumn])
    SELECT [PrimaryKeyColumn]
    FROM [my_table]
    ORDER BY [RowID]
    
    UPDATE [my_table]
    SET [RowID] = DS.[RowID]
    FROM [my_table] T
    INNER JOIN @DataSource DS
        ON T.[PrimaryKeyColumn] = DS.[PrimaryKeyColumn];
    

上面的内容看起来很奇怪,因为我们有 INSERTORDER BY。用值生成 RowID 似乎更好。这是基于 当有 IDENTITY 列时 ORDER BY 不会被忽略。

此外,对数据库中的行进行排序,而不是在提取数据时排序似乎是错误的,但这是另一个话题。