我在Firebird 2.5.2中有一张表:
create table SearchTest ( val varchar(20) )
有两行:
insert into SearchTest ( val ) values ('one')
insert into SearchTest ( val ) values ('three')
我想选择列'val'包含'one'或'hre'的所有行。使用linq我可以表达为:
var a = from b in TestEntities.SEARCHTESTs
from c in new []{ "one", "hre" }
where b.VAL.Contains(c)
select b;
这会生成如下查询:
SELECT
"C"."VAL" AS "VAL"
FROM "SEARCHTEST" AS "C"
CROSS JOIN (SELECT
_UTF8 X'4F4E45' AS "C1"
FROM ( SELECT 1 AS X FROM RDB$DATABASE) AS "D"
UNION ALL
SELECT
_UTF8 X'485245' AS "C1"
FROM ( SELECT 1 AS X FROM RDB$DATABASE) AS "E") AS "F"
WHERE ((POSITION("F"."C1", "C"."VAL")) > 0)
为便于检查,这也是做同样的事情:
SELECT
val,
substr,
POSITION(Substr, Val) as pos
FROM
SearchTest
CROSS JOIN
(
SELECT 'one' AS substr FROM RDB$DATABASE
UNION ALL
SELECT 'hre' AS substr FROM RDB$DATABASE
)
使用搜索词“one”和“hre”,结果如您所愿:
val substr pos
--- ------ ---
one one 1
three one 0
one hre 0
three hre 2
但是,如果搜索词的长度不匹配:
SELECT
val,
substr,
POSITION(Substr, Val) as pos
FROM
SearchTest
CROSS JOIN
(
SELECT 'one' AS substr FROM RDB$DATABASE
UNION ALL
SELECT 'hree' AS substr FROM RDB$DATABASE
)
比赛失败:
val substr pos
--- ------ ---
one one 1
three one 0
one hree 0
three hree 0
如果我投射搜索词(投射类型不必匹配,如此处所示):
SELECT
val,
substr,
POSITION(Substr, Val) as pos
FROM
SearchTest
CROSS JOIN
(
SELECT cast('one' as varchar(3)) AS substr FROM RDB$DATABASE
UNION ALL
SELECT cast('hree' as char(5)) AS substr FROM RDB$DATABASE
)
比赛再次起作用:
val substr pos
--- ------ ---
one one 1
three one 0
one hree 0
three hree 2
为什么会这样,并且有办法解决它吗?
修改
Jiri Cincura已经注意到此错误已在下一版本中修复;字符串常量现在显式转换为varchars。 Firebird跟踪器问题:http://tracker.firebirdsql.org/browse/DNET-466
答案 0 :(得分:3)
Firebird将文字视为CHAR
,因此当您在此处有两个不同长度的文字('one'
和'hree'
)时,它会将它们描述为CHAR(4)
。
为了说明这一点,带有SQLDA_DISPLAY ON
的ISQL中的输出用于类似的查询:
SQL> SET SQLDA_DISPLAY ON;
SQL> SELECT 'one' as X FROM RDB$DATABASE
CON> UNION ALL
CON> SELECT 'hree' as x FROM RDB$DATABASE;
INPUT SQLDA version: 1 sqln: 10 sqld: 0
OUTPUT SQLDA version: 1 sqln: 20 sqld: 1
01: sqltype: 452 TEXT sqlscale: 0 sqlsubtype: 0 sqllen: 4
: name: (0) alias: (1)X
: table: (0) owner: (0)
X
======
one
hree
Type 452 TEXT是CHAR
列的Firebird类型。 sqllen
表示长度为4. 'one'
这意味着它实际上是'one '
(请注意额外的空格)。因此当它被馈送到POSITION
时,它不匹配,因为你的原始值是'one'(作为VARCHAR)。
我不确定它是POSITION
中的错误(CHAR
的行为是SQL标准的烦人特征),还是Firebird使用CHAR
文字的方式需要更改为VARCHAR
。确实令人困惑,因此我建议将其报告为http://tracker.firebirdsql.org/browse/CORE
现在,如果您正在编写直接查询,则解决方案与上一个示例相同。由于您将其中一个文字转换为VARCHAR
,因此联合会自动将所有值转换为VARCHAR
:
SQL> SELECT CAST('one' AS VARCHAR(3)) as X FROM RDB$DATABASE
CON> UNION ALL
CON> SELECT 'hree' as x FROM RDB$DATABASE;
INPUT SQLDA version: 1 sqln: 10 sqld: 0
OUTPUT SQLDA version: 1 sqln: 20 sqld: 1
01: sqltype: 448 VARYING sqlscale: 0 sqlsubtype: 21 sqllen: 4
: name: (0) alias: (1)X
: table: (0) owner: (0)
X
======
one
hree
类型448 VARYING是VARCHAR
的Firebird类型。请注意,长度为4而不是3,因为Firebird会将其扩展到找到的最大大小(在UNION
的第二部分中)。
由于您没有直接编写SQL,我不确定这里的解决方案是什么。您可以为Firebird .NET提供程序添加一个改进请求,以便为这些类型的转换将值转换为VARCHAR
(在http://tracker.firebirdsql.org/browse/DNET的跟踪器中)