2013-04-16 9 views
9

Nella mia applicazione voglio eseguire query come SELECT * FROM tbl WHERE col IN (@list) dove, @ elenco può avere numero variabile di valori. Sto usando il database del server MS SQL. Quando ho google Questo problema poi ho trovato questo linkChiamata stored procedure con parametro con valori di tabella da java

http://www.sommarskog.se/arrays-in-sql-2008.html

Questo link dice di usare il parametro con valori di tabella. Così ho creato il tipo di dati definiti dall'utente utilizzando Microsoft SQL Server Management Studio.

Crea tipo integer_list_tbltype come tabella (N non int NULL PRIMARY KEY)

Poi ho scritto stored procedure

CREATE PROCEDURE get_product_names @prodids integer_list_tbltype READONLY AS 
    SELECT p.ProductID, p.ProductName 
    FROM Northwind.dbo.Products p 
    WHERE p.ProductID IN (SELECT n FROM @prodids) 

e quindi utilizzando Management Studio solo io eseguito questa procedura

DECLARE @mylist integer_list_tbltype 
INSERT @mylist(n) VALUES(9),(12),(27),(37) 
EXEC get_product_names @mylist 

e mi sta dando l'output corretto. Ma mi sto chiedendo come chiamare questa stored procedure dal codice sorgente java. Io so come chiamare semplice stored procedure con numero costante di argomento

CallableStatement proc_stmt = null; 
proc_stmt = con.prepareCall("{call test(?)}"); 
proc_stmt.setString(1,someValue); 

ma come chiamare stored procedure in caso di parametri di tabella valore?

risposta

-6

Dopo aver cercato per un po 'ho trovato la risposta a questo problema. Soprattutto quando si utilizza la clausola IN e no degli operandi sono variabili, è possibile utilizzare valori delimitati da virgole come input nella clausola IN.

Ecco l'esempio di come la procedura memorizzata che recupera tutti gli avvocati del tipo di avvocato indicato nel codice postale fornito sarà simile all'utilizzo di SQL dinamico.

CREATE PROCEDURE [dbo].[GetLawyers] (@ZIP CHAR(5), @LawyerTypeIDs VARCHAR(100)) 
AS 

DECLARE @SQL  VARCHAR(2000) 

SET @SQL = 'SELECT * FROM [dbo].[Lawyers] 
      WHERE [ZIP] = ' + @ZIP + ' AND 
        [LawyerTypeID] IN (' + @LawyerTypeIDs + ')' 
EXECUTE (@SQL) 

GO 

Per eseguire la stored procedure passando il codice postale inserito dal tipi avvocato selezionati in un valori separati da virgole utente e:

EXECUTE [dbo].[GetLawyers] '12345', '1,4' 

Quindi la conclusione è che non è necessario utilizzare TVP. Qualunque sia la lingua [Java, PHP], usi semplicemente i parametri come stringa delimitata da virgole per la stored procedure e funzionerà perfettamente.

Quindi, in JAVA è possibile chiamare stored procedure sopra: -

proc_stmt = con.prepareCall("{call GetLawyers(?,?)}"); 
proc_stmt.setString(1,"12345"); 
proc_stmt.setString(2,"'1,4'"); 
+2

Il problema con questo approccio è che potrebbe riempire la cache dell'istruzione. –

+0

@ngund Ciao, ho provato questa soluzione, e se ho capito bene, ho fatto quanto segue: m_cStatement.setString ("'1,2,3'")); e poi ho ottenuto la seguente eccezione: "Operando type clash: nvarchar è incompatibile con udt_UsersById". Potete consigliare per favore? (Ho pubblicato una domanda su http://stackoverflow.com/questions/31161789/what-is-the-java-equivalent-to-sql-user-defined-table-type ed è stata contrassegnata come duplicata. guarda per favore?) 10X! – dushkin

+5

Il problema è che potrebbe aprirti all'iniezione sql –

4

Sembra che questo è un aggiunta prevista alla JDBC, ma non è stato ancora implementato:

http://blogs.msdn.com/b/jdbcteam/archive/2012/04/03/how-would-you-use-table-valued-parameters-tvp.aspx

passare il parametro come una stringa delimitata ("9,12,27,37") e quindi creare una funzione valutata a livello di tabella in SQL Server denominata "fnSplit" o qualsiasi altra cosa restituisce i valori interi in una tabella (basta cercare "sql server split function", ci sono milioni di essi).

+0

grazie @Kevin. Conosci qualche soluzione diversa dall'utilizzo di TVP per problemi. –

+1

Ciao Pranay, sei il benvenuto. Controlla la mia risposta originale per il suggerimento. Passa la stringa delimitata come singolo parametro di tipo varchar (max). Quindi, all'interno del tuo proc memorizzato, chiama la funzione definita dall'utente di SQL Server Split (dovrai creare questo, basta cercarlo, troverai molto aiuto). La funzione "Dividi" dovrebbe restituire una tabella da una stringa delimitata. In bocca al lupo. –

+0

Giusto per aggiungere alla risposta utile di @ Kevin, il link originale dell'OP include un esempio di una funzione chiamata "iter_intlist_to_tbl" per dividere il varchar in una variabile di tabella (http://www.sommarskog.se/arrays-in-sql-2005 .html # iterativo)! Ho pensato di dirlo. – DarthPablo

2

Le risposte tipiche (delimitati da virgole o XML) tutti hanno problemi con SQL Injection. Avevo bisogno di una risposta che mi permettesse di usare un PreparedStatement.Così mi è venuto in mente questo:

StringBuilder query = new StringBuilder(); 
query.append(
    "DECLARE @mylist integer_list_tbltype;" + 
    "INSERT @mylist(n) VALUES(?)"); 
for (int i = 0; i < values.size() - 1; ++i) { 
    query.append(",(?) "); 
} 
query.append("; EXEC get_product_names @mylist "); 
PreparedStatement preparedStmt = conn.prepareStatement(query.toString()); 
for (int i = 0; i < values.size(); ++i) { 
    preparedStmt.setObject(i + 1, itemLookupValues.get(i)); 
} 
2

Ora è stato aggiunto a JDBC Driver 6.0. It CTP2 ancora.

"Questo nuovo driver ora supporta i parametri con valori di tabella e Azure Active Directory.Oltre a queste nuove funzionalità, abbiamo aggiunto funzionalità aggiuntive per Sempre crittografato.Il driver supporta anche nomi di dominio internazionalizzati e parametrizzati."

https://blogs.msdn.microsoft.com/jdbcteam/2016/04/04/get-the-new-microsoft-jdbc-driver-6-0-preview/

Download link: https://www.microsoft.com/en-us/download/details.aspx?displaylang=en&id=11774

Ecco la documentazione come usarlo https://msdn.microsoft.com/en-us/library/mt651781(v=sql.110).aspx

4

Questo è documentato qui nel JDBC driver manual. Nel tuo caso, dovreste fare questo:

try (SQLServerCallableStatement stmt = 
    (SQLServerCallableStatement) con.prepareCall("{call test(?)}")) { 

    SQLServerDataTable table = new SQLServerDataTable(); 
    sourceDataTable.addColumnMetadata("n", java.sql.Types.INTEGER); 

    sourceDataTable.addRow(9); 
    sourceDataTable.addRow(12); 
    sourceDataTable.addRow(27); 
    sourceDataTable.addRow(37); 

    stmt.setStructured(1, "dbo.integer_list_tbltype", table); 
} 

I've also recently documented this in an article.