2012-02-02 17 views
11

Se ho due domande, che chiamerò horrible_query_1 e ugly_query_2, e voglio eseguire le seguenti due meno operazioni su di essi:Come riutilizzare una query di grandi dimensioni senza ripetizione?

(horrible_query_1) minus (ugly_query_2) 
(ugly_query_2) minus (horrible_query_1) 

O forse ho un terribly_large_and_useful_query, e impostare il risultato Produce I voglio usare come parte di diverse domande future.

Come evitare di copiare e incollare le stesse query in più posti? Come posso "non ripetermi" e seguire i principi ASCIUTTI. È possibile in SQL?

Sto usando Oracle SQL. Le soluzioni SQL portatili sono preferibili, ma se devo usare una funzione specifica di Oracle (incluso PL/SQL) va bene.

risposta

15

Poi

(horrible_query_1_VIEW) minus (ugly_query_2_VIEW) 

(ugly_query_2_VIEW) minus (horrible_query_1_VIEW) 

O, forse, con un with clause:

with horrible_query_1 as (
    select .. .. .. 
    from .. .. .. 
) , 
ugly_query_2 as (
    select .. .. .. 
    .. .. .. 
) 
(select * from horrible_query_1 minus select * from ugly_query_2 ) union all 
(select * from ugly_query_2  minus select * from horrible_query_1) 
+0

Buono, ma vorrei gestire oggetti unici per horrible_query_1 diverso rispetto gestisco gli oggetti unici per ugly_query_2. È possibile dire con quale serie gli articoli sono unici con questa query? EDIT: le viste possono essere la soluzione migliore per SQL semplice. – Buttons840

+2

Certo, avvolgi le tue operazioni con due set in query esterne con selezioni che includono colonne fisse chiamate Source, ad esempio 'SELECT 'horrible_query_1' AS Source, *' e 'SELECT 'ugly_query_2' AS Source, *'. In questo modo la tua UNION ti fornirà tutte le colonne della tua query più l'identificatore della fonte. – JamieSee

+1

L'ultimo esempio, utilizzando le "clausole con" è stato molto utile nel corso degli anni da quando ho posto questa domanda in origine. Queste "clausole con" sono talvolta chiamate espressioni di tabella comuni (o CTE). Questi sono alcuni termini utili da sapere quando si cercano maggiori dettagli. Le clausole con-sono state aggiunte allo standard SQL nel 1999, quindi la maggior parte dei database dovrebbe supportarle, tranne MySQL che non ha ancora implementato questa parte dello standard ventennale. – Buttons840

0

Se si opera con i valori, è possibile scrivere funzioni. Qui trovi le informazioni su come farlo. Funziona fondamentalmente come scrivere una funzione in qualsiasi lingua. È possibile definire parametri e valori di ritorno. Che ti dà la bella possibilità di scrivere codice solo una volta. Ecco come si fa:

http://docs.oracle.com/cd/B19306_01/server.102/b14200/statements_5009.htm

4

Se si desidera riutilizzare il testo SQL delle query, quindi la definizione di vista è il modo migliore, come descrittore in precedenza.

Se si vuole riutilizzare il risultato delle domande, allora si dovrebbe considerare tabelle temporanee globali. Queste tabelle temporanee memorizzano i dati per la durata della sessione o della transazione (a seconda di quale si sceglie). Sono davvero utili nel caso in cui sia necessario riutilizzare i dati calcolati più volte, soprattutto se le query sono effettivamente "brutte" e "orribili" (che significa lunga esecuzione). Vedere Temporary tables per ulteriori informazioni.

Se è necessario mantenere i dati più lunghi di una sessione, è possibile considerare le viste materializzate .

1

Hai provato a utilizzare il suggerimento RESULT_CACHE nelle tue query? Inoltre, è possibile

ALTER SESSION SET RESULT_CACHE_MODE=FORCE 

e vedere se aiuta.

+1

Questo può aiutare con le prestazioni, ma penso che questa domanda riguardi più lo stile di codifica. –

1

Poiché si utilizza Oracle, creerei le funzioni TABLE pipelined. La funzione accetta parametri e restituisce un oggetto (che devi creare) e quindi SELEZIONA * o anche colonne specifiche da esso utilizzando la funzione TABLE() e può utilizzarlo con una clausola WHERE o con JOINs. Se si desidera un'unità di riutilizzo (una funzione) non si è limitati a restituire solo valori (vale a dire una funzione scalare) è possibile scrivere una funzione che restituisce righe o recordset. qualcosa di simile:

FUNCTION RETURN_MY_ROWS(Param1 IN type...ParamX IN Type) 
      RETURN PARENT_OBJECT PIPELINED 
      IS 
      local_curs cursor_alias; --you need a cursor alias if this function is in a Package 
      out_rec ROW_RECORD_OF_CUSTOM_OBJECT:=ROW_RECORD_OF_CUSTOM_OBJECT(NULL, NULL,NULL) --one NULL for each field in the record sub-object 
     BEGIN 
     OPEN local_curs FOR 
      --the SELECT query that you're trying to encapsulate goes here 
      -- and it can be very detailed/complex and even have WITH() etc.. 
     SELECT * FROM baseTable WHERE col1 = x; 

    -- now that you have captured the SELECT into a Cursor 
    -- here you put a LOOP to take what's in the cursor and put it in the 
    -- child object (that holds the individual records) 
      LOOP 
     FETCH local_curs --opening the ref-cursor 
      INTO out_rec.COL1, 
       out_rec.COL2, 
       out_rec.COL3; 
     EXIT WHEN local_curs%NOTFOUND; 
     PIPE ROW(out_rec); --piping out the Object 
     END LOOP; 
     CLOSE local_curs; -- always do this 
     RETURN; -- we're now done 
    END RETURN_MY_ROWS; 

dopo aver fatto questo, è possibile utilizzarlo in questo modo

SELECT * FROM TABLE(RETURN_MY_ROWS(val1, val2)); 

è possibile inserire SELECT o addirittura CREATE TABLE fuori di esso, si può avere nei join .

altre due cose parlare:

--ROW_RECORD_OF_CUSTOM_OBJECT is something along these lines 
CREATE or REPLACE TYPE ROW_RECORD_OF_CUSTOM_OBJECT AS OBJECT 
(
    col1 type; 
    col2 type; 
     ... 
    colx type; 
); 

e PARENT_OBJECT è una tabella dell'altro oggetto (con le definizioni di campo) abbiamo appena fatto

create or replace TYPE PARENT_OBJECT IS TABLE OF ROW_RECORD_OF_CUSTOM_OBJECT; 

quindi questa funzione necessita di due oggetti per supportare ma uno è un record, l'altro è una tabella di quel record (devi prima creare il record).

In breve, la funzione è facile da scrivere, è necessario un oggetto figlio (con campi) e un oggetto padre che ospiterà l'oggetto figlio di tipo TABLE dell'oggetto figlio e verrà aperto l'originale tabella di base che preleva SQL in un SYS_REFCURSOR (che potrebbe essere necessario per l'alias) se si è in un pacchetto e si legge da quel cursore da un loop ai singoli record. La funzione restituisce un tipo di PARENT_OBJECT ma al suo interno racchiude il sottooggetto record con i valori del cursore.

Spero che questo funziona per voi (ci possono essere problemi di permissioning con la DBA, se si desidera creare oggetti e funzioni tabella) */

Problemi correlati