2013-04-16 11 views
9

In meiner Anwendung möchte ich Abfrage wie ausführen SELECT * FROM Tabl WHERE Spalte IN (@list) wo, @ Liste kann Variable keine Werte. Ich benutze MS SQL Server Datenbank. Wenn ich dieses Problem google dann fand ich diesen LinkAufruf gespeicherte Prozedur mit Tabellenwert-Parameter von Java

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

Dieser Link sagt Tabellenwertparameter zu verwenden. Also habe ich einen benutzerdefinierten Datentyp mit Microsoft SQL Server Management Studio erstellt.

CREATE TYPE integer_list_tbltype AS TABLE (n int NOT NULL PRIMARY KEY)

Dann schrieb ich gespeicherte Prozedur

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) 

und dann Management Studio nur ausgeführt ich diese Prozedur

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

und es gibt mir korrekte Ausgabe. Aber ich frage mich, wie ich diese gespeicherte Prozedur aus Java-Quellcode aufrufen soll. Ich weiß, wie einfache gespeicherte Prozedur mit konstanter Anzahl von Argumenten aufgerufen wird

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

aber wie gespeicherte Prozedur in Tabelle-Wert-Parameter-Fall aufgerufen wird?

Antwort

-6

Nach der Suche nach einer Weile habe ich die Antwort auf dieses Problem gefunden.Speziell, wenn Sie IN-Klausel verwenden und keine der Operanden Variablen sind, dann können Sie kommagetrennte Werte als Eingabe in IN-Klausel verwenden.

Hier ist das Beispiel, wie die gespeicherte Prozedur, die alle Anwälte des angegebenen Anwaltstyps in der bereitgestellten Postleitzahl abrufen wird, wie mit dynamischem SQL aussieht.

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

So Schlussfolgerung ist, dass Sie nicht TVP verwenden müssen:

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 

Um die gespeicherte Prozedur übergeben Sie die ZIP-Code durch den Benutzer und den ausgewählten Anwalt Typen in einem Komma getrennt Wert eingegeben auszuführen. Welche Sprache [Java, PHP] Sie auch verwenden, übergeben Sie einfach Parameter als kommagetrennte Zeichenfolge an gespeicherte Prozedur und es wird perfekt funktionieren.

in JAVA So können Sie über gespeicherte Prozedur aufrufen: -

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

Das Problem mit diesem Ansatz besteht darin, dass es den Anweisungscache füllen kann. –

+0

@ngund Hallo, ich habe diese Lösung versucht, und wenn ich es gut verstanden habe, habe ich Folgendes getan: m_cStatement.setString ("1,2,3")); und dann habe ich die folgende Ausnahme: "Operand typ clash: nvarchar ist inkompatibel mit udt_UsersById". Kannst du bitte beraten? (Ich habe eine Frage zu http://stackoverflow.com/questions/31161789/what-is-the-java-equivalent-to-sql-user-de-fined-table-type gestellt und sie wurde als dupliziert markiert. Kannst du eine schau bitte?) 10X! – dushkin

+5

Das Problem damit ist, es könnte Sie bis zur SQL-Injektion öffnen –

4

Sieht aus wie dies eine geplante neben JDBC ist aber noch nicht implementiert wurde:

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

Übergeben Sie den Parameter als begrenzte Zeichenfolge („9,12,27,37“) und erstellen eine Tabellenwert-Funktion in SQL Server namens "fnSplit" oder was auch immer, die die Integer-Werte in einer Tabelle zurückgibt (einfach nach "sql server split function" suchen, gibt es Millionen von ihnen).

+0

danke @Kevin. Kennen Sie eine andere Lösung als die Verwendung von TVP als Problem? –

+1

Hallo Pranay, Gern geschehen. Überprüfen Sie meine ursprüngliche Antwort auf Vorschlag. Übergeben Sie die Zeichenfolge mit Trennzeichen als einzelnen Parameter vom Typ varchar (max). Dann, in Ihrem gespeicherten proc, SQL Server Split benutzerdefinierte Funktion aufrufen (Sie müssen dies erstellen, nur suchen, finden Sie jede Menge Hilfe). "Split" -Funktion sollte eine Tabelle aus einer Zeichenfolge mit Trennzeichen zurückgeben. Viel Glück. –

+0

Nur um @ Kevins hilfreiche Antwort hinzuzufügen, enthält der ursprüngliche Link des OPs ein Beispiel einer Funktion namens "iter_intlist_to_tbl", um den varchar in eine Tabellenvariable zu teilen (http://www.sommarskog.se/arrays-in-sql-2005) .html # iterativ)! Ich dachte nur, ich würde es erwähnen. – DarthPablo

2

Die typischen Antworten (durch Komma getrennt oder XML) alle Probleme mit SQL Injection haben. Ich brauchte eine Antwort, die es mir erlaubt, ein PreparedStatement zu verwenden.Also kam ich auf diese Idee:

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

Jetzt ist es zu JDBC Driver 6.0 hinzugefügt. Es CTP2 noch.

"Dieser neue Treiber unterstützt nun Table-Valued Parameter und Azure Active Directory. Zusätzlich zu diesen neuen Funktionen haben wir zusätzliche Funktionalität für Always Encrypted hinzugefügt. Der Treiber unterstützt auch Internationalisierte Domainnamen und parametrisierte."

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

Hier ist die Dokumentation, wie es zu benutzen https://msdn.microsoft.com/en-us/library/mt651781(v=sql.110).aspx

4

Diese hier im JDBC driver manual dokumentiert. In Ihrem Fall, dann würden Sie, dies zu tun haben:

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.

Verwandte Themen