转换unnest()的结果时会丢失NULL emements

Erw*_*ter 5 arrays postgresql casting unnest set-returning-functions

unnest()在扩展数组后进行转换时,我偶然发现了的异常行为。

介绍

使用unnest()有三种基本语法变体:

1)SELECT unnest('{1,NULL,4}'::int[]) AS i;
2)SELECT i FROM unnest('{2,NULL,4}'::int[]) AS i;
3)SELECT i FROM (SELECT unnest('{3,NULL,4}'::int[])) AS t(i);

它们都包括一行,NULL结果符合预期

 i
---
 1
(null)
 4
Run Code Online (Sandbox Code Playgroud)

要将数组元素转换为其他类型,可以在扩展数组后立即将元素转换为基本类型,或者在扩展之前将数组本身转换为其他数组类型。第一个变体对我来说似乎更简单或更短:

A)SELECT unnest('{4,NULL,1}'::int[])::text;
B)SELECT unnest('{4,NULL,2}'::int[]::text[]);

 i
---
 4
(null)
 1
Run Code Online (Sandbox Code Playgroud)

奇怪的行为

所有组合都可能,除了 2A)

由于某些原因,无法2)A)

SELECT * FROM unnest('{2,NULL,1}'::int[])::text;
Run Code Online (Sandbox Code Playgroud)

错误:“ ::”或附近的语法错误

我可以接受。由于某种原因未实施的罕见的极端情况。
所有其他组合都会飞,但是:

1A)SELECT unnest('{1,NULL,1}'::int[])::text AS i;
2A)SELECT i FROM unnest('{2,NULL,1}'::int[])::text AS i;
3A)SELECT i FROM (SELECT unnest('{3,NULL,1}'::int[])::text) AS t(i);
1B)SELECT unnest('{1,NULL,2}'::int[]::text[]) AS i;
2B)SELECT i FROM unnest('{2,NULL,2}'::int[]::text[]) AS i;
3B)SELECT i FROM (SELECT unnest('{3,NULL,2}'::int[]::text[])) AS t(i);

与上述相同的结果。

真的很奇怪

以下观察仅涉及A)。用代替可以避免这个问题B)

不出所料,到目前为止,我们已经看到了NULL数组中的元素导致NULL在所有查询中都带有值的行。但是,将结果从某些数组类型转换为某些基本类型时,情况并非如此。

在这里,带有NULL值的行突然消失(!):

SELECT unnest('{1,NULL,4}'::int[])::int8;

 i
---
 1
 4
Run Code Online (Sandbox Code Playgroud)

例子

我去看看兔子洞有多深。这里有些例子:

NULL 消失:

SELECT unnest('{1,NULL,1}'::int[])::int2;
SELECT unnest('{1,NULL,2}'::int[])::int8;
SELECT unnest('{1,NULL,3}'::int[])::real;
SELECT unnest('{1,NULL,4}'::int[])::float8;
SELECT unnest('{1,NULL,5}'::int[])::numeric;
SELECT unnest('{1,NULL,6}'::numeric[])::int2;
SELECT unnest('{1,NULL,7}'::numeric[])::int8;
SELECT unnest('{1,NULL,8}'::numeric[])::real;
SELECT unnest('{1,NULL,9}'::numeric[])::float8;
SELECT unnest('{1,NULL,a}'::text[])::char;
SELECT unnest('{1,NULL,b}'::text[])::char(1);
SELECT unnest('{1,NULL,c}'::text[])::varchar(10);      -- !!!
SELECT unnest('{1,NULL,d}'::varchar[])::varchar(10);   -- !!!
SELECT unnest('{2013-1-1,NULL,2013-1-1}'::date[])::timestamp;
SELECT unnest('{2013-1-1,NULL,2013-1-1}'::timestamp[])::date;
SELECT unnest('{23:11,NULL,23:11}'::time[])::interval;
SELECT unnest('{23:11,NULL,23:11}'::interval[])::time;
Run Code Online (Sandbox Code Playgroud)

NULL 停留:

SELECT unnest('{1,NULL,1}'::int[])::int4;    -- is really from int to int
SELECT unnest('{1,NULL,2}'::int[])::text;
SELECT unnest('{1,NULL,3}'::int8[])::text;
SELECT unnest('{1,NULL,4}'::numeric[])::text;
SELECT unnest('{1,NULL,5}'::text[])::int;
SELECT unnest('{1,NULL,6}'::text[])::int8;
SELECT unnest('{1,NULL,7}'::text[])::numeric;
SELECT unnest('{1,NULL,8}'::text[])::varchar;    -- !!!
SELECT unnest('{1,NULL,9}'::varchar[])::text;    -- !!!
SELECT unnest('{2013-1-1,NULL,2013-1-1}'::date[])::text;
SELECT unnest('{2013-1-1,NULL,2013-1-1}'::text[])::date;
SELECT unnest('{23:11,NULL,23:11}'::time[])::text;
SELECT unnest('{23:11,NULL,23:11}'::text[])::time;
Run Code Online (Sandbox Code Playgroud)

这似乎是不能接受的。

在测试了很多组合之后,模式似乎是:

在相关类型之间进行转换会导致NULL元素丢失。
在不相关类型之间进行转换会导致NULL保留元素。
除了varchar[]-> text反之亦然-破坏了我的这个小假设。或者varchartext我想象的不同。

经过PostgreSQL 9.1和9.2测试。结果相同。
-> SQLfiddle的

问题

我在这里想念什么吗?有人可以解释这种行为吗?
如果不是,问题就变成了:我应该继续提交错误报告吗?

Pav*_*ule 4

不支持转换 SRF 函数(在 FROM 子句中) - 您不能在那里使用任何运算符。只允许函数调用。

\n\n

只能在列列表中进行强制转换:

\n\n
postgres=# SELECT * FROM unnest(\'{2,NULL,1}\'::int[])::text;\nERROR:  syntax error at or near "::"\nLINE 1: SELECT * FROM unnest(\'{2,NULL,1}\'::int[])::text;\n                                                 ^\npostgres=# SELECT v::text FROM unnest(\'{2,NULL,1}\'::int[]) g(v);\n   v    \n\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\n      2\n [null]\n      1\n(3 rows)\n
Run Code Online (Sandbox Code Playgroud)\n\n

NULL 中缺少行可能是错误,应该报告

\n\n
postgres=# SELECT unnest(\'{1,NULL,4}\'::int[])::text;\n unnest \n\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\n      1\n [null]\n      4\n(3 rows)\n\npostgres=# SELECT unnest(\'{1,NULL,4}\'::int[])::numeric;\n unnest \n\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\n      1\n      4\n(2 rows)\n
Run Code Online (Sandbox Code Playgroud)\n\n

我认为没有理由应该删除 NULL 行

\n

  • [我报告了该错误,Tom Lane 立即发现并修复了该问题](http://www.postgresql.org/message-id/flat/E1Ub8p6-0006NC-Ja@wrigleys.postgresql.org#E1Ub8p6-0006NC-Ja@ wrigleys.postgresql.org)。Postgres 9.3 不会出现此错误,但他们没有进行反向修补,以免破坏旧代码。 (2认同)