Voici ma requête
SELECT
COUNT(C.SETID)
FROM
MYCUSTOMER C
LEFT OUTER JOIN MYCUSTOPTION CO
ON
(C.SETID = CO.SETID
AND C.CUST_ID = CO.CUST_ID
AND CO.effdt = (
SELECT MAX(COI.EFFDT)
FROM MYCUSTOPTION COI
WHERE
COI.SETID = CO.SETID
AND COI.CUST_ID = CO.CUST_ID
AND COI.EFFDT <=SYSDATE
)
)
et voici le message d'erreur que je reçois ..
Qu'est-ce que je fais mal???
vous pouvez réécrire cela en poussant la sous-requête de manière à ce qu'elle ne soit pas jointe à l'extérieur:
select Count(C.setid)
from mycustomer C
left outer join (select *
from mycustoption co
where co.effdt <= (select Max(COI.effdt)
from mycustoption COI
where COI.setid = co.setid
and COI.cust_id = co.cust_id
and COI.effdt <= sysdate)) co
on ( C.setid = CO.setid
and C.cust_id = CO.cust_id )
Eh bien, Oracle ne prend apparemment pas en charge l'utilisation d'une sous-requête à l'intérieur de la condition de jointure pour une jointure externe. Vous devez donc vous débarrasser de la sous-requête.
La question est, pourquoi est-elle là? Vous avez des conditions "<=" à deux endroits, donc le prédicat dit essentiellement "tous les enregistrements dont la date effective n'est pas plus tard que la dernière date effective qui n'est pas plus tard que maintenant". Si c'est ce que vous voulez vraiment, vous pouvez le simplifier pour "tous les enregistrements dont la date d'entrée en vigueur est au plus tard", à savoir:
ON
(C.SETID = CO.SETID
AND C.CUST_ID = CO.CUST_ID
AND CO.effdt <= SYSDATE
)
Voila, pas de sous-requête.
Mais est-ce vraiment ce que vous voulez, ou vouliez-vous dire que le premier "<=" soit juste "=" - c'est-à-dire trouver l'enregistrement avec la date d'entrée en vigueur la plus récente avant maintenant? Si c'est ce que vous voulez vraiment, ce sera plus complexe à réécrire.
j'ai également fait face à ce problème aujourd'hui et
SELECT
COUNT(C.SETID)
FROM
MYCUSTOMER C
LEFT OUTER JOIN MYCUSTOPTION CO
ON
(C.SETID = CO.SETID
AND C.CUST_ID = CO.CUST_ID
AND CO.effdt IN (
SELECT MAX(COI.EFFDT)
FROM MYCUSTOPTION COI
WHERE
COI.SETID = CO.SETID
AND COI.CUST_ID = CO.CUST_ID
AND COI.EFFDT <=SYSDATE
)
)
Votre question a déjà reçu une réponse, mais quelqu'un peut avoir un cas légèrement différent où il doit obtenir le dernier EFFDT sur la base d'une colonne, au lieu d'une date fixe. Pour ces cas, je n'ai trouvé qu'une seule option IMPERFECT et une seule solution UGLY ...
Option imparfaite:
SELECT ...
FROM MYTABLE N, CUST_OPT C
WHERE etc...
AND C.SETID (+) = N.SETID
AND C.CUST_ID (+) = N.CUST_ID
AND NVL(C.EFFDT,TO_DATE('01011900','DDMMYYYY')) = NVL((SELECT MAX(EFFDT)
FROM CUST_OPT SC
WHERE SC.SETID = C.SETID
AND SC.CUST_ID = C.CUST_ID
AND SC.EFFDT <= N.ISSUE_DT)
,TO_DATE('01011900','DDMMYYYY'))
Il s'agit d'une option imparfaite car si la table CUST_OPT a des dates futures, mais aucune date actuelle (<= N.ISSUE_DT), la jointure externe ne fonctionnera pas et aucune ligne ne sera retournée. En termes généraux PeopleSoft (oui j'ai vu votre SETID + EFFDT là! ;-D) cela n'arriverait pas très souvent car les gens ont tendance à en créer un 01/01/1900 EFFDT pour rendre une première valeur efficace depuis "pour toujours", mais depuis ce n'est pas toujours le cas; nous avons également une solution laide:
J'ai également trouvé une option UGLY (mais je la recommande en fait, et elle résout le problème, alors appelons-la une solution), qui est la suivante:
SELECT n.field1, n.field2,
CASE WHEN NVL(c.EFFDT,n.ISSUE_DT-1)<=n.ISSUE_DT THEN c.field1 ELSE NULL END,
CASE WHEN NVL(c.EFFDT,n.ISSUE_DT-1)<=n.ISSUE_DT THEN c.field2 ELSE NULL END
FROM MYTABLE N, CUST_OPT C
WHERE etc...
AND C.SETID (+) = N.SETID
AND C.CUST_ID (+) = N.CUST_ID
AND NVL(C.EFFDT,TO_DATE('01011900','DDMMYYYY')) = NVL((SELECT MAX(EFFDT)
FROM CUST_OPT SC
WHERE SC.SETID = C.SETID
AND SC.CUST_ID = C.CUST_ID
AND SC.EFFDT <= N.ISSUE_DT)
,NVL( (SELECT MIN(EFFDT)
FROM CUST_OPT SC
WHERE SC.SETID = C.SETID
AND SC.CUST_ID = C.CUST_ID
AND SC.EFFDT >= N.ISSUE_DT)
,TO_DATE('01011900','DDMMYYYY')
)
)
Cette option retournera des lignes FUTURES qui doivent être ignorées! Nous ajoutons donc les conditions sur l'instruction SELECT qui IGNORERONT les valeurs retournées, si elles n'étaient pas destinées à être récupérées. Comme je l'ai dit ... c'est une solution UGLY, mais c'est une solution.
Pour ma solution laide, si les lignes seront traitées plus tard dans un moteur d'application ou PL/SQL ou autre; vous pouvez, au lieu d'avoir une instruction CASE pour chaque colonne, ajouter simplement une nouvelle colonne qui vous dira que vous avez récupéré des données "incorrectes" et ignorer les champs plus tard dans votre code, basé sur cette colonne, comme ceci:
CASE WHEN NVL(c.EFFDT,n.ISSUE_DT-1)<=n.ISSUE_DT THEN 'N' ELSE 'Y' END AS IGNORE_CUST_OP_COLS