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

Sha*_*ski 5 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)
Run Code Online (Sandbox Code Playgroud)

我想让它成为我在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)
Run Code Online (Sandbox Code Playgroud)

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

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

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

Image.setInt(1, userID);
Image.setString(2, langID);
Image.executeQuery()
Run Code Online (Sandbox Code Playgroud)

我的问题是当我发送字符串"targetLang"作为第二个参数时,准备好的语句将其作为'targetLang'插入(带有'之前和之后'),数字不是问题beacuse 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)
Run Code Online (Sandbox Code Playgroud)

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

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

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

所以我不是在与固定字符串"targetLang"进行比较.我想为此列选择所有可能的值(从translationChains中选择targetLang).

Bil*_*win 3

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

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

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

我在构建 SQL 查询时编写了许多白名单示例。

您还可以查看我的演示文稿SQL 注入神话和谬论以及我的书《SQL 反模式卷 1:避免数据库编程的陷阱》中的示例。


回复您的评论:

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

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

Image.setString(2, langID); // either '3' or 'targetLang'
Run Code Online (Sandbox Code Playgroud)

但我不确定我是否完全理解你的描述。该数字是否代表您要比较的列的位置?与 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.
Run Code Online (Sandbox Code Playgroud)

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

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

如果我仍然弄错并且不理解您的问题,请编辑您的原始问题并提供更多详细信息。 SHOW CREATE TABLE translationChains有助于。另外,您能否回答我必须做出假设的一些事情,例如您是否尝试将 t.targetLang 与同一行的另一列中的值进行匹配


好吧,现在我更好地理解你的目标了。这是一个可以完成您想要的操作的查询:

从图像中选择 i.imageID、theImage、translationRating i INNER JOIN TranslationChains t ON t.imageId = i.imageid WHERE i.userID = ? AND i.translated = 0 AND ? IN (t.targetLang, 'targetLang')

'targetLang您可以传递一个与 t.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()
Run Code Online (Sandbox Code Playgroud)