Pour ceux qui rencontreraient ce bug, je poste ici sa description et sa solution :

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
SELECT DISTINCT C.NUMORDRE
FROM A
      LEFT OUTER JOIN C ON 
     (C.NUMORDRE = A.NUMORDRE)
WHERE (A.CARTE is null)
        AND (A.NUMORDRE  not in 
(
select  B.NUMORDRE 
from B
 inner join D ON (D.REF = B.REF) AND (D.TAG = 1) 
where (B.REF like 'A%') AND (B.NUMORDRE = A.NUMORDRE)
)
)
me donne un résultat erroné (vide dans mon cas)

alors que

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
SELECT DISTINCT C.NUMORDRE
FROM A
      LEFT OUTER JOIN C ON 
     (C.NUMORDRE = A.NUMORDRE)
WHERE (A.CARTE is null)
        AND (A.NUMORDRE  not in 
(
select  B.NUMORDRE 
from B
 inner join D ON (D.REF = B.REF) AND (D.TAG = 1) 
where (B.NUMORDRE > 0) AND
 (B.REF like 'A%') AND (B.NUMORDRE = A.NUMORDRE)
)
)
donne le bon résultat (une ligne dans mon cas)

La différence entre les deux ? l'ajout de (B.NUMORDRE > 0) AND dans la clause where de la sous-requête.
Cette condition toujours vraie a pour but de forcer le l'optimiseur de requête à produire un plan corect du join.

En effet, dans le premier cas, le plan de la sous-requête était
Code : Sélectionner tout - Visualiser dans une fenêtre à part
PLAN JOIN (ECRITURES_1 INDEX (RDB$FOREIGN106,RDB$FOREIGN111),EXERCICES_1 INDEX (RDB$18))
alors que dans le second, il est
Code : Sélectionner tout - Visualiser dans une fenêtre à part
PLAN JOIN (EXERCICES_1 NATURAL,ECRITURES_1 INDEX (KYECRITURESREFEXERCICE,RDB$FOREIGN106,RDB$FOREIGN111))
Le truc vient de Ann Harrison, sur une page à entête Firebird.

Mais ce qu'elle indique marche avec Interbase, mais pas avec Firebird (1.5).

J'ajouterai qu'il s'agit bien d'un problème de plan, car, en mettant un bon plan explicite,
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
SELECT DISTINCT C.NUMORDRE
FROM A
      LEFT OUTER JOIN C ON 
     (C.NUMORDRE = A.NUMORDRE)
WHERE (A.CARTE is null)
        AND (A.NUMORDRE  not in 
(
select  B.NUMORDRE 
from B
 inner join D ON (D.REF = B.REF) AND (D.TAG = 1) 
where (B.REF like 'A%') AND (B.NUMORDRE = A.NUMORDRE)
PLAN JOIN (B INDEX (RDB$FOREIGN111),D INDEX (RDB$18))
)
)
on obtient aussi le résultat correct, et cette fois-ci aussi bien avec Interbase que Firebird.

A noter que le problème vient de ce que B.REF est indexé, et que FB 1.5/IB 6, à tort, veut introduire cet index (RDB$FOREIGN106) dans la plan du join