Domanda

Ci scusiamo per una lunga domanda e non un titolo molto descrittivo, ma il mio problema è molto difficile da spiegare brevemente.

Ho tre tabelle di database:

TABLE A:  
AID PK  
STATUS VARCHAR

TABLE B:  
BID PK  
AID FK  
CID FK

TABLE C:  
CID PK  
CREATIONTIME DATE

Per ogni STATUS = 'OK' riga nella tabella A Voglio trovare la riga corrispondente in C che ha l'ora di creazione più recente.

Per prima cosa posso recuperare tutte le righe dalla tabella A dove STATUS = 'OK'.
Quindi posso recuperare tutte le righe corrispondenti dalla tabella B.
Ma come continuare da lì?

Ad esempio:

select AID, CID from B where AID in (select AID from A where STATUS = 'OK')

potrebbe restituire qualcosa del tipo:

AID, CID  
1    1  
2    2  
2    3  
3    4  
4    5  
4    6  

Supponiamo che CID 2 abbia un tempo di creazione successivo rispetto a CID 3 e CID 6 sia più recente di CID 5. Ciò significa che il risultato corretto sarebbe le righe 1, 2, 4 e 6 nella tabella C.

C'è un modo per esprimerlo con una query?

EDIT: Mi dispiace di non essere stato abbastanza specifico. Quello che voglio ottenere sono i CID dalla tabella C.

EDIT: Ho contato le righe restituite con le diverse soluzioni. I risultati sono stati molto interessanti e diversificati:
HAINSTECH: 298 473 file
JMUCCHIELLO: 298 473 file
RUSS CAM: 290 121 file
CHRIS: 344 093 righe
TYRANNOSAURS: 290 119 righe

Non ho ancora avuto il tempo di analizzare in profondità le righe restituite, ma apprezzerei molto le visualizzazioni su quali delle query sono "interrotte". e perché.

È stato utile?

Soluzione

Qualcosa del genere, se ti ho capito correttamente

SELECT
    MAX(CREATIONTIME),
    A.AID
FROM
    A
INNER JOIN
    B
    ON 
    A.AID = B.AID
INNER JOIN
    C
    ON 
    B.CID = C.CID
WHERE
    A.STATUS = 'OK'
GROUP BY
    A.AID

Modifica

Ora ho verificato quanto segue in SQL Server (vorrei ottenere lo stesso risultato in Oracle) e restituisce il CID per il record C con il massimo CREATIONTIME dove STATUS per il record correlato in A id 'OK' .

SELECT C.CID
FROM 
C C
INNER JOIN
B B
ON 
C.CID = B.CID
INNER JOIN
(
    SELECT
        MAX(C.CREATIONTIME) CREATIONTIME,
        A.AID
    FROM
        A A
    INNER JOIN
        B B
        ON 
        A.AID = B.AID
    INNER JOIN
        C C
        ON 
        B.CID = C.CID
    WHERE
        A.STATUS = 'OK'
    GROUP BY
        A.AID
) ABC
ON B.AID = ABC.AID
AND C.CREATIONTIME = ABC.CREATIONTIME

Dimostrato con il seguente T-SQL

DECLARE @A TABLE(AID INT IDENTITY(1,1), STATUS VARCHAR(10))
DECLARE @B TABLE(BID INT IDENTITY(1,1), AID INT, CID INT)
DECLARE @C TABLE(CID INT IDENTITY(1,1), CREATIONTIME DATETIME)

INSERT INTO @A VALUES ('OK')
INSERT INTO @A VALUES ('OK')
INSERT INTO @A VALUES ('NOT OK')
INSERT INTO @A VALUES ('OK')
INSERT INTO @A VALUES ('NOT OK')

INSERT INTO @C VALUES ('10 MAR 2008')
INSERT INTO @C VALUES ('13 MAR 2008')
INSERT INTO @C VALUES ('15 MAR 2008')
INSERT INTO @C VALUES ('17 MAR 2008')
INSERT INTO @C VALUES ('21 MAR 2008')

INSERT INTO @B VALUES (1,1)
INSERT INTO @B VALUES (1,2)
INSERT INTO @B VALUES (1,3)
INSERT INTO @B VALUES (2,2)
INSERT INTO @B VALUES (2,3)
INSERT INTO @B VALUES (2,4)
INSERT INTO @B VALUES (3,3)
INSERT INTO @B VALUES (3,4)
INSERT INTO @B VALUES (3,5)
INSERT INTO @B VALUES (4,5)
INSERT INTO @B VALUES (4,1)
INSERT INTO @B VALUES (4,2)


SELECT C.CID
FROM 
@C C
INNER JOIN
@B B
ON 
C.CID = B.CID
INNER JOIN
(
SELECT
    MAX(C.CREATIONTIME) CREATIONTIME,
    A.AID
FROM
    @A A
INNER JOIN
    @B B
    ON 
    A.AID = B.AID
INNER JOIN
    @C C
    ON 
    B.CID = C.CID
WHERE
    A.STATUS = 'OK'
GROUP BY
    A.AID
) ABC
ON B.AID = ABC.AID
AND C.CREATIONTIME = ABC.CREATIONTIME

Risultati nel seguente

CID
-----------
3
4
5

MODIFICA 2:

In risposta al tuo commento su ciascuna delle affermazioni che danno risultati diversi, ho eseguito alcune delle diverse risposte qui tramite SQL Server 2005 usando i miei dati di test sopra (apprezzo che tu stia usando Oracle). Ecco i risultati

--Expected results for CIDs would be

--CID
-----------
--3
--4
--5

--As indicated in the comments next to the insert statements

DECLARE @A TABLE(AID INT IDENTITY(1,1), STATUS VARCHAR(10))
DECLARE @B TABLE(BID INT IDENTITY(1,1), AID INT, CID INT)
DECLARE @C TABLE(CID INT IDENTITY(1,1), CREATIONTIME DATETIME)

INSERT INTO @A VALUES ('OK') -- AID 1
INSERT INTO @A VALUES ('OK') -- AID 2
INSERT INTO @A VALUES ('NOT OK')
INSERT INTO @A VALUES ('OK') -- AID 4
INSERT INTO @A VALUES ('NOT OK')

INSERT INTO @C VALUES ('10 MAR 2008')
INSERT INTO @C VALUES ('13 MAR 2008')
INSERT INTO @C VALUES ('15 MAR 2008')
INSERT INTO @C VALUES ('17 MAR 2008')
INSERT INTO @C VALUES ('21 MAR 2008')

INSERT INTO @B VALUES (1,1)
INSERT INTO @B VALUES (1,2)
INSERT INTO @B VALUES (1,3) -- Will be CID 3 For AID 1
INSERT INTO @B VALUES (2,2)
INSERT INTO @B VALUES (2,3)
INSERT INTO @B VALUES (2,4) -- Will be CID 4 For AID 2
INSERT INTO @B VALUES (3,3)
INSERT INTO @B VALUES (3,4)
INSERT INTO @B VALUES (3,5)
INSERT INTO @B VALUES (4,5) -- Will be CID 5 FOR AID 4
INSERT INTO @B VALUES (4,1)
INSERT INTO @B VALUES (4,2)

-- Russ Cam
SELECT C.CID, ABC.CREATIONTIME
FROM 
@C C
INNER JOIN
@B B
ON 
C.CID = B.CID
INNER JOIN
(
SELECT
    MAX(C.CREATIONTIME) CREATIONTIME,
    A.AID
FROM
    @A A
INNER JOIN
    @B B
    ON 
    A.AID = B.AID
INNER JOIN
    @C C
    ON 
    B.CID = C.CID
WHERE
    A.STATUS = 'OK'
GROUP BY
    A.AID
) ABC
ON B.AID = ABC.AID
AND C.CREATIONTIME = ABC.CREATIONTIME

-- Tyrannosaurs
select   A.AID,  
         max(AggC.CREATIONTIME)  
from    @A A,  
         @B B,  
         (  select  C.CID,  
             max(C.CREATIONTIME) CREATIONTIME  
            from @C C  
            group by CID
          ) AggC  
where    A.AID = B.AID  
and    B.CID = AggC.CID  
and    A.Status = 'OK'  
group by A.AID

-- jmucchiello
SELECT c.cid, max(c.creationtime)
FROM @B b, @C c
WHERE b.cid = c.cid
 AND b.aid IN (SELECT a.aid FROM @A a WHERE status = 'OK')
GROUP BY c.cid

-- hainstech
SELECT agg.aid, agg.cid
FROM (
    SELECT a.aid
        ,c.cid
        ,max(c.creationtime) as maxcCreationTime
    FROM @C c INNER JOIN @B b ON b.cid = c.cid
        INNER JOIN @A a on a.aid = b.aid
    WHERE a.status = 'OK'
    GROUP BY a.aid, c.cid
) as agg

--chris
SELECT A.AID, C.CID, C.CREATIONTIME
FROM @A A, @B B, @C C
WHERE A.STATUS = 'OK'
AND A.AID = B.AID
AND B.CID = C.CID
AND C.CREATIONTIME = 
(SELECT MAX(C2.CREATIONTIME) 
FROM @C C2, @B B2 
WHERE B2.AID = A.AID
AND C2.CID = B2.CID);

i risultati sono i seguenti

--Russ Cam - Correct CIDs (I have added in the CREATIONTIME for reference)
CID         CREATIONTIME
----------- -----------------------
3           2008-03-15 00:00:00.000
4           2008-03-17 00:00:00.000
5           2008-03-21 00:00:00.000

--Tyrannosaurs - No CIDs in the resultset
AID         
----------- -----------------------
1           2008-03-15 00:00:00.000
2           2008-03-17 00:00:00.000
4           2008-03-21 00:00:00.000


--jmucchiello - Incorrect CIDs in the resultset
cid         
----------- -----------------------
1           2008-03-10 00:00:00.000
2           2008-03-13 00:00:00.000
3           2008-03-15 00:00:00.000
4           2008-03-17 00:00:00.000
5           2008-03-21 00:00:00.000

--hainstech - Too many CIDs in the resultset, which CID has the MAX(CREATIONTIME) for each AID?
aid         cid
----------- -----------
1           1
1           2
1           3
2           2
2           3
2           4
4           1
4           2
4           5

--chris - Correct CIDs, it is the same SQL as mine
AID         CID         CREATIONTIME
----------- ----------- -----------------------
1           3           2008-03-15 00:00:00.000
2           4           2008-03-17 00:00:00.000
4           5           2008-03-21 00:00:00.000

Consiglio di eseguire ciascuna delle risposte fornite su un numero inferiore di record, in modo da poter verificare se il set di risultati restituito è quello previsto.

Altri suggerimenti

SQL> create table a (aid,status)
  2  as
  3  select 1, 'OK' from dual union all
  4  select 2, 'OK' from dual union all
  5  select 3, 'OK' from dual union all
  6  select 4, 'OK' from dual union all
  7  select 5, 'NOK' from dual
  8  /

Tabel is aangemaakt.

SQL> create table c (cid,creationtime)
  2  as
  3  select 1, sysdate - 1 from dual union all
  4  select 2, sysdate - 2 from dual union all
  5  select 3, sysdate - 3 from dual union all
  6  select 4, sysdate - 4 from dual union all
  7  select 5, sysdate - 6 from dual union all
  8  select 6, sysdate - 5 from dual
  9  /

Tabel is aangemaakt.

SQL> create table b (bid,aid,cid)
  2  as
  3  select 1, 1, 1 from dual union all
  4  select 2, 2, 2 from dual union all
  5  select 3, 2, 3 from dual union all
  6  select 4, 3, 4 from dual union all
  7  select 5, 4, 5 from dual union all
  8  select 6, 4, 6 from dual union all
  9  select 7, 5, 6 from dual
 10  /

Tabel is aangemaakt.

SQL> select a.aid
  2       , max(c.cid) keep (dense_rank last order by c.creationtime) cid
  3       , max(c.creationtime) creationtime
  4    from a
  5       , b
  6       , c
  7   where b.aid = a.aid
  8     and b.cid = c.cid
  9     and a.status = 'OK'
 10   group by a.aid
 11  /

       AID        CID CREATIONTIME
---------- ---------- -------------------
         1          1 30-04-2009 09:26:00
         2          2 29-04-2009 09:26:00
         3          4 27-04-2009 09:26:00
         4          6 26-04-2009 09:26:00

4 rijen zijn geselecteerd.

Seleziona il campo che stai cercando usando un join di tutte e 3 le tabelle e quindi limita i risultati a quelli in cui CREATIONDATE è il più recente.

SELECT A.AID, C.CID, C.CREATIONTIME
FROM A A, B B, C C
WHERE A.STATUS = 'OK'
AND A.AID = B.AID
AND B.CID = C.CID
AND C.CREATIONTIME = 
(SELECT MAX(C2.CREATIONTIME) 
FROM C C2, B B2 
WHERE B2.AID = A.AID
AND C2.CID = B2.CID);

EDIT: la mia risposta precedente era una sciocchezza. Questa è ora una riscrittura completa

Questo è in realtà un problema che mi ha infastidito per tutta la mia vita SQL. La soluzione che sto per darti è disordinata ma funziona e apprezzerei chiunque dica "sì, è disordinata ma è l'unico modo per farlo" oppure dì " no, fai questo ... " ;.

Penso che il disagio derivi dall'unione di due date. Il modo in cui succede qui non è un problema in quanto saranno una corrispondenza esatta (hanno esattamente gli stessi dati di root) ma sembra ancora sbagliato ...

Ad ogni modo, suddividendolo, devi farlo in due fasi.

1) Il primo è quello di restituire un set di risultati [AID], [primo tempo di creazione] che ti dà il primo tempo di creazione per ciascun AID.

2) È quindi possibile utilizzare latestCreationTime per estrarre il CID desiderato.

Quindi, per la parte (1), creerei personalmente una vista per farlo solo per mantenere le cose pulite. Ti consente di testare questa parte e farla funzionare prima di fonderla con le altre cose.

create view LatestCreationTimes
as
select b.AID,
       max(c.CreationTime) LatestCreationTime
from   TableB b,
       TableC c
where  b.CID = c.CID
group by b.AID

Nota, a questo punto non abbiamo preso in considerazione lo stato.

È quindi necessario unirlo a TableA (per ottenere lo stato) e TableB e TableC (per ottenere il CID). Devi fare tutti i collegamenti ovvi (AID, CID) e anche unirti alla colonna LatestCreationTime nella vista alla colonna CreationTime in TableC. Non dimenticare anche di unirti alla vista su AID, altrimenti dove due record sono stati creati contemporaneamente per diversi record A, otterrai problemi.

select A.AID,
       C.CID
from   TableA a,
       TableB b,
       TableC c,
       LatestCreationTimes lct
where  a.AID = b.AID
and    b.CID = c.CID
and    a.AID = lct.AID
and    c.CreationTime = lct.LatestCreationTime
and    a.STATUS = 'OK'

Sono certo che funzioni: l'ho testato, ottimizzato i dati, riprovato e si comporta. Almeno fa quello che credo debba fare.

Tuttavia non tratta la possibilità di due identici tempi di creazione nella tabella C per lo stesso record. Immagino che ciò non dovrebbe accadere, tuttavia, a meno che tu non abbia scritto qualche volta che lo limiti assolutamente, deve essere preso in considerazione.

Per fare questo ho bisogno di fare un presupposto su quale preferiresti. In questo caso, dirò che se ci sono due CID corrispondenti, preferiresti avere quello più alto (molto probabilmente è più aggiornato).

select A.AID,
       max(C.CID) CID
from   TableA a,
       TableB b,
       TableC c,
       LatestCreationTimes lct
where  a.AID = b.AID
and    b.CID = c.CID
and    c.CreationTime = lct.LatestCreationTime
and    a.STATUS = 'OK'
group by A.AID

E credo che dovrebbe funzionare per te. Se lo desideri come una query anziché con la vista, quindi:

select A.AID,
       max(C.CID) CID
from   TableA a,
       TableB b,
       TableC c,
       (select b.AID,
               max(c.CreationTime) LatestCreationTime
        from   TableB b,
               TableC c
        where  b.CID = c.CID
        group by b.AID) lct
where  a.AID = b.AID
and    b.CID = c.CID
and    c.CreationTime = lct.LatestCreationTime
and    a.STATUS = 'OK'
group by A.AID

(Ho appena incorporato la vista nella query, altrimenti l'entità è esattamente la stessa).

Non è necessaria una sottoquery, l'aggregazione per determinare il tempo di creazione dell'ultimo cid è semplice:

SELECT a.aid
    ,c.cid
    ,max(c.creationtime) as maxcCreationTime
FROM c INNER JOIN b ON b.cid = c.cid
    INNER JOIN a on a.aid = b.aid
WHERE a.status = 'OK'
GROUP BY a.aid, c.cid

Se davvero non vuoi il tempo di creazione nel tuo set di righe, puoi semplicemente inserirlo in una sottoquery e rilasciarlo dalla proiezione:

SELECT agg.aid, agg.cid
FROM (
    SELECT a.aid
        ,c.cid
        ,max(c.creationtime) as maxcCreationTime
    FROM c INNER JOIN b ON b.cid = c.cid
        INNER JOIN a on a.aid = b.aid
    WHERE a.status = 'OK'
    GROUP BY a.aid, c.cid
) as agg

Codifica nella pagina Web, scusa eventuali errori di sintassi. Inoltre, sono un ragazzo mssql, quindi spero che non ci sia nulla di diverso nel mondo Oracle per questo ..

Nota che lo schema che hai fornito non impone l'unicità di CREATIONTIME per cid. Se ci sono mai due valori cid associati a un determinato valore di aiuto con lo stesso tempo di creazione, verranno entrambi emessi. Se fai affidamento sulla coppia di cid, il momento della creazione è unico, dovresti applicarlo in modo dichiarativo con un vincolo.

Mi sto perdendo qualcosa? Cosa c'è di sbagliato in:

EDIT: Okay, vedo che in realtà vuoi raggruppare per aiuto.

SELECT c.cid FROM b, c,
    (SELECT b.aid as aid, max(c.creationtime) as creationtime
     FROM b, c
     WHERE b.cid = c.cid
       AND b.aid IN (SELECT a.aid FROM a WHERE status = 'OK')
     GROUP BY b.aid) as z
WHERE b.cid = c.cid
  AND z.aid = b.aid
  AND z.creationtime = c.creationtime
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top