How do I pass a []slice to an IN-condition in a prepared SQL statement with non-IN-conditions as well?

心不动则不痛 提交于 2019-12-12 10:10:45

问题


Imagine you have the following SQL query:

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

And you have the following possible data (we imagine that a user interface can set these data):

var Type int
var SubTypes []int

In the case of SubTypes, we are talking about a multiple choice selection.

Now, the following code won't work:

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

Because the driver (at least the mysql driver used in this example) doesn't recognise a []slice. Typing to explode it (SubTypes...) doesn't work either, because A) you cannot have more than one exploded parameter and B) even if you could, your SQL only supports a single item ((?)).


回答1:


Prepared statements do not work that way, at least not in major DBMS I know. I mean, in Go, the support for prepared statements implemented by database/sql drivers is supposed to use the corresponding facility provided by the underlying DBMS (a driver might opt to simulate such support if it's not provided by the DB engine it interfaces with).

Now in all the DBMS-s I'm familiar with, the whole idea of prepared statement is that it's processed once by the DB engine and cached; "processed" here means syntax checking, compiling into some DB-specific internal representation and its execution plan figured out. As follows from the term "compiled", the statement's text is processed exactly once, and then each call to the prepared statement just essentially tells the server "here is the ID of that prepared statement I supplied you earlier, and here's the list of actual parameters to use for placeholders it contained". It's like compiling a Go program and then calling it several times in a row with different command-line flags.

So the solution you have come up with is correct: if you want to mess with the statement text between invocation then by all means use client-side text manipulations1 but do not attempt to use the result of it as a prepared statement unless you really intend to execute the resulting text more than once.

And to be may be more clear: your initial attempt to prepare something like

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

supposedly fails at your attempt to supply a set of values for that IN (?) placeholder because commas which would be required there to specify several values are syntax, not parts of the value.

I think it should still be fine to prepare something like

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

because it does not break that rule. Not that it's a solution for you…

See also this and this — studying the latter would allow you to play with prepared statements directly in the MySQL client.


1 Some engines provide for server-side SQL generation with subsequent execution of the generated text.




回答2:


However, there is a solution. First of all, since we can only have a single exploding parameter and no others, we should first put together our parameters in a single []slice:

var params []interface{}
params = append(params, Type)
for _, subtype := range SubTypes {
  params = append(params, SubTypes)
}

Since the SQL will not expand on its own, let's expand that loop:

var params []interface{}
params = append(params, Type)
inCondition := ""
for _, subtype := range SubTypes {
  params = append(params, SubTypes)
  if inCondition != "" {
    inCondition += ", "
  }
  inCondition += "?"
}

Assuming SubTypes contains []int{1,2,3}, inCondition should now contain ?, ?, ?.

We then combine that to our SQL statement and explode the argument:

sqlstr := fmt.Sprintf(`SELECT *
  FROM foo
  WHERE type = ?
    AND subtype IN (%s)`, inCodition)
rows, err := sqldb.Query(sqlstr, params...)

Of course, it would be pretty cool, if you could simply pass []slices to your prepared statements, and the automatically expanded. But that might give some unexpected results if you are dealing with more 'unknown' data.



来源:https://stackoverflow.com/questions/20652901/how-do-i-pass-a-slice-to-an-in-condition-in-a-prepared-sql-statement-with-non

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!