2017-07-26 4 views
1

J'ai un (simplifié) requête comme ceci:force sous-requête SQL à exécuter d'abord

SELECT DISTINCT this_.person_id AS y0_ 
    FROM coded_entity this_ 
WHERE this_.code  = ? 
    AND this_.tenant_id = ? 
    AND this_.person_id IN (SELECT person_id 
          FROM person that 
          WHERE that.date_voided IS NULL 
           AND that.ssn   = ? 
           AND that.tenant_id  = ?) 

Je voudrais être en mesure de forcer Oracle à exécuter toujours la sous-requête le plus interne d'abord parce que je sais que ce sera toujours plus sélectif. Le code a seulement quelques valeurs alors que le ssn a une multitude beaucoup plus grande. Chaque table aura le même nombre de lignes.

Cependant, dans la pratique, il arrive que l'OCB Oracle décide d'exécuter la requête coded_entity en premier, causant des temps beaucoup plus lents.

Existe-t-il un moyen de forcer ce comportement sans diviser la requête en appels distincts?

+0

Avez-vous pensé à utiliser l'indice de cardinalité? –

+2

Rassemblez les statistiques, Oracle n'est pas si stupide, il doit y avoir une raison pour laquelle le plan exec n'est pas stable. Sinon, en dernier recours, vous pouvez utiliser les indications CARNALITY, MATERIALIZE ou NO_PUSH_PRED – ibre5041

+0

La colonne '' person_id' est-elle une clé primaire dans la table de personne? – krokodilko

Répondre

1

Ma première pensée (indépendamment des clés) est d'essayer ceci:

SELECT DISTINCT this_.person_id AS y0_ 
    FROM coded_entity this_ 
WHERE this_.code  = ? 
    AND this_.tenant_id = ? 
    AND EXISTS (SELECT null 
       FROM person THAT_ 
       WHERE THAT_.date_voided IS NULL 
        AND THAT_.ssn   = ? 
        AND THAT_.tenant_id  = this_.tenant_id 
        AND THAT_.person_id  = this_.person_id) 

Mais une meilleure façon (si person_id est la clé de la personne et pour chaque personne, il y a zéro ou plusieurs entités codées) seraient ceci:

SELECT this_.person_id AS y0_ 
    FROM person_ this_ 
WHERE this_.ssn  = ? 
    AND this_.tenant_id = ? 
    AND this_.date_voided is null 
    AND EXISTS (SELECT null 
       FROM coded_entity THAT_ 
       WHERE THAT_.code = ? 
        AND THAT_.tenant_id  = this_.tenant_id 
        AND THAT_.person_id  = this_.person_id) 
+0

Je vais essayer cela et voir comment il se comporte. Je vous remercie. – AHungerArtist

0

Pas besoin d'une sous-requête. Juste INNER JOIN et filtre pour les enregistrements uniques. Laissez l'optimiseur le comprendre un peu mieux.

SELECT DISTINCT this_.person_id AS y0_ 
FROM coded_entity this_ 
INNER JOIN person that ON this_.person_id = that.person_id 
    AND this_.tenant_id = that.tenant_id 
    AND that.date_voided IS NULL 
    AND that.ssn = ? 
WHERE this_.code  = ? 
    AND this_.tenant_id = ? 
+0

Voir, c'est en fait la requête originale que j'avais, que je réécris. Et ça fonctionnait très bien la plupart du temps. Cependant, de temps en temps, une fusion cartésienne se produirait, causant des temps de requête massivement longs - cassant efficacement les choses. Nos administrateurs de base de données n'ont jamais vraiment trouvé de cause, alors c'était ma tentative d'essayer de m'éloigner des choses qui pourraient causer une situation de jointure. – AHungerArtist

+0

Intéressant. Ils n'étaient pas en mesure de vous dire quelles entrées de données causaient les JOINs longues? Je ne peux rien y voir qui causerait un étrange JOIN. Mauvaises données dans la base de données, peut-être? Ou indice de wonky? – Shawn

+0

En fait, si vous travaillez avec beaucoup de données, vous pourrez peut-être le raccourcir en changeant le JOIN 'AND this_.tenant_id = that.tenant_id' en 'AND et.tenant_id =?'. – Shawn

0

Vous pouvez toujours réécrire le script pour avoir la condition appliquent d'abord comme sous-sélection, puis au-dessus de qui appliquent les conditions supplémentaires, comme ceci:

SELECT DISTINCT this_.person_id AS y0_ 
FROM (
SELECT * 
    FROM coded_entity this_ 
WHERE this_.person_id IN (SELECT person_id 
          FROM person that 
          WHERE that.date_voided IS NULL 
           AND that.ssn   = ? 
           AND that.tenant_id  = ?) 
) sub 
where this_.code  = ? 
    AND this_.tenant_id = ? 

Ou vous pouvez réécrire dans le rejoindre Format:

SELECT DISTINCT this_.person_id AS y0_ 
    FROM coded_entity this_ 
    JOIN person that 
    ON that.person_id = this_person_id 
    AND that.date_voided IS NULL 
    AND that.ssn   = ? 
    AND that.tenant_id  = ? 
WHERE this_.code  = ? 
    AND this_.tenant_id = ? 

Ou ce que je pense va certainement travailler est juste d'avoir correctement les statistiques recueillies sur les deux tables, puis le CBO décidera correctement.