2

我在 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

4

1 回答 1

3

Firebird 将文字视为,因此当您在此处 (和)CHAR有两个不同长度的文字时,它会将它们描述为.'one''hree'CHAR(4)

为了说明这一点,在 ISQL with SQLDA_DISPLAY ONis 中,类似查询的输出是:

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

类型 452 TEXT 是用于CHAR列的 Firebird 类型。sqllen表示它的长度为 4。因为这'one'意味着它实际上是'one '(注意额外的空间)。因此,当它被馈送到 时POSITION,它不匹配,因为您的原始值为“一”(作为 VARCHAR)。

我不确定这是否是一个错误POSITION( 的行为CHAR是 SQL 标准的一个令人讨厌的特性),或者 FirebirdCHAR用于文字的方式是否需要更改为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. 请注意,长度为 4 而不是演员表中的 3,因为 Firebird 会将其扩展到找到的最大尺寸(在 的第二部分中UNION)。

由于您不是直接编写 SQL,因此我不确定这里的解决方案是什么。您可以为 Firebird .NET 提供程序添加改进请求,以便VARCHAR为这些类型的转换转换值(在http://tracker.firebirdsql.org/browse/DNET的跟踪器中)

于 2012-11-08T20:22:40.840 回答