Reputation: 744
Can someone explain to me why this does not work?
inq := "6,7" //strings.Join(artIds, ",")
rows, err = db.Query("SELECT DISTINCT title FROM tags_for_articles LEFT JOIN tags ON tags.id = tags_for_articles.tag_id WHERE article_id IN (?)", inq)
And this does
rows, err = db.Query("SELECT DISTINCT title FROM tags_for_articles LEFT JOIN tags ON tags.id = tags_for_articles.tag_id WHERE article_id IN (6,7)", inq)
I'm trying to do a simple IN clause with a slice of ints, and every solution suggested doesn't seem very idiomatic
Tried to do this, but the problem appears to be the string substitution.
inq := strings.Join(artIds, ",")
I'm a bit surprised that go doesn't seem to have a graceful way to handle this query.
Upvotes: 6
Views: 13065
Reputation: 388
I just had the same problem then I came up with another solution in more secure way. So, you don't need to aware of SQL injection.
params := []string{"A", "B"}
jsonParams, err := json.Marshal(params)
if err != nil {
return err
}
rows, err = db.Query(`
WITH compared_values as(
SELECT *
FROM JSON_TABLE(
?,
"$[*]" COLUMNS(
value TEXT PATH "$"
)
) as compared_values
)
SELECT * FROM target_table
WHERE target_value IN (SELECT value FROM compared_values);
`, jsonParams)
you can also use pure select part in SQL query like this but I'm not sure about performace. I think using WITH cause can prevent repeatly JSON parsing.
SELECT * FROM target_table
WHERE target_value IN (
SELECT value FROM JSON_TABLE(
?,
"$[*]" COLUMNS(
value TEXT PATH "$"
)
) as compared_values
);
Upvotes: -1
Reputation: 171
You need the number of "?" in the "IN" clause to match the number of arguments , so you need to do something like this:
inq := "6,7" //strings.Join(artIds, ",")
qms := strings.Repeat("?,", len(inq))
qms = params[:len(params)-1] // remove the trailing ","
rows, err = db.Query("SELECT DISTINCT title FROM tags_for_articles LEFT JOIN tags ON tags.id = tags_for_articles.tag_id WHERE article_id IN (" + qms + ")", inq)
Upvotes: 3
Reputation: 9623
If you have been careful to build your inq string from real ints (to avoid injection), you can just build the string yourself and avoid using ?:
inq := "6,7"
sql := fmt.Sprintf("SELECT DISTINCT title FROM tags_for_articles LEFT JOIN tags ON tags.id = tags_for_articles.tag_id WHERE article_id IN (%s)",inq)
rows, err := db.Query(sql)
If you do it a lot, better to have a WhereIn function that does this for you, or use an orm. Be careful which args you accept though, as if you accept arbitrary strings anything could be injected.
Upvotes: 3
Reputation: 12675
Because database/sql does not inspect your query and it passes your arguments directly to the driver, it makes dealing with queries with IN clauses difficult:
SELECT * FROM users WHERE level IN (?);
When this gets prepared as a statement on the backend, the bindvar ? will only correspond to a single argument, but what is often desired is for that to be a variable number of arguments depending on the length of some slice
var levels = []int{4, 6, 7}
rows, err := db.Query("SELECT * FROM users WHERE level IN (?);", levels)
There is a way to handle these types of queries using sqlx
package which provide more control over database queries.
This pattern is possible by first processing the query with sqlx.In:
var levels = []int{4, 6, 7}
query, args, err := sqlx.In("SELECT * FROM users WHERE level IN (?);", levels)
For more information Go through Godoc for InQueries
Upvotes: 5