2009-08-11 6 views
0

Imaginez la table t1 ont 2 cols:"Smart" clause where pour les valeurs de filtrage (Oracle 10g)

col1  col2 
-------------- 
test 900  1 
test 901  2 
test 902  3 
user 901  4 
test 909  5 

Je veux trouver juste une ligne de 2 params (nom, code) où nom de son (test, utilisateur ..etc) et le code - (901, 902, null, .. etc). Il ne peut pas y avoir plus d'une rangée de la même chose (code, nom) - Je veux dire qu'il n'y a pas 2 lignes qui ont "test 901" dans Col1.

code:

declare 
name varchar2(30); 
code varchar(10); 
col_val varchar2(30); 
col2_val numeric; 
begin 
name:= 'test'; 
code := '900'; 

    select col1, col2 into col_val, col2_val 
    from t1 
    where 

    (REGEXP_LIKE(col1, name||'\s+'||code) -- (3) 
    or (
     not REGEXP_LIKE(col1, name||'\s+'||code) -- (1) 
     and REGEXP_LIKE(col1, name) -- (2) 
    ) 
    ) 
    order by col1; 
    DBMS_OUTPUT.PUT_LINE('val:'||col_val||' id:'||col2_val); 
end; 

1) pour le nom des valeurs de test: 'test' code =: = '900' le résultat devrait être "val: Test 900 id: 1" - Il est OK.

2) mais pour nom: = code 'test': = '909' le résultat devrait être "val: test de 909 id: 5", mais je suis arrivé "val: Test 900 id: 1" (la première rangée avec Name = 'Test') - Ce n'est pas ce que je veux. 3) et dans le cas: = 'test' code: = '999' le résultat devrait être "val: test 900 id: 1" (il n'y a pas de code 999, donc j'ai besoin de n'importe quelle ligne avoir Name = 'test' à l'intérieur).

La question principale est pourquoi oracle Ignores (1) clause pour 2) exemple? Peut-être que je fais quelque chose de mal - donc ce serait génial si vous pouviez montrer mon erreur!

+0

Suppression de la ligne "rownum = 1" de la clause where. Tnks Thilo. – zmische

Répondre

0

Cette condition:

(REGEXP_LIKE(col1, :name || '\s+' || :code) 
OR (NOT REGEXP_LIKE(col1, :name || '\s+' || :code) AND REGEXP_LIKE(col1, :name)) 

correspondront toutes les lignes dans votre exemple. Lorsque vous recherchez test 909, vous correspondez à 'test 909' OR (NOT 'test 909' AND 'test'). Cela correspondra à chaque ligne à partir de 'test' (soit par première condition ou par seconde).

Puisque vous utilisez rownum = 1, la première colonne apparié à revenu, dans votre cas 'test 900'

Si vous voulez un match de fallback (retour 'test 909' si elle existe, toute 'test' autrement), utilisez ceci:

SELECT * 
FROM (
     SELECT * 
     FROM t1 
     WHERE REGEXP_LIKE(col1, name || '\s+' || code) 
     UNION ALL 
     SELECT * 
     FROM t1 
     WHERE REGEXP_LIKE(col1, name) 
     ) 
WHERE rownum = 1 

, ou, si vous ne l'aimez pas UNION s:

SELECT * 
FROM (
     SELECT * 
     FROM t1 
     WHERE REGEXP_LIKE(col1, name) 
     ORDER BY 
       CASE WHEN REGEXP_LIKE(col1, name || '\s+' || code) THEN 0 ELSE 1 END 
     ) 
WHERE rownum = 1 

Le dernier, cependant, est moins efficace, puisqu'il doit trier.

+0

rownum = 1 était là par erreur. Je l'ai enlevé. Le problème est que pour 2) exemple je ne peux pas obtenir une seule ligne! Je donne si c'est possible, ayant une telle clause. – zmische

+0

Ouais! vous m'avez raison - je veux un match de fallbak. Mais est-ce possible SANS UNION ??? Je pensais à l'union, mais dint comme cette solution. – zmische

+0

'@ zmische': il n'y a rien de mal dans' UNION'. Je peux certainement vous donner une solution sans UNION, mais ce sera moins efficace. – Quassnoi

0

Vous ne pouvez pas combiner ROWNUM et ORDER BY comme ceci, car le ROWNUM est le numéro de ligne avant il est trié.

Vous devez obtenir plus bavard et d'écrire quelque chose comme

select a.* from (
    select * from table order by col1) a 
    where rownum = 1; 

Consultez la documentation ROWNUM et la pagination.

+0

Thilo, merci - il a été copypasted de mon code. Mais le problème existe toujours - sans rownum = 1 je ne peux pas obtenir le résultat nécessaire pour 2) exemple !!! – zmische

0

Vous feriez mieux de stocker les parties de col1 dans des colonnes séparées. En tout cas, voici une chose de plus que ce que Thilo a dit.En ignorant la partie rownum, quatre lignes de votre table correspondent à la clause WHERE de votre exemple.

Votre clause WHERE en anglais est "le nom et le code correspondent tous les deux ou bien ils ne correspondent pas, mais les correspondances de noms".

C'est une façon compliquée de dire "appariements de noms", et il n'y a pas besoin d'une clause WHERE si compliquée, si c'est vraiment ce que vous voulez dire.

(Si \ s signifie espace, ce dont je ne suis pas sûr, c'est "nom correspond et il y a un espace après le nom.") Si vous voulez voir une "deux" match avant une correspondance de nom seulement , choisissez la première ligne de cette "notation" commande:

ORDER BY 
    CASE WHEN REGEXP_LIKE(col1, name||'\s+'||code) THEN 2 
     WHEN REGEXP_LIKE(col1, name||'\s+) THEN 1 
    ELSE 0 END DESC 

Réponse au commentaire:

votre clause WHERE ne parvient pas à exprimer la mais sinon, ... aspect de vos besoins. Votre clause WHERE exprime simplement S'il y a une ligne avec à la fois CODE et NOM OU une ligne avec juste NOM dedans, montrez-le, et ce n'est pas votre condition.

Pour exprimer OU A, mais sinon A, B, il ne fonctionnera pas à écrire

WHERE <condition A> OR <condition B> 

Vous devez écrire

WHERE <condition A> OR (NOT <condition A> AND <condition B>) 

ou faire quelque chose comme je l'ai fait avec mon expression CASE.

Cela aide-t-il?

+0

Classement est OK, je voulais juste comprendre où était mon erreur dans la clause Where que je pouvais obtenir la ligne nécessaire juste via "clause where". Je ne peux pas accepter que ma clause where soit identique à "name matches". Je pensais qu'il devrait être lu comme ceci: "S'il y a une rangée avec à la fois CODE et NAme - montrez-le, MAIS s'il n'y en a PAS - essayez de trouver la ligne avec juste NAME dedans". – zmische

+0

@zmishce: Voir ce que j'ai ajouté à ma réponse. –

Questions connexes