2015-10-16 10 views
6

Voglio trovare i mutuatari che hanno preso tutti i tipi di prestito.I mutuatari che prendono tutti i prestiti usando NOT EXISTS

schema:

loan (number (PKEY), type, min_rating) 
borrower (cust (PKEY), no (PKEY)) 

Esempio tabelle:

number | type  | min_rating 
------------------------------ 
L1  | student | 500 
L2  | car  | 550 
L3  | house | 500 
L4  | car  | 700 
L5  | car  | 900 

cust | no 
----------- 
Jim | L2 
Tom | L1 
Tom | L2 
Tom | L3 
Tom | L4 
Tom | L5 
Bob | L3 

La risposta qui sarebbe "Tom".

Posso semplicemente contare il numero totale di prestiti e confrontare il numero di prestiti del mutuatario con quello, ma NON sono autorizzato a (questo è un esercizio di compiti a casa), ai fini di questo compito e apprendimento.

Volevo utilizzare la doppia negazione dove trovo prima i mutuatari che non hanno preso tutti i prestiti e trovano i mutuatari che non sono in quel set. Voglio usare il nesting con NOT EXISTS dove trovo prima i mutuatari che non hanno preso tutti i prestiti ma non sono stato in grado di creare una query di lavoro per quello.

+3

Pensa al problema in questo modo: non esiste un prestito che il prestito non ha. –

+1

... che è * due * 'NOT EXISTS'. –

+0

@GordonLinoff Sì, è quello che sto cercando di fare. Ma ho iniziato col cercare di trovare i clienti che non hanno preso tutti i prestiti usando 'SELECT b.cust FROM borrower b, loan l WHERE l.no = b.number AND NOT EXISTS (SELECT * FROM loan)' e questo non è corretto quindi mi stavo chiedendo dove dovrei sistemarlo. – maregor

risposta

3

Un approccio semplice è quello di utilizzare i fatti:

  • che un outer join si null quando non c'è join
  • coalesce (dà) può trasformare un nulla in uno spazio (che sarà sempre meno che un valore reale)

Pertanto, il minimo si fusero prestito numero di una persona che non ha tutti i tipi di prestito sarà vuoto:

select cust 
from borrower b 
left join loan l on l.number = b.no 
group by cust 
having min(coalesce(l.number, '')) > '' 

Il group-by elude in modo ordinato il problema di selezionare le persone più di una volta (e le subquery subdole che spesso richiedono), e fa affidamento sul presupposto abbastanza ragionevole che un numero di prestito non sia mai vuoto. Anche se fosse possibile, potresti comunque trovare un modo per far funzionare questo schema (ad esempio, fondere il min_rating con un numero negativo, ecc.).

È possibile che query può essere riscritta, forse più essere letti, per usare un NOT IN espressione:

select distinct cust 
from borrower 
where cust not in (
    select cust 
    from borrower b 
    left join loan l on l.number = b.no 
    where l.number is null 
) 

Utilizzando il fatto che un mancato join restituisce tutti i valori null, la clausola WHERE della query interna continua solo mancati join.

È necessario utilizzare DISTINCT per evitare che i mutuatari compaiano due volte.


lo schema ha un problema - v'è una relazione molti-a-molti tra il debitore e il carico, ma lo schema gestisce questo male. borrower dovrebbe avere una riga per ogni persona, e un altro associazione tavolo per registrare il fatto che un mutuatario ha preso un prestito:

create table borrower (
    id int, 
    name varchar(20) 
    -- other columns about the person 
); 

create table borrrower_loan (
    borrower_id int, -- FK to borrower 
    load_number char(2) -- FK to loan 
); 

Ciò significa che non avrebbe bisogno l'operatore distinct (da sinistra a voi capire perché), ma gestisce anche situazioni di vita reale come due mutuatari che hanno lo stesso nome.

+0

E 'possibile farlo con query annidate usando NOT IN o NOT EXISTS? – maregor

+0

@maregor si - vedi modifica per una spiegazione completa. – Bohemian

1

Penso che un buon primo passo sarebbe prendere un prodotto cartesiano * dei mutuatari e dei prestiti, quindi utilizzare una clausola where per filtrare verso il basso a quelli che non sono presenti nella tabella dei "mutuatari". (Anche se penso che userebbe un NOT IN piuttosto che un NOT EXISTS, quindi potrebbe non essere esattamente quello che hai in mente?)

(* Con l'avvertenza che i prodotti cartesiani sono una cosa terribile da fare, e tu? D bisogno di riflettere molto attentamente sulle prestazioni prima di farlo nella vita reale)

ETA: la variante NON ESISTE potrebbe essere simile a questa: Prendere il prodotto cartesiano come prima, fare una sottoquery correlata per la combinazione di mutuatario e prestito, quindi filtrare se questa query restituisce qualsiasi riga, utilizzando una clausola WHERE con una condizione NOT EXISTS.

+0

Penso che sia' NOT IN' che 'NOT EXISTS' possono funzionare ma sto cercando qualcosa di semplice e non troppo correlato al nesting, se possibile. – maregor

+0

Questo avrebbe una sottoquery e non sarebbe correlato. Consentitemi di modificare un po 'di più sull'alternativa NOT EXISTS ... – hugh

0
select cust from borrower 
except 
select t.cust 
from (select distinct cust,number 
     from borrower cross join loan) t 
left join borrower b on t.cust = b.cust and t.number = b.num 
where b.num is null 

Fiddle

Prendete un cross join di clienti provenienti da mutuatari e nums prestito da prestiti. Quindi left join la tabella borrower per trovare i clienti che non hanno preso tutti i prestiti. Infine, utilizzare except per selezionare i clienti che hanno preso tutti i prestiti.

+0

C'è un modo per farlo senza usare i join? – maregor

+0

proverò a trovare una risposta senza un 'join' –

Problemi correlati