SQL 带有嵌套的 CASE 和连接字符串给出尾随空格
SQL with nested CASE and joining strings gives trailing spaces
我正在使用 Firebird 2.5。当我运行
SELECT
CASE BILANCA
WHEN 1 THEN
CASE BILANCA WHEN 0 THEN 'SMALLINT' WHEN 1 THEN 'NUME' END
ELSE 'A'||'B' END
AS BILANCA1,
CASE BILANCA
WHEN 1 THEN
CASE BILANCA WHEN 0 THEN 'SMALLINT' WHEN 1 THEN 'NUME' END
ELSE 'AB' END
AS BILANCA2
FROM GS01BILANCA
字段BILANCA2为'NUME '
(NUME后有4个空格),BILANCA1为'NUME'
。添加的空格数等于 CASE 中最长字符串的长度。
唯一的区别是在 BILANCA1 中连接字符串:
ELSE 'A'||'B' END
DDL 是
CREATE TABLE GS01BILANCA
(BILANCA INTEGER DEFAULT 0 NOT NULL,
CONSTRAINT PK_GS01BILANCA PRIMARY KEY (BILANCA));
Table 有 2 条记录:
- 1
- 2
结果是
'NUME '
(NUME后有4个空格),'NUME'
'AB'
,'AB'
问题:这是预期的吗?为什么?
Firebird 中的字符串文字是 CHAR
类型,case 表达式的数据类型将具有所有分支中最长的长度。所以
CASE BILANCA WHEN 0 THEN 'SMALLINT' WHEN 1 THEN 'NUME' END
数据类型将为 CHAR(8)
。 CHAR
值将始终用空格向右填充到声明的长度。
然而,当 case 表达式的分支之一是 VARCHAR
时,所有分支都被强制转换为 VARCHAR
。 Firebird 中的串联将始终产生 VARCHAR
,即使两个操作数都是 CHAR
这意味着在BILANCA1
的情况下:
CASE BILANCA
WHEN 1 THEN
CASE BILANCA WHEN 0 THEN 'SMALLINT' WHEN 1 THEN 'NUME' END
ELSE 'A'||'B' END
'A'||'B'
是 VARCHAR(2)
,这导致整个案例被强制转换为 VARCHAR
,然后导致 'SMALLINT'
成为 VARCHAR(8)
而'NUME'
一个VARCHAR(4)
,这使得整个表达式变成了一个VARCHAR(8)
。
请注意,此类型强制被下推,因此文字将从一开始就是 varchar
,并且不会被最内层 case 语句的中间评估填充*。
BILANCA2
不是这种情况,因为所有(嵌套的)分支都是文字,因此它将是 CHAR(8)
。
如果您希望两者的行为相同,您可以这样做:
- 将整个表达式转换为所需的类型(这对
bilanca1
和 bilanca2
都有效)
- 将表达式的一部分(例如文字之一或嵌套大小写)转换为
varchar
;这只适用于强制 bilanca2
到 varchar
- 将字符串连接转换为
char
;这只适用于强制 bilanca1
到 char
- 将一个空字符串连接到其中一个文字;这只适用于强制
bilanca2
到 varchar
- 在整个表达式上使用
trim
(这也强制转换为 varchar
)
*) 这与例如 cast(cast('nume' as char(8)) as varchar(8))
形成对比,后者将产生 'nume '
而不仅仅是 'nume'
,因为在这种情况下,中间结果将被保留,并且不会抛出现有空格离开 varchar
.
我正在使用 Firebird 2.5。当我运行
SELECT
CASE BILANCA
WHEN 1 THEN
CASE BILANCA WHEN 0 THEN 'SMALLINT' WHEN 1 THEN 'NUME' END
ELSE 'A'||'B' END
AS BILANCA1,
CASE BILANCA
WHEN 1 THEN
CASE BILANCA WHEN 0 THEN 'SMALLINT' WHEN 1 THEN 'NUME' END
ELSE 'AB' END
AS BILANCA2
FROM GS01BILANCA
字段BILANCA2为'NUME '
(NUME后有4个空格),BILANCA1为'NUME'
。添加的空格数等于 CASE 中最长字符串的长度。
唯一的区别是在 BILANCA1 中连接字符串:
ELSE 'A'||'B' END
DDL 是
CREATE TABLE GS01BILANCA
(BILANCA INTEGER DEFAULT 0 NOT NULL,
CONSTRAINT PK_GS01BILANCA PRIMARY KEY (BILANCA));
Table 有 2 条记录:
- 1
- 2
结果是
'NUME '
(NUME后有4个空格),'NUME'
'AB'
,'AB'
问题:这是预期的吗?为什么?
Firebird 中的字符串文字是 CHAR
类型,case 表达式的数据类型将具有所有分支中最长的长度。所以
CASE BILANCA WHEN 0 THEN 'SMALLINT' WHEN 1 THEN 'NUME' END
数据类型将为 CHAR(8)
。 CHAR
值将始终用空格向右填充到声明的长度。
然而,当 case 表达式的分支之一是 VARCHAR
时,所有分支都被强制转换为 VARCHAR
。 Firebird 中的串联将始终产生 VARCHAR
,即使两个操作数都是 CHAR
这意味着在BILANCA1
的情况下:
CASE BILANCA
WHEN 1 THEN
CASE BILANCA WHEN 0 THEN 'SMALLINT' WHEN 1 THEN 'NUME' END
ELSE 'A'||'B' END
'A'||'B'
是 VARCHAR(2)
,这导致整个案例被强制转换为 VARCHAR
,然后导致 'SMALLINT'
成为 VARCHAR(8)
而'NUME'
一个VARCHAR(4)
,这使得整个表达式变成了一个VARCHAR(8)
。
请注意,此类型强制被下推,因此文字将从一开始就是 varchar
,并且不会被最内层 case 语句的中间评估填充*。
BILANCA2
不是这种情况,因为所有(嵌套的)分支都是文字,因此它将是 CHAR(8)
。
如果您希望两者的行为相同,您可以这样做:
- 将整个表达式转换为所需的类型(这对
bilanca1
和bilanca2
都有效) - 将表达式的一部分(例如文字之一或嵌套大小写)转换为
varchar
;这只适用于强制bilanca2
到varchar
- 将字符串连接转换为
char
;这只适用于强制bilanca1
到char
- 将一个空字符串连接到其中一个文字;这只适用于强制
bilanca2
到varchar
- 在整个表达式上使用
trim
(这也强制转换为varchar
)
*) 这与例如 cast(cast('nume' as char(8)) as varchar(8))
形成对比,后者将产生 'nume '
而不仅仅是 'nume'
,因为在这种情况下,中间结果将被保留,并且不会抛出现有空格离开 varchar
.