2013-04-04 3 views
5

Ich habe die folgende SQL-Anweisung:eine Zeichenfolge in eine vorbereitete Anweisung Einfügen ohne ‚‘

Select i.imageID, theImage, translationRating 
From images i 
inner join translationchains t 
on t.imageId = i.imageid 
where i.userID=(someUserID) And i.translated =0 
and t.targetLang in (select targetLang from translationChains) 

ich es eine vorbereitete Aussage machen wollen in meinem Java-Code zu verwenden:

Select i.imageID, theImage, translationRating 
From images i 
inner join translationchains t 
on t.imageId = i.imageid 
where i.userID=? And i.translated =0 
and t.targetLang in (select ? from translationChains) 

der Eingabe für die erste? ist eine Benutzer-ID (Integer) und es funktioniert gut.

der zweite Eingang ist eine Zeichenfolge, die eine languageId enthalten - es ist ein String ist, dass entweder eine Nummer enthalten, die eine Sprache, oder die Zeichenkette „targetLang“ darstellen, die der Name der Spalte (= alle langs)

ist in meinem Java-Code habe ich folgendes:

Image.setInt(1, userID); 
Image.setString(2, langID); 
Image.executeQuery() 

mein Problem ist, wenn ich die Zeichenfolge „targetLang“ als zweiter Parameter bin das Senden die vorbereitete Anweisung eingefügt wird er als ‚targetLang‘ (mit 'vor und nach '), mit Zahlen ist es kein Problem, weil 3 =' 3 ', aber mit Strings gibt es mir andere Ergebnisse aus Was ich brauche - ich bekomme immer eine leere Ergebnismenge, weil nichts mit 'targetLang' gleich ist. Ich muss diese Zeichenfolge in die vorbereitete Anweisung ohne die '. ist es möglich oder muss ich etwas anderes als die vorbereitete aussage verwenden?

Ich weiß, dass ich eine Zeichenfolge erstellen, die alle diese Abfrage enthalten, aber ich bin auf der Suche nach etwas elegantere tnx


edit:

Dies ist die Tabelle erstellen translationChains:

Create Table if not exists TranslationChains (
    ImageID int (10) NOT NULL, 
    SourceLang int NOT NULL, 
    TargetLang int NOT NULL, 
    Translated tinyint default 0, 
    Translation text, 
    Translator varchar (30), 
    CONSTRAINT translate_image PRIMARY KEY (ImageID,SourceLang, TargetLang), 
    FOREIGN KEY (ImageID) REFERENCES Images(ImageID) ON DELETE CASCADE, 
    FOREIGN KEY (SourceLang) REFERENCES Languages(languageID) ON DELETE CASCADE, 
    FOREIGN KEY (TargetLang) REFERENCES Languages(languageID) ON DELETE CASCADE) 

Wie Sie sehen können, versuche ich Bilder entsprechend der "targetLang" Spalte wh Ich bin eine int-Spalte. Jede Nummer repräsentiert eine Sprache.

Jetzt habe ich zwei Optionen in Bilder aus dieser Tabelle auswählen:

  • eine bestimmte Sprache auswählen, das heißt eine Zahl als zweiten Eingang geben.
  • Auswahl aller Sprachen, d. H. Einstellung der zweiten Eingabe als Spaltenname "targetLang".

Also ich bin nicht mit der festen Zeichenfolge "targetLang" verglichen. Ich möchte alle möglichen Werte für diese Spalte auswählen (wählen Sie targetLang aus translationChains).

Antwort

3

Abfrageparameter können nur die Stelle eines literalen Werts einnehmen, d. H. Wo Sie normalerweise ein String-Literal in Anführungszeichen, ein Literal in Anführungszeichen oder ein numerisches Literal setzen würden. Daher wird ein Zeichenfolgenwert immer als Zeichenfolgenliteral interpretiert, als ob Sie ihn in die Abfrage mit einfachen Anführungszeichen gesetzt hätten.

Bei Spaltennamen, Tabellennamen, SQL-Ausdrücken, SQL-Schlüsselwörtern usw. müssen Sie diese Werte vor dem Aufruf von prepare() in die SQL-Abfrage interpolieren.

Um sicher zu sein, verwenden Sie Whitelisting, so dass Benutzereingaben niemals in SQL-Abfragen wortwörtlich interpoliert werden. Überprüfen Sie immer die Benutzereingabe (oder jeden anderen Inhalt), bevor Sie sie in einer Abfrage verwenden.

Ich habe written many examples Whitelisting beim Erstellen einer SQL-Abfrage.

Sie können auch Beispiele in meiner Präsentation SQL Injection Myths and Fallacies und meinem Buch SQL Antipatterns: Avoiding the Pitfalls of Database Programming sehen.


Re Ihr Kommentar:

Ich bin nicht sicher genau, was Sie tun. Es klingt, als ob Sie entweder t.targetLang einer Literalnummer oder einer festen Zeichenfolge 'targetLang' entsprechen möchten. Wenn ja, ich weiß nicht, warum Sie eine Unterabfrage überhaupt tun - man muss nur einen Abfrageparameter für einen Wert verwenden soll:

where i.userID = ? And i.translated = 0 
and t.targetLang = ? 

Image.setString(2, langID); // either '3' or 'targetLang' 

Aber ich bin nicht sicher, ob ich Ihre Beschreibung voll und ganz verstehen. Stellt die Zahl die Position einer Spalte dar, mit der Sie vergleichen möchten? In derselben Zeile wie t.targetLang? Wenn dem so ist, denke ich immer noch nicht, dass du eine Unterabfrage brauchst. Sie könnten einen Ausdruck wie die folgenden verwenden:

where i.userID = ? And i.translated = 0 
and FIELD(t.targetLang, t.targetLang, t.column2, t.column3, t.column4) = ? 

Image.setString(2, '1'); // for the case where you allow all langs 
Image.setString(2, '3'); // for the case where you want to match a specific column. 

finden Sie im Handbuch auf die FIELD() Funktion in MySQL, die aus einer Liste von Ausdrücken für das erste Argument sucht. Es gibt die Ganzzahlposition des übereinstimmenden Feldes zurück.

Mit dieser Methode übergeben Sie die Position, an die Ihr t.targetLang angepasst werden soll. Dadurch können Sie den dynamischen Teil als Wert übergeben und nicht als Spaltennamen. Sie können damit auch die Unterabfrage vermeiden.

Wenn ich immer noch falsch verstanden habe und Ihr Problem nicht verstehe, bearbeiten Sie bitte Ihre ursprüngliche Frage und geben Sie weitere Einzelheiten an. SHOW CREATE TABLE translationChains würde helfen. Kannst du auch einige Dinge beantworten, über die ich Annahmen machen musste, z. Versuchen Sie, t.targetLang mit einem Wert in einer anderen Spalte in derselben Zeile zu vergleichen?


Okay, jetzt verstehe ich Ihr Ziel besser. Hier ist eine Abfrage, das tut, was Sie wollen:

SELECT i.imageID, theImage, translationRating aus Bildern i INNER JOIN TranslationChains t ON t.imageId = i.imageid WHERE i.userID =? UND i.translated = 0 UND? IN (t.targetLang, 'targetLang')

Sie können als zweiten Parameter entweder eine ganze Zahl übergeben, die mit t.targetLang übereinstimmt, oder die literale Zeichenfolge 'targetLang', die mit dem Wert 'targetLang übereinstimmt Prädikat.

Ich kann diese Lösung jedoch nicht empfehlen, da sie wahrscheinlich Indizes nicht sehr gut verwenden wird, muss sie möglicherweise Typkonvertierungen durchführen, indem sie den Zeichenfolgenparameter mit der Ganzzahlspalte vergleicht.

Die bessere Praxis, wenn Sie alle Sprachen übereinstimmen möchten, ist die Abfrage ohne der letzte Begriff in der WHERE-Klausel.Fügen Sie diesen Begriff in Ihrer Anwendung nur bedingt an, wenn die Abfrage eine bestimmte Sprache abrufen soll. Andernfalls, lassen Sie diesen Begriff aus und überspringen Sie die Image.setString().

String sql = "SELECT i.imageID, theImage, translationRating 
FROM Images i 
INNER JOIN TranslationChains t 
    ON t.imageId = i.imageid 
WHERE i.userID = ? AND i.translated = 0 "; 

if (langId) { 
    sql += " AND ? IN (t.targetLang, 'targetLang')"; 
} 

. . . 

Image.setInt(1, userID); 
if (langId) { 
    Image.setString(2, langID); 
} 
Image.executeQuery() 
+0

tnx, aber können Sie die Antwort auf mich klären? Ich bin mir nicht sicher, was ich tun soll. Ich benutze whitelisting = Ich überprüfe die Benutzereingabe und dann entscheiden, was in die Abfrage einfügen, aber ich bin mir nicht sicher, wie Sie es mit dem Problem einfügen –

+0

bearbeitet den ursprünglichen Beitrag –

+1

tnx! Diese Abfrage funktioniert: SELECT i.imageID, theImage, ÜbersetzungBewertung FROM Bilder i INNER JOIN TranslationChains t ON t.imageId = i.imageID WHERE i.userID =? UND i.translated = 0 UND? IN (t.targetLang, 'targetLang') –

0

können Sie ändern:

und t.targetLang in (wählen Sie aus translationChains?)

An:

and (
     (
      ? = 'targetLang' and 
      t.targetLang in (
            select 
             targetLang 
            from 
             translationChains 
          ) 
     ) 
     or 
     ? = t.targetLang 
    ) 

und machen setString (3, langId), dh der dritte Parameter ist derselbe wie der zweite. Auf keinen Fall ist dies in meinem Wissen eleganter oder effizienter, aber etwas, das funktionieren sollte!