Usar CSV é provavelmente a maneira mais simples, supondo que você possa ter 100% de certeza de que seus elementos não conterão strings.
Uma maneira alternativa, e provavelmente mais robusta, de fazer isso é criar um tipo personalizado como uma tabela de strings. Supondo que suas strings nunca tivessem mais de 100 caracteres, você poderia ter:
CREATE TYPE string_table AS TABLE OF varchar2(100);
Você pode então passar uma variável desse tipo para seu procedimento armazenado e referenciá-la diretamente. No seu caso, algo assim:
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
A
table()
A função transforma seu tipo personalizado em uma tabela com uma única coluna "COLUMN_VALUE", que você pode tratar como qualquer outra tabela (assim como junções ou, neste caso, subseleções). A beleza disso é que o Oracle criará um construtor para você, portanto, ao chamar seu procedimento armazenado, você pode simplesmente escrever:
execute_update(string_table('foo','bar','baz'), 32);
Estou assumindo que você pode lidar com a criação desse comando programaticamente a partir do C#.
Como um aparte, na minha empresa temos vários desses tipos personalizados definidos como padrão para listas de strings, doubles, ints e assim por diante. Também fazemos uso do Oracle JPublisher para poder mapear diretamente desses tipos em objetos Java correspondentes. Eu dei uma olhada rápida, mas não consegui ver nenhum equivalente direto para C#. Apenas pensei em mencioná-lo caso os desenvolvedores de Java se deparassem com essa pergunta.