【问题标题】:NULL emements lost when casting result of unnest()转换 unnest() 的结果时丢失 NULL 元素
【发布时间】:2013-05-03 09:09:23
【问题描述】:

在扩展数组后进行转换时,我偶然发现了 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

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

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

 i
---
 4
(null)
 1

奇怪的行为

除了2A)之外的所有可能组合

由于某种原因,不能将2)A) 结合起来

SELECT * FROM unnest('{2,NULL,1}'::int[])::text;

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

我可以接受。由于某种原因尚未实施的罕见极端案例。
不过,所有其他组合都会飞起来:

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 值的行。但是,将结果从 some 数组类型转换为 some 基本类型时,情况并非如此。

这里有 NULL 值的行突然消失了(!):

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

 i
---
 1
 4

示例

我去看看兔子洞有多深。以下是一些示例:

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;

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;

这似乎是不可接受的。

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

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

使用 PostgreSQL 9.1 和 9.2 测试。结果相同。
-> SQLfiddle

问题

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

【问题讨论】:

    标签: arrays postgresql casting unnest set-returning-functions


    【解决方案1】:

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

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

    postgres=# SELECT * FROM unnest('{2,NULL,1}'::int[])::text;
    ERROR:  syntax error at or near "::"
    LINE 1: SELECT * FROM unnest('{2,NULL,1}'::int[])::text;
                                                     ^
    postgres=# SELECT v::text FROM unnest('{2,NULL,1}'::int[]) g(v);
       v    
    ────────
          2
     [null]
          1
    (3 rows)
    

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

    postgres=# SELECT unnest('{1,NULL,4}'::int[])::text;
     unnest 
    ────────
          1
     [null]
          4
    (3 rows)
    
    postgres=# SELECT unnest('{1,NULL,4}'::int[])::numeric;
     unnest 
    ────────
          1
          4
    (2 rows)
    

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

    【讨论】:

    • 谢谢,帕维尔。我将为第二部分提交错误报告。
    • I reported the bug and Tom Lane immediately found and fixed the problem。 Postgres 9.3 不会出现这个错误,但他们没有回补丁,以免破坏旧代码。
    • 这个错误似乎在 12.6 中再次出现!我正在使用 unnest 将 XML 数据插入使用 CREATE TABLE tbl AS SELECT unnest(xpath('//Record/@symbol', xml_string)::varchar[24][]) 作为符号的表中; @ErwinBrandstetter
    • @Kostas - 你可以发送示例吗?
    • @PavelStehule 谢谢!在尝试制作可重现的示例时,我发现问题不在于 unnest,而在于 xpath。在这里讨论:stackoverflow.com/questions/50122513/…
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-07
    • 1970-01-01
    相关资源
    最近更新 更多