在没有''的情况下将字符串插入预准备语句

时间:2013-04-04 18:23:34

标签: mysql sql prepared-statement

我有以下sql语句:

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)

我想让它成为我在java代码中使用的预准备语句:

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)

第一个输入?是用户ID(整数),它工作正常。

第二个输入是一个包含languageId的字符串 - 它是一个包含表示语言的数字的字符串,或者是字符串“targetLang”,它是列的名称(=所有语言)

在我的java代码中,我做了以下内容:

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

我的问题是当我发送字符串“targetLang”作为第二个参数时 准备好的语句将其插入'targetLang'(带'之前和之后'),数字不是问题,因为3 ='3',但是使用字符串它会给我不同的结果 - 我总是得到一个空结果集,因为没有任何东西等于'targetLang'。 我需要将此字符串插入到没有'的预准备语句中。 是否有可能或者我需要使用与准备好的陈述不同的东西?

我知道我可以构建一个包含所有查询的字符串,但我正在寻找更优雅的东西 TNX


编辑:

这是Create table 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)

如您所见,我正在尝试根据“targetLang”列(即int列)拍摄图像。每个数字代表一种语言。

现在我有两个选择从该表中选择图像的选项:

  • 选择特定语言,即将数字作为第二个输入。
  • 选择所有语言,即将第二个输入设置为列名“targetLang”。

所以我没有比较固定字符串“targetLang”。我想为此列选择所有可能的值(从translationChains中选择targetLang)。

2 个答案:

答案 0 :(得分:3)

查询参数只能取代文字值 - 即通常放置引用的字符串文字,引用的日期文字或数字文字。因此,字符串值将始终解释为字符串文字,就好像您已使用单引号将其放入查询中一样。

对于列名,表名,SQL表达式,SQL关键字等,您必须在调用prepare()之前将这些值插入到SQL查询中。

为了最安全,请使用白名单,以便用户输入永远不会逐字插入到SQL查询中。在查询中使用之前,请始终验证用户输入(或任何其他内容)。

构建SQL查询时,我有written many examples个白名单。

您还可以在我的演示文稿SQL Injection Myths and Fallacies和我的书SQL Antipatterns: Avoiding the Pitfalls of Database Programming中看到示例。


重新评论:

我不确定你到底在做什么。听起来您要么t.targetLang等于文字数字,要么是固定字符串'targetLang'。如果是这样,我不知道为什么你要做一个子查询 - 你应该只使用一个查询参数:

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

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

但我不确定我完全理解你的描述。数字是否代表您要比较的列的位置?和t.targetLang在同一行?如果是这样,我仍然认为你不需要子查询。您可以使用如下表达式:

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.

请参阅MySQL中FIELD()函数的手册,该函数在表达式列表中搜索第一个参数。它返回匹配字段的整数位置。

使用此方法,您可以传递希望t.targetLang匹配的位置,这样您就可以将动态部分作为值传递,而不是作为列名传递。它还允许您避免子查询。

如果我仍然弄错了并且不理解您的问题,请编辑原始问题并提供更多详细信息。 SHOW CREATE TABLE translationChains会有所帮助。你也可以回答一些我必须做出假设的事情,例如:你试图将t.targetLang与同一行中另一列中的值匹配吗?


好的,现在我更了解你的目标了。这是一个执行您想要的查询:

SELECT i.imageID,theImage,translationRating 来自图像我 INNER JOIN TranslationChains t   ON t.imageId = i.imageid 在哪里i.userID =? AND i.translated = 0   和? IN(t.targetLang,'targetLang')

您可以将第二个参数传递给与t.targetLang匹配的整数,或者传递与谓词中的值'targetLang'匹配的文字字符串'targetLang'。

我不推荐这个解决方案,因为它可能不会很好地使用索引,它可能必须通过将字符串参数与整数列进行比较来执行类型转换。

当您想匹配所有语言时,更好的做法是在WHERE子句中执行查询而不最后一个术语。也就是说,在您的应用程序中,仅在您希望查询获取特定语言时才有条件地附加该术语。否则,省略该术语并跳过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()

答案 1 :(得分:0)

您可以更改:

  

和t.targetLang in(select?from translationChains)

要:

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

并制作setString(3,langId),即使第3个参数与第2个相同。在我的知识中,这不是更优雅或更有效,而是应该有用的东西!