在讨论此问题的递归CTE解决方案时:
@ypercube偶然发现了一个令人惊讶的异常,这使我们研究了类型修饰符的处理。我们发现了令人惊讶的行为。
1.类型转换在某些情况下保留类型修饰符
即使指示不要这样做。最基本的例子:
SELECT 'vc8'::varchar(8)::varchar
varchar
至少我会期望(没有修饰符)。但是结果是varchar(8)
(带有修饰符)。在下面的小提琴中有许多相关案例。
2.数组串联在某些情况下会丢失类型修饰符
不需要,因此在另一侧犯了错误:
SELECT ARRAY['vc8']::varchar(8)[]
, ARRAY['vc8']::varchar(8)[] || 'vc8'::varchar(8)
第一个表达式产生varchar(8)[]
预期的效果。
但是第二个,在连接另一个之后,varchar(8)
会减少到varchar[]
(无修饰符)。array_append()
以下小提琴中的示例提供类似的行为。
在大多数情况下,所有这些都无关紧要。Postgres不会丢失数据,并且当分配给列时,无论如何该值都被强制为正确的类型。但是,在相反方向上的错误最终导致一个令人惊讶的异常:
3.递归CTE要求数据类型完全匹配
给出此简化表:
CREATE TABLE a (
vc8 varchar(8) -- with modifier
, vc varchar -- without
);
INSERT INTO a VALUES ('a', 'a'), ('bb', 'bb');
尽管此rCTE适用于该varchar
列vc
,但不适用于该varchar(8)
列vc8
:
WITH RECURSIVE cte AS (
(
SELECT ARRAY[vc8] AS arr -- produces varchar(8)[]
FROM a
ORDER BY vc8
LIMIT 1
)
UNION ALL
(
SELECT a.vc8 || c.arr -- produces varchar[] !!
FROM cte c
JOIN a ON a.vc8 > c.arr[1]
ORDER BY vc8
LIMIT 1
)
)
TABLE cte;
错误:递归查询“ cte”列1在非递归术语中具有类型字符varianting(8)[],但总体上具有类型字符variable [] 提示:将非递归项的输出强制转换为正确的类型。位置:103
一种快速的解决方法是强制转换为text
。
普通UNION
查询不会出现相同的问题:它会查询没有修饰符的类型,从而保证保留所有信息。但是rCTE更挑剔。
另外,使用/ max(vc8)
代替更常用的方法也不会遇到问题,因为和朋友可以立即解决(或没有修饰符的相应基本类型)。ORDER BY
LIMIT 1
max()
text
SQL Fiddle演示了三件事:
- 一系列示例表达式,包括令人惊讶的结果。
- 与
varchar
(不带修饰符)一起使用的简单rCTE 。 - 相同的rCTE引发
varchar(n)
(带修饰符)的例外。
小提琴是针对9.3版的。对于第9.4.4页,我在本地得到相同的结果。
我从演示表达式创建表,以便能够显示包括修饰符的确切数据类型。尽管pgAdmin开箱即用地显示了此信息,但sqlfiddle无法提供该信息。值得注意的是,它在psql
(!)中也不可用。这在psql中是已知的缺点,以前在pgsql-hacker上已经讨论了一种可能的解决方案-但尚未实现。这可能是尚未发现并解决该问题的原因之一。
在SQL级别上,您可以pg_typeof()
用来获取类型(但不能获取修饰符)。
问题
这三个问题在一起使事情变得一团糟。
确切地说,问题1.没有直接涉及,但它以非递归术语(ARRAY[vc8]::varchar[]
或类似术语)的强制转换破坏了看似明显的修复,这增加了混乱。
这些项目中的哪一个是错误,故障或应有的状态?
我是否缺少某些东西,还是应该报告错误?
UNION
查询更严格(不够智能)。难道我们一次发现了三个独立的小错误?(经过数月未找到此类信息。)您认为应该将其中哪些记录为错误?