golangでmysqlを使ってコードを書いています。
func XXXX(db sql.DB, n int,
modifiedBefore *time.Time,
tags []string,
url string,
) ([]*ContentsWithAll, error) {
tagsSql := strings.Repeat("?, ", len(tags)-1) + ` ) `
const sqlstr = `SELECT ` +
// SELECT部は省略
`FROM ` +
`((((contents ` +
`LEFT JOIN contents_urls ON ((contents_urls.content_id = contents.id))) ` +
`LEFT JOIN urls ON ((urls.id = contents_urls.url_id))) ` +
`LEFT JOIN contents_tags ON ((contents_tags.content_id = contents.id))) ` +
`LEFT JOIN tags ON ((tags.id = contents_tags.tag_id))) ` +
`WHERE modified < ? ` +
`AND tags.name in (? ` + tagsSql +
`AND urls.url like ? ` +
`GROUP BY contents.id ` +
`Limit ? `
urlSql := fmt.Sprintf("%%%s%%", url)
// syntax error: unexpected urlSql, expecting )
q, err := db.Query(sqlstr, *modifiedBefore, tags..., urlSql, n)
if err != nil {
return nil, err
}
defer q.Close()
// load results
var res []*ContentsWithAll
for q.Next() {
cwa := ContentsWithAll{}
// scan
err = q.Scan(/*省略*/)
if err != nil {
return nil, err
}
res = append(res, &cwa)
}
return res, nil
}
先ず IN演算子
の構文を含める場合sqlstrの?の数が可変長になります。
実際 stackoverflowの情報をヒントにsqlstrの?
の数をtagsのtag数に応じて増減させています。
その場合のdb.Queryのパラメータの渡し方を教えて下さい。
現状 *modifiedBefore, tags..., urlSql, n
という渡し方ではsyntaxerrorとなってしまいます。
どう渡せばいいんでしょうか?
-
気になる質問をクリップする
クリップした質問は、後からいつでもマイページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
クリップを取り消します
-
良い質問の評価を上げる
以下のような質問は評価を上げましょう
- 質問内容が明確
- 自分も答えを知りたい
- 質問者以外のユーザにも役立つ
評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。
質問の評価を上げたことを取り消します
-
評価を下げられる数の上限に達しました
評価を下げることができません
- 1日5回まで評価を下げられます
- 1日に1ユーザに対して2回まで評価を下げられます
質問の評価を下げる
teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。
- プログラミングに関係のない質問
- やってほしいことだけを記載した丸投げの質問
- 問題・課題が含まれていない質問
- 意図的に内容が抹消された質問
- 過去に投稿した質問と同じ内容の質問
- 広告と受け取られるような投稿
評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。
質問の評価を下げたことを取り消します
この機能は開放されていません
評価を下げる条件を満たしてません
質問の評価を下げる機能の利用条件
この機能を利用するためには、以下の事項を行う必要があります。
- 質問回答など一定の行動
-
メールアドレスの認証
メールアドレスの認証
-
質問評価に関するヘルプページの閲覧
質問評価に関するヘルプページの閲覧
check解決した方法
0
args変数([]interface)を作って渡す引数を全部固めて渡すことで解決しました
args := []interface{}{*modifiedBefore}
for _, tag := range tags {
args = append(args, tag)
}
args = append(args, urlSql, n)
q, err := db.Query(sqlstr, args...)
// q, err := db.Query(sqlstr, *modifiedBefore, tags..., urlSql, n)
実際にはqueryBuilder(squirrel)を使うのが一番いいのかなとわかりました
modifiedBefore := time.Now()
tags := []string{"go", "rust", "typescript"}
url := "//"
n := 100
tagsStmt := sq.Expr("CONCAT(DISTINCT tags.name SEPARATOR ',')")
q := sq.Select("id",
"account_id",
"pairs",
"complete_rate",
"point",
"title",
"description",
"created",
"modified").
Column("CONCAT(DISTINCT urls.url SEPARATOR ',') as urls").
// Column("(DISTINCT tags.name SEPARATOR ',') as tags").
Column(sq.Alias(tagsStmt, "tags")).
From("contents").
LeftJoin("contents_urls ON contents_urls.content_id = contents.id").
LeftJoin("contents_tags ON contents_tags.content_id = contents.id").
LeftJoin("tags ON tags.id = contents_tags.tag_id").
Where(sq.Lt{"modified": modifiedBefore}).
Where(sq.Eq{"tags.name": tags}).
Where("urls.url like ?", fmt.Sprint("%", url, "%")).
GroupBy("contents.id").
Limit(uint64(n))
fmt.Println(q.ToSql())
実行結果
SELECT id, account_id, pairs, complete_rate, point, title, description, created, modified, CONCAT(DISTINCT urls.url SEPARATOR ',') as urls, (CONCAT(DISTINCT tags.name SEPARATOR ',')) AS tags FROM contents LEFT JOIN contents_urls ON contents_urls.content_id = contents.id LEFT JOIN contents_tags ON contents_tags.content_id = contents.id LEFT JOIN tags ON tags.id = contents_tags.tag_id WHERE modified < ? AND tags.name IN (?,?,?) AND urls.url like ? GROUP BY contents.id LIMIT 100
[go rust typescript %//%] <nil>
プレースホルダと引数の調整を自動化しますし。
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
15分調べてもわからないことは、teratailで質問しよう!
- ただいまの回答率 88.33%
- 質問をまとめることで、思考を整理して素早く解決
- テンプレート機能で、簡単に質問をまとめられる