2012-11-08 3 views
2

J'ai une table dans Firebird 2.5.2:Firebird POSITION comportement inattendu?

create table SearchTest (val varchar(20)) 

qui a deux lignes:

insert into SearchTest (val) values ('one') 
insert into SearchTest (val) values ('three') 

Je veux sélectionner toutes les lignes où la colonne 'val' contient soit 'un' ou « hre '. LINQ je peux exprimer ce que:

var a = from b in TestEntities.SEARCHTESTs 
     from c in new []{ "one", "hre" } 
     where b.VAL.Contains(c) 
     select b; 

Cela génère une requête comme ceci:

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) 

Pour faciliter l'inspection bien, cela fait la même chose:

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 
) 

Utilisation de la termes de recherche 'un' et 'hre', le résultat est comme vous pouvez l'attendre:

val substr pos 
--- ------ --- 
one one 1 
three one 0 
one hre 0 
three hre 2 

Cependant, si les longueurs des termes de recherche ne correspondent pas:

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 
) 

Le match échoue:

val substr pos 
--- ------ --- 
one one 1 
three one 0 
one hree 0 
three hree 0 

Si je jetai les termes de recherche (les types de fonte ne doivent pas correspondre, comme montré ici):

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 
) 

Le match fonctionne à nouveau:

val substr pos 
--- ------ --- 
one one 1 
three one 0 
one hree 0 
three hree 2 

Pourquoi est-ce, et y a-t-il un moyen de contourner le problème?

Edit:

Jiri Cincura a noté que ce bug est corrigé pour la prochaine version; Les constantes de chaîne sont maintenant converties en varchar explicitement. problème Firebird tracker: http://tracker.firebirdsql.org/browse/DNET-466

+0

Pourquoi n'utilisez-vous pas l'opérateur SIMILAR TO? –

+0

Qu'est-ce que la version Firebird? À première vue, cela ressemble à un bug, donc vous pourriez aussi vouloir créer un ticket de bug sur http://tracker.firebirdsql.org/browse/CORE –

Répondre

3

Firebird traite littéraux comme CHAR, alors quand vous avez deux littéraux de différentes longueurs (ici 'one' et 'hree'), il les décrit comme CHAR(4).

Pour illustrer cela, la sortie dans ISQL avec SQLDA_DISPLAY ON est pour une requête similaire:

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 TEXTE est le type Firebird pour CHAR colonnes. Le sqllen indique qu'il est de longueur 4. Pour 'one' cela signifie qu'il est réellement 'one ' (notez l'espace supplémentaire). Donc, quand il est alimenté à POSITION, il ne correspond pas, puisque votre valeur d'origine est 'un' (comme VARCHAR).

Je ne sais pas si elle est un bogue dans POSITION (le comportement de CHAR est une caractéristique gênante de la norme SQL), ou si la façon dont Firebird utilise CHAR pour littéraux aurait besoin d'être changé à VARCHAR.C'est sûr que c'est confus, donc je suggère de signaler comme un bug sur http://tracker.firebirdsql.org/browse/CORE

Maintenant, si vous écriviez des requêtes directes, la solution est comme dans votre dernier exemple. Depuis que vous lancez l'un des littéraux à VARCHAR, le syndicat convertit automatiquement toutes les valeurs à 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 est le type Firebird pour VARCHAR. Notez que c'est la longueur 4 et non la 3 comme dans la distribution car Firebird l'étend jusqu'à la taille maximale trouvée (dans la seconde partie du UNION).

Comme vous n'écrivez pas le SQL directement, je ne suis pas sûr de ce qui serait la solution ici pour vous. Vous pouvez ajouter une demande d'amélioration pour que le fournisseur Firebird .NET convertisse les valeurs en VARCHAR pour ces types de conversions (dans le tracker à http://tracker.firebirdsql.org/browse/DNET)

+1

Ça explique, merci! J'ai créé un problème Firebird .NET Provider à l'adresse http://tracker.firebirdsql.org/browse/DNET-466 Le code affecté traite seulement environ 50k lignes, donc je peux contourner ce problème en déplaçant l'opération Contient() en mémoire pour le moment. – DaveK