Domanda

Voglio controllare in modo efficiente se una tabella contiene tutte le righe che corrispondono e non corrispondono , dove le condizioni sono arbitrari.

In Oracle, questo quasi funziona:

select count(*) from dual
where exists (
  select * from people
  where (<condition A>)
  and not (<condition B>)
);
-- returns zero if all rows that match <condition A> also match <condition B>
-- (well, almost)

Il problema è che i valori nulli temute. Diciamo è name = 'Aaron' e è età = 21 . La query correttamente individuare eventuali Aarons la cui età non è uguale a 21, ma non riesce a individuare eventuali Aarons la cui età è nullo.

Ecco una soluzione corretta, ma su una tabella con milioni di record si può prendere un po ':

select (
  select count(*) from people
  where (<condition A>)
) - (
  select count(*) from people
  where (<condition A>)
  and (<condition B>)
) from dual;
-- returns zero if all rows that match <condition A> also match <condition B>
-- (correct, but it is s l o w...)

Purtroppo le due condizioni saranno arbitraria, complessa, mutevole, e in generale fuori dal mio controllo. Essi sono generati dal framework di persistenza dell'applicazione da ricerche degli utenti, e mentre cerchiamo di mantenere i nostri indici con i nostri utenti, un sacco di tempo che causerà grandi scansioni di tabella (che è il motivo che prima query con il "esiste" clausola è modo molto più veloce rispetto al secondo -. si può fermare, non appena ne trova uno record corrispondente, e non avere a che fare due scansioni separate)

Come posso fare questo in modo efficace, senza futzing up sulle null?

È stato utile?

Soluzione

Se si ha il campo id, provare:

selezionare il conteggio (*) da duale dove esiste ( select * from persone dove (cond a) e zzz.id not in (select id da persone dove (cond b)) );

Altri suggerimenti

Supponendo che il vostro tavolo ha un id chiave primaria, un possibile approccio è:

select count(*)
from people p1
left join people p2
  on (p1.id = p2.id
  and (p2.<condition A>)
  and (p2.<contition B>))
where p1.<condition A>
  and p2.id IS NULL

Avete bisogno di un po 'di semplice di pre-elaborazione delle condizioni (precedere ogni nome di colonna con p1. o p2. a seconda dei casi), ma questo è molto più facile che le condizioni correttamente negando con i problemi NULL di cui parli.

LEFT JOIN sometable ON whatever WHERE ... AND sometable.id IS NULL è un modo popolare per esprimere "e non c'è nessun record corrispondente nella sometable che ha soddisfatto il vincolo whatever, quindi mi aspetterei un buon motore per essere ben sintonizzato per ottimizzare che linguaggio tanto quanto consentito dagli indici disponibili.

Se per ogni colonna Null che si può trovare con un valore fittizio che non dovrebbe mai essere valido, allora si potrebbe fare qualcosa di simile:

select count(*) from dual
where exists (
  select * from (
    select NVL(name, 'No Name') name, NVL(age, -1) age from people
    )
  where (<condition A>)
  and not (<condition B>)
);

Si sarebbe probabilmente vuole creare indici basati su funzioni su quelle espressioni.

Questa è certamente più facile che l'analisi attraverso le condizioni in fase di esecuzione e cercando di sostituire i nomi di colonna con le espressioni NVL, e dovrebbe avere lo stesso risultato finale.

Non credo che ci sia qualcosa che si può fare se le condizioni sono completamente arbitraria. E 'possibile "ri-scrittura" le condizioni ad un certo punto sulla base di alcune regole?

Credo che se si esegue questa operazione:

... where not (age = 21) ....

che si traduce internamente in:

... where (age != 21) ...

si ottiene anche alcuni record, perché non corrisponde a valori nulli, giusto?

Ma se si fa questo:

... where not (age = 21 and age is not null) ....

che si traduce internamente in:

... where (age != 21 or age is null) ....

quindi si otterrà i risultati attesi. (Giusto?)

Quindi, si può forzare tutti i confronti nelle vostre condizioni per includere un test nullo, sia nella forma (... o x è nullo) o (... e x non è nullo)?

Una soluzione è quella di sbarazzarsi di qualsiasi nulli all'interno dei parametri del confronto prima, cioè, aggiungere le stringhe di valori nulli o sostituire con i valori impossibile per quella colonna. Per un esempio del primo:

select x, y
  from foo
  join bar on bar.a||'x' = foo.a||'x' /* Replace "=" with "<>" for opposite result */
;

Sostituzione valori nulli:

select x, y
  from foo
  join bar on nvl(bar.a, 'x') = nvl(foo.a, 'x') -- Ditto
;

Ora, la seconda opzione è più difficile (almeno in Oracle 9.2) perché si deve fare in modo che il valore di sostituzione è lo stesso tipo di dati della colonna che sostituisce (NVL è un po 'sciocco del genere), e che si tratta di un valore esterno precisione del tipo di dati colonna (ad esempio, per 9999 number(3)), ma potrebbe essere possibile farlo funzionare con indici. Naturalmente, ciò non è possibile se la colonna utilizza già massima precisione / lunghezza.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top