2015-01-02 12 views
8

Sto affrontando un problema di progetto di cui sto lavorando, non posso darvi codice vero e proprio, ma ho creato un codice di esempio eseguibile come di seguitoSELECT da tavolo con variano in lista nella clausola WHERE

Qui temp e temp_id sono due tavoli

  1. temp tabella contiene elenco separato da virgole ids quali è VARCHAR2
  2. temp_id tabella contiene ids reale che è NUMBER

voglio righe dalla tabella temp_id cercare ottenendo ids da virgole elenco di ID separato da temp tavolo

//DDLs to create table 
CREATE TABLE temp(ids VARCHAR2(4000)); 
CREATE TABLE temp_id(data_id NUMBER); 

//DMLs to populate test data 
INSERT INTO temp VALUES('1, 2, 3'); 

INSERT INTO temp_id VALUES(1); 
INSERT INTO temp_id VALUES(2); 
INSERT INTO temp_id VALUES(3); 
INSERT INTO temp_id VALUES(4); 
INSERT INTO temp_id VALUES(5); 

Questa query non funziona

SELECT * FROM temp_id WHERE data_id IN (SELECT to_number(COLUMN_VALUE) FROM XMLTABLE(SELECT ids FROM temp)); 

interrogazione lavoro

SELECT * FROM temp_id WHERE data_id IN (SELECT to_number(COLUMN_VALUE) FROM XMLTABLE('1, 2, 3')); 

Qui la differenza tra due query di cui sopra è che sto usando la colonna dalla tabella temp nella prima query e diretta citata varchar2 nella seconda query. Non capisco perché non funziona? Mi manca qualcosa? Penso che potrebbe esserci qualche discrepanza tra i tipi di dati, ma non siamo in grado di capirlo.

+0

'1, 2, 3' non è un numero valido, è una stringa! – SMA

+0

Ho fatto 'to_number (COLUMN_VALUE)'. È possibile controllare la seconda query funziona. – eatSleepCode

+0

Si tratta di un problema di 'Liste di liste IN'. Vedi la mia risposta. –

risposta

6

Il vostro requisito è chiamato come Variabili in elenco.Vedere Varying IN list of values in WHERE clause

Motivo:IN ('1, 2, 3') è NON stessa IN (1, 2, 3) O IN('1', '2', '3')

Quindi,

SELECT * FROM WHERE temp_id data_id IN (SELECT ID da temp);

è stessa

SELECT * FROM WHERE temp_id data_id IN ('1, 2, 3');

che avrebbe gettato un errore ORA-01722: invalid number -

SQL> SELECT * FROM temp_id WHERE data_id IN('1, 2, 3'); 
SELECT * FROM temp_id WHERE data_id IN('1, 2, 3') 
             * 
ERROR at line 1: 
ORA-01722: invalid number 


SQL> SELECT * FROM temp_id WHERE data_id IN(SELECT ids FROM temp); 
SELECT * FROM temp_id WHERE data_id IN(SELECT ids FROM temp) 
               * 
ERROR at line 1: 
ORA-01722: invalid number 

NON stessa

SELECT * FROM WHERE temp_id data_id IN (1, 2, 3);

che darebbe corretta uscita -

SQL> SELECT * FROM temp_id WHERE data_id IN(1, 2, 3); 

    DATA_ID 
---------- 
     1 
     2 
     3 

Soluzione:

Per il vostro requisito, si può raggiungere in questo modo -

SQL> SELECT * FROM temp; 

IDS 
-------------------------------------------------------------- 
1, 2, 3 

SQL> SELECT * FROM temp_id; 

    DATA_ID 
---------- 
     1 
     2 
     3 
     4 
     5 

SQL> WITH data AS 
    2 (SELECT to_number(trim(regexp_substr(ids, '[^,]+', 1, LEVEL))) ids 
    3 FROM temp 
    4  CONNECT BY instr(ids, ',', 1, LEVEL - 1) > 0 
    5 ) 
    6 SELECT * FROM temp_id WHERE data_id IN 
    7 (SELECT ids FROM data 
    8 ) 
    9/

    DATA_ID 
---------- 
     1 
     2 
     3 

In alternativa, si può crea la tua funzione TABLE o Funzione pipelined per ottenere ciò. Il tuo obiettivo dovrebbe essere split the comma-separated IN list into multiple rows. Come lo fai dipende da te!

demo funzionante

Facciamo un esempio dello standard EMP tavolo SCOTT schema.

Ho un elenco di posti di lavoro in una stringa, e voglio contare i dipendenti per quei lavori:

SQL> SET serveroutput ON 
SQL> DECLARE 
    2 str VARCHAR2(100); 
    3 cnt NUMBER; 
    4 BEGIN 
    5 str := q'[CLERK,SALESMAN,ANALYST]'; 
    6 SELECT COUNT(*) INTO cnt FROM emp WHERE JOB IN (str); 
    7 dbms_output.put_line('The total count is '||cnt); 
    8 END; 
    9/
The total count is 0 

PL/SQL procedure successfully completed. 

Oh! Quello che è successo? La tabella emp standard dovrebbe fornire un'uscita 10. Il motivo è che il numero varia nella lista IN.

Vediamo il modo corretto:

SQL> SET serveroutput ON 
SQL> DECLARE 
    2 str VARCHAR2(100); 
    3 cnt NUMBER; 
    4 BEGIN 
    5 str := q'[CLERK,SALESMAN,ANALYST]'; 
    6 SELECT COUNT(*) 
    7 INTO cnt 
    8 FROM emp 
    9 WHERE job IN 
10  (SELECT trim(regexp_substr(str, '[^,]+', 1, LEVEL)) 
11  FROM dual 
12  CONNECT BY instr(str, ',', 1, LEVEL - 1) > 0 
13  ); 
14 dbms_output.put_line('The total count is '||cnt); 
15 END; 
16/
The total count is 10 

PL/SQL procedure successfully completed. 
0

C'è un altro modo in cui questo potrebbe essere realizzato, vale a dire utilizzando LIKE:

SELECT ti.* 
    FROM temp t, temp_id ti 
WHERE ',' || REPLACE(t.ids, ' ') || ',' LIKE '%,' || TO_CHAR(ti.data_id) || ',%' 

(ho usato REPLACE() sopra per sbarazzarsi del spazio bianco estraneo nella lista ID - semplifica le cose.) In alternativa, è possibile utilizzare REGEXP_LIKE():

SELECT ti.* 
    FROM temp t, temp_id ti 
WHERE REGEXP_LIKE(REPLACE(t.ids, ' '), '(^|,)' || TO_CHAR(ti.data_id) || '(,|$)') 

[I caratteri del caret (^) e il simbolo del dollaro ($) devono corrispondere rispettivamente all'inizio e alla fine della stringa, in modo che l'ID possa corrispondere a qualcosa all'inizio della stringa (che termina con una virgola o la fine della stringa) o qualcosa che inizia con una virgola (di nuovo, che termina con una virgola o la fine della stringa).]