2

Firebird 2.5.2 にテーブルがあります。

create table SearchTest ( val varchar(20) )

次の 2 つの行があります。

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 は、このバグは次のリリースで修正されていると述べています。文字列定数は明示的に varchar に変換されるようになりました。Firebird トラッカーの問題: http://tracker.firebirdsql.org/browse/DNET-466

4

1 に答える 1

3

Firebird はリテラルを として扱うCHARため、ここに長さが異なる 2 つのリテラル ('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 タイプです。は、長さが 4 であることを示します。これは、実際にsqllenは長さが 4 である'one'ことを意味します'one '(余分なスペースに注意してください)。したがってPOSITION、元の値が「1」(VARCHARとして)であるため、に供給されると一致しません。

それがバグなのかPOSITION( の動作はCHARSQL 標準の厄介な機能です)、それとも Firebird がCHARリテラルに使用する方法を に変更する必要があるのか​​どうかはわかりませんVARCHAR。確かに紛らわしいので、http://tracker.firebirdsql.org/browse/COREでバグとして報告することをお勧めします。

直接クエリを書いていた場合、解決策は最後の例のようになります。リテラルの 1 つを にキャストするため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

Type 448 VARYING は の Firebird タイプですVARCHAR。長さは 4 であり、キャストの 3 ではなく、Firebird が見つかった最大サイズ (の 2 番目の部分UNION) に拡張することに注意してください。

SQL を直接書いていないので、ここで何が解決策になるかわかりません。Firebird .NET プロバイダーに改善要求を追加して、これらのタイプの変換の値をキャストすることができます( http://tracker.firebirdsql.org/browse/DNETVARCHARのトラッカー内) 。

于 2012-11-08T20:22:40.840 に答える