Oracle stored procedure con parametri per la clausola IN
-
04-07-2019 - |
Domanda
Come posso creare una procedura memorizzata Oracle che accetta un numero variabile di valori di parametro utilizzati per alimentare una clausola IN?
Questo è ciò che sto cercando di ottenere. Non so come dichiarare in PLSQL per il passaggio di un elenco variabile di chiavi primarie delle righe che voglio aggiornare.
FUNCTION EXECUTE_UPDATE
( <parameter_list>
value IN int)
RETURN int IS
BEGIN
[...other statements...]
update table1 set col1 = col1 - value where id in (<parameter_list>)
RETURN SQL%ROWCOUNT ;
END;
Inoltre, vorrei chiamare questa procedura da C #, quindi deve essere compatibile con le funzionalità .NET.
Grazie, Robert
Soluzione
L'uso di CSV è probabilmente il modo più semplice, supponendo che tu possa essere sicuro al 100% che i tuoi elementi non contengano stringhe.
Un modo alternativo, e probabilmente più robusto, per farlo è quello di creare un tipo personalizzato come una tabella di stringhe. Supponiamo che le tue stringhe non siano mai più lunghe di 100 caratteri, quindi potresti avere:
CREATE TYPE string_table AS TABLE OF varchar2(100);
È quindi possibile passare una variabile di questo tipo nella procedura memorizzata e fare riferimento direttamente a essa. Nel tuo caso, qualcosa del genere:
FUNCTION EXECUTE_UPDATE(
identifierList string_table,
value int)
RETURN int
IS
BEGIN
[...other stuff...]
update table1 set col1 = col1 - value
where id in (select column_value from table(identifierList));
RETURN SQL%ROWCOUNT;
END
La funzione table ()
trasforma il tuo tipo personalizzato in una tabella con una singola colonna " COLUMN_VALUE " ;, che puoi quindi trattare come qualsiasi altra tabella (quindi fai join o, in questo caso, subselect).
Il bello di questo è che Oracle creerà un costruttore per te, quindi quando chiami la tua procedura memorizzata puoi semplicemente scrivere:
execute_update(string_table('foo','bar','baz'), 32);
Suppongo che tu possa gestire la creazione di questo comando a livello di codice da C #.
A parte questo, nella mia azienda abbiamo un certo numero di questi tipi personalizzati definiti come standard per elenchi di stringhe, doppi, ints e così via. Utilizziamo anche Oracle JPublisher per essere in grado di mappare direttamente da questi tipi nei corrispondenti oggetti Java. Ho dato una rapida occhiata in giro ma non sono riuscito a vedere alcun equivalente diretto per C #. Ho pensato di menzionarlo nel caso in cui gli sviluppatori Java incontrassero questa domanda.
Altri suggerimenti
Ho trovato il seguente articolo di Mark A. Williams che pensavo potesse essere un'utile aggiunta a questa discussione. L'articolo fornisce un buon esempio del passaggio di matrici da C # a proc PL / SQL mediante matrici associative (TYPE myType IS TABLE OF mytable.row% INDICE TIPO DI PLS_INTEGER):
Penso che non esista un modo diretto per creare procedure con un numero variabile di parametri. Tuttavia ci sono alcune, almeno parziali soluzioni al problema, descritte qui .
- Se ci sono alcuni tipi tipici di chiamata, il sovraccarico della procedura può aiutare.
- Se esiste un limite superiore nel numero di parametri (e il loro tipo è noto anche in anticipo), possono essere utili i valori predefiniti dei parametri.
- L'opzione migliore è forse l'uso delle variabili del cursore che sono puntatori ai cursori del database.
Sfortunatamente non ho esperienza con gli ambienti .NET.
Perché non usare solo un lungo elenco di parametri e caricare i valori in un costruttore di tabelle? Ecco l'SQL / PSM per questo trucco
UPDATE Foobar
SET x = 42
WHERE Foobar.keycol
IN (SELECT X.parm
FROM (VALUES (in_p01), (in_p02), .., (in_p99)) X(parm)
WHERE X.parm IS NOT NULL);
Perché deve essere una procedura memorizzata? È possibile creare un'istruzione preparata a livello di programmazione.
Non l'ho fatto per Oracle, ma con SQL Server è possibile utilizzare una funzione per convertire una stringa CSV in una tabella, che può quindi essere utilizzata in una clausola IN. Dovrebbe essere semplice riscriverlo per Oracle (penso!)
CREATE Function dbo.CsvToInt ( @Array varchar(1000))
returns @IntTable table
(IntValue nvarchar(100))
AS
begin
declare @separator char(1)
set @separator = ','
declare @separator_position int
declare @array_value varchar(1000)
set @array = @array + ','
while patindex('%,%' , @array) <> 0
begin
select @separator_position = patindex('%,%' , @array)
select @array_value = left(@array, @separator_position - 1)
Insert @IntTable
Values (Cast(@array_value as nvarchar))
select @array = stuff(@array, 1, @separator_position, '')
end
return
end
Quindi puoi passare una stringa CSV (ad es. "0001.0002.0003") e fare qualcosa del genere
UPDATE table1 SET
col1 = col1 - value
WHERE id in (SELECT * FROM csvToInt(@myParam))
Esiste un articolo sul sito Web AskTom che mostra come creare una funzione per analizzare la stringa CSV e utilizzarla nell'istruzione in modo simile all'esempio di SQL Server fornito.
Vedi Asktom