一尘不染

如何在具有非IN条件的已准备好的SQL语句中将[]切片传递给IN条件?

go

假设您有以下SQL查询:

SELECT *
  FROM foo
  WHERE type = ?
    AND subtype IN (?)

并且您具有以下可能的数据(我们假设用户界面可以设置这些数据):

var Type int
var SubTypes []int

在的情况下SubTypes,我们谈论的是多项选择。

现在,以下代码将不起作用:

rows, err := sqldb.Query(`SELECT *
  FROM foo
  WHERE type = ?
    AND subtype IN (?)`, Type, SubTypes)

因为驱动程序(至少mysql在本示例中使用驱动程序)无法识别[]slice。键入使其爆炸(SubTypes...)也不起作用,因为A)您不能有多个爆炸参数,而B)即使可以,您的SQL仅支持单个项目((?))。


阅读 239

收藏
2020-07-02

共1个答案

一尘不染

准备的语句不能那样工作,至少在我所知的主要DBMS中不行。我的意思是,在Go中,由database/sql驱动程序实现的对准备好的语句的支持 应该
使用底层DBMS提供的相应功能(如果驱动程序所接口的DB引擎未提供,则驱动程序可能会选择模拟这种支持)。

现在,在我熟悉的所有DBMS中,prepared语句的整体思想是,它由DB引擎处理 一次
并缓存;这里的“已处理”是指语法检查,编译为某些特定于数据库的内部表示形式,并确定其执行计划。从术语“已编译”开始,语句的 文本
仅被处理一次,然后每次对预处理语句的调用实际上只是告诉服务器“这是我之前提供给您的预处理语句的ID,这是实际的列表。用于包含的占位符的参数”。这就像编译Go程序,然后使用不同的命令行标志连续多次调用它。

因此,您想出的解决方案是正确的:如果您想在调用之间弄乱语句文本,则一定要使用客户端文本操作1,但不要尝试将其结果用作准备好的语句,除非您真的打算多次执行结果文本。

而且可能会更清楚:您最初尝试准备类似

SELECT a, b FROM foo WHERE a IN (?)

据说您尝试为该IN (?)占位符提供一组值失败了,因为在那里指定多个值所需的逗号 是语法,而不是值的一部分。

我认为准备类似的东西还是可以的

SELECT a, b FROM foo WHERE a IN (?, ?, ?)

因为它不会违反该规则。并不是说这是您的解决方案……

另见 -学习后者将让你与准备语句直接在MySQL客户端播放。


1一些引擎提供服务器端SQL生成,并随后执行生成的文本。

2020-07-02