首页 >数据库 >mysql教程 >使用PostgreSQL的unnest()函数时如何获取元素编号?

使用PostgreSQL的unnest()函数时如何获取元素编号?

Linda Hamilton
Linda Hamilton原创
2025-01-23 04:25:13242浏览

How to Get the Element Number When Using PostgreSQL's unnest() Function?

PostgreSQL unnest() 函数与元素编号

问题

当遇到包含分隔值的列时,unnest() 函数提供了一种提取这些值的方法:

<code class="language-sql">myTable
id | elements
---+------------
1  |ab,cd,efg,hi
2  |jk,lm,no,pq
3  |rstuv,wxyz

select id, unnest(string_to_array(elements, ',')) AS elem
from myTable

id | elem
---+-----
1  | ab
1  | cd
1  | efg
1  | hi
2  | jk
...</code>

但是,您可能希望也包含元素编号,格式如下:

<code class="language-sql">id | elem | nr
---+------+---
1  | ab   | 1
1  | cd   | 2
1  | efg  | 3
1  | hi   | 4
2  | jk   | 1
...</code>

最终目标是在不使用窗口函数(如 row_number()rank())的情况下获得源字符串中每个元素的原始位置,因为这些函数始终返回 1,这可能是因为所有元素都位于源表的同一行中。

解决方法

PostgreSQL 14 或更高版本

对于逗号分隔的字符串,请使用 string_to_table() 代替 unnest(string_to_array())

<code class="language-sql">SELECT t.id, a.elem, a.nr
FROM   tbl t
LEFT   JOIN LATERAL string_to_table(t.elements, ',') WITH ORDINALITY AS a(elem, nr) ON true</code>

演示

PostgreSQL 9.4 或更高版本

对于返回集合的函数,使用 WITH ORDINALITY

<code class="language-sql">SELECT t.id, a.elem, a.nr
FROM   tbl AS t
LEFT   JOIN LATERAL unnest(string_to_array(t.elements, ',')) WITH ORDINALITY AS a(elem, nr) ON true</code>

LEFT JOIN ... ON true 确保保留左侧表中的所有行,而不管右侧表表达式是否返回任何行。

或者,由于 LEFT JOIN ... ON true 保留了所有行,因此可以使用更简洁的查询版本:

<code class="language-sql">SELECT t.id, a.elem, a.nr
FROM   tbl t, unnest(string_to_array(t.elements, ',')) WITH ORDINALITY a(elem, nr)</code>

对于实际数组(arr 为数组列),可以使用更简洁的形式:

<code class="language-sql">SELECT t.id, a.elem, a.nr
FROM   tbl t, unnest(t.arr) WITH ORDINALITY a(elem, nr)</code>

为了简单起见,可以使用默认列名:

<code class="language-sql">SELECT id, a, ordinality
FROM   tbl, unnest(arr) WITH ORDINALITY a</code>

还可以进一步简化:

<code class="language-sql">SELECT * FROM tbl, unnest(arr) WITH ORDINALITY a</code>

此最终形式返回 tbl 的所有列。当然,显式指定列别名和表限定列可以提高清晰度。

a 既用作表别名,也用作列别名(对于第一列),附加的序号列的默认名称为 ordinality

PostgreSQL 8.4 - 9.3

使用 row_number() OVER (PARTITION BY id ORDER BY elem) 根据排序顺序(而不是序号位置)获取数字:

<code class="language-sql">SELECT *, row_number() OVER (PARTITION by id) AS nr
FROM  (SELECT id, regexp_split_to_table(elements, ',') AS elem FROM tbl) t</code>

虽然这通常有效,并且在简单的查询中没有观察到失败,但 PostgreSQL 不保证在没有 ORDER BY 的情况下行的顺序。当前行为是实现细节的结果。

保证空格分隔字符串中元素的序号

<code class="language-sql">SELECT id, arr[nr] AS elem, nr
FROM  (
   SELECT *, generate_subscripts(arr, 1) AS nr
   FROM  (SELECT id, string_to_array(elements, ' ') AS arr FROM tbl) t
   ) sub</code>

对于实际数组,可以使用更简单的版本:

<code class="language-sql">SELECT id, arr[nr] AS elem, nr
FROM  (SELECT *, generate_subscripts(arr, 1) AS nr FROM tbl) t</code>

PostgreSQL 8.1 - 8.4

由于 PostgreSQL 8.1 到 8.4 版本缺少某些功能,例如 RETURNS TABLEgenerate_subscripts()unnest()array_length(),因此可以使用名为 f_unnest_ord 的自定义 SQL 函数:

<code class="language-sql">CREATE FUNCTION f_unnest_ord(anyarray, OUT val anyelement, OUT ordinality integer)
  RETURNS SETOF record
  LANGUAGE sql IMMUTABLE AS
'SELECT [i], i - array_lower(,1) + 1
 FROM   generate_series(array_lower(,1), array_upper(,1)) i'</code>

修改后的函数如下:

<code class="language-sql">myTable
id | elements
---+------------
1  |ab,cd,efg,hi
2  |jk,lm,no,pq
3  |rstuv,wxyz

select id, unnest(string_to_array(elements, ',')) AS elem
from myTable

id | elem
---+-----
1  | ab
1  | cd
1  | efg
1  | hi
2  | jk
...</code>

此扩展函数 f_unnest_ord_idx 返回附加的 idx 列。比较:

<code class="language-sql">id | elem | nr
---+------+---
1  | ab   | 1
1  | cd   | 2
1  | efg  | 3
1  | hi   | 4
2  | jk   | 1
...</code>

输出

<code class="language-sql">SELECT t.id, a.elem, a.nr
FROM   tbl t
LEFT   JOIN LATERAL string_to_table(t.elements, ',') WITH ORDINALITY AS a(elem, nr) ON true</code>

以上是使用PostgreSQL的unnest()函数时如何获取元素编号?的详细内容。更多信息请关注PHP中文网其他相关文章!

声明:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn