如何在不使用窗口函数的情况下生成 row_number?

Eva*_*oll 3 postgresql

在PostgreSQL中,如何生成行号:

  • 没有窗口函数(如row_number()
  • 没有临时表
  • 只使用一个 SELECT

这是一些可以使用的示例数据,

CREATE TEMP TABLE foo AS 
SELECT * FROM ( VALUES
  ('wgates', 'Gates', 'William' ),
  ('wgrant', 'Grant', 'Wallace' ),
  ('jjones', 'Jones', 'John' ),
  ('psmith', 'Smith', 'Paul' )
) AS t(name_id, last_name, first_name);
Run Code Online (Sandbox Code Playgroud)

所需的输出是:

 row_number ? name_id ? last_name ? first_name 
???????????????????????????????????????????????
          1 ? wgates  ? Gates     ? William
          2 ? wgrant  ? Grant     ? Wallace
          3 ? jjones  ? Jones     ? John
          4 ? psmith  ? Smith     ? Paul
Run Code Online (Sandbox Code Playgroud)

其中一些方法可能会变得棘手。请解释你的答案。我还可以想象两类有效的答案:

  • 带有UNIQUE或的数据PRIMARY KEY(我们仍然可以name_id在这里使用)
  • 什么UNIQUE都没有。

所有功能都在最新版本的 PostgreSQL 中。

最终,我需要一个没有 ID 的表上的唯一键,以便我可以根据自身的交叉连接更新它。我也是出于单纯的好奇而问的。

Dav*_*itz 8

如果是您担心的性能,请使用row_numberwithoutorder by以避免排序。

row_number() over ()
Run Code Online (Sandbox Code Playgroud)


a_h*_*ame 7

确定用于重复删除的良好技术PK

现在这是一个完全不同的问题,然后为row_number().

在 Postgres 中,您可以使用ctid它。不需要窗口函数或缓慢且不可扩展的解决方法。


直接回答问题:

可以在没有窗口函数的情况下完成,但这会非常慢:

select name_id, last_name, first_name, 
       (select count(*)
        from the_table t2
        where t2.name_id <= t1.name_id) as row_number
from the_table t1
order by name_id;
Run Code Online (Sandbox Code Playgroud)

以上等同于:

select name_id, last_name, first_name, 
       row_number() over (order by name_id) as row_number
from the_table
order by name_id;
Run Code Online (Sandbox Code Playgroud)

但随着窗函数的解决方案是一个很大更快。如果您不需要任何订购,则使用

select name_id, last_name, first_name, 
       row_number() over () as row_number
from the_table
order by name_id;
Run Code Online (Sandbox Code Playgroud)

这样你不会得到一个“稳定”的行号,但它是唯一的。

另一种可能的替代方法是创建一个序列,然后nextval()在 select 语句中使用。


Eva*_*oll 1

UNIQUE必填栏目

\n\n

我发现的一种方法(在Leo Hsu 和 Regina Obe 的SIMULATING ROW NUMBER IN POSTGRESQL PRE 8.4中)被称为“The all in one WTF”。它略有改编,但令人惊叹。

\n\n
SELECT row_number, name_id, last_name, first_name\nFROM people\nCROSS JOIN (\n  SELECT array_agg(name_id ORDER BY last_name, first_name) AS id FROM people\n) AS oldids\nCROSS JOIN generate_series(1, (SELECT count(*) FROM people))\n  AS gs(row_number)\nWHERE id[row_number] = people.name_id;\n
Run Code Online (Sandbox Code Playgroud)\n\n

那么让我们进入兔子洞:

\n\n
    \n
  1. 创建ARRAY[]任意特定列的一个,不生成额外的行。

    \n\n
    SELECT *\nFROM people\nCROSS JOIN (\n  SELECT array_agg(name_id ORDER BY last_name, first_name) AS id\n  FROM people\n) AS oldids;\n
    Run Code Online (Sandbox Code Playgroud)\n\n
    SELECT row_number, name_id, last_name, first_name\nFROM people\nCROSS JOIN (\n  SELECT array_agg(name_id ORDER BY last_name, first_name) AS id FROM people\n) AS oldids\nCROSS JOIN generate_series(1, (SELECT count(*) FROM people))\n  AS gs(row_number)\nWHERE id[row_number] = people.name_id;\n
    Run Code Online (Sandbox Code Playgroud)
  2. \n
  3. 使用 . 创建笛卡尔积generate_series(1..last_row)。为了获得最后一行,我们使用 进行子选择count()

    \n\n
     SELECT *\n FROM people\n CROSS JOIN (\n   SELECT array_agg(name_id ORDER BY last_name, first_name) AS id FROM people\n ) AS oldids\n CROSS JOIN generate_series(1, (SELECT count(*) FROM people))\n   AS gs(row_number);\n
    Run Code Online (Sandbox Code Playgroud)\n\n
    SELECT *\nFROM people\nCROSS JOIN (\n  SELECT array_agg(name_id ORDER BY last_name, first_name) AS id\n  FROM people\n) AS oldids;\n
    Run Code Online (Sandbox Code Playgroud)
  4. \n
  5. 现在魔法来了,我们使用行号作为我们创建的数组的索引。因为数组是以下函数的函数:(a) 列UNIQUE和 (b) 集合中的顺序,我们可以减少笛卡尔积,并保留 row_number。我们所做的就是添加子句WHERE id[row_number] = people.name_id;

  6. \n
\n