質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.33%

SQLのIN演算子をgolangから使いたい場合

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 3,014

m0a

score 700

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ページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

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%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る