😺

sqlc がちょっと惜しい

2021/11/01に公開2

概要

Go 言語でデータベース操作をする方法は、標準パッケージ・GORM・sqlx などいくつかあります。
今回はそのうちの一つの sqlc を使ってみたので、所感をまとめておこうと思います。

sqlc とは

sqlc を使うために必要なファイルは3つあります。

  1. スキーマファイル(database/migrations/users.sql)
  2. 使用するデータベースの種類やパッケージ名などを指定する設定ファイル(sqlc.yaml)
  3. 使うクエリをまとめたファイル(query.sql)
-- users.sql
CREATE TABLE IF NOT EXISTS users(
    id INTEGER NOT NULL AUTO_INCREMENT,
    name VARCHAR(30) NOT NULL,
    age INTEGER NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
# sqlc.yaml
version: 1
packages:
  - path: "repository"
    name: "repository"
    engine: "mysql"
    schema: "database/migrations/"
    queries: "query.sql"
    emit_db_tags: true
-- query.sql
-- name: InsertUser :exec
INSERT INTO users(name, age) VALUES (?, ?);

query.sql にあるコメントは sqlc 独自なので詳しくはドキュメントを参考にしてください。
これらを用意して、sqlc generate を実行すると path に指定した名前でディレクトリが作られます。パッケージ名は name で指定できます。
中には query.sql.godb.gomodels.go の3つのファイルができます。

これらのコードを使うことで、データベースにアクセスする層を簡単に作れます。
sqlc は標準パッケージの database/sql を使っているので速度が早く、クエリを自分で定義するのでどのようなクエリが発行されているかブラックボックスにならないです。

sqlc がちょっと惜しい

使いやすく良い感じなのですが、現状だとできないクエリがあったので採用を見送ることにしました。
それは IN 句を使ったクエリです。下のように IN を使ったクエリのユースケースはあると思いますが、sqlc だと現状使えないです。(参照)

これを query.sql に書いて sqlc generate をしても意図したコードにならないです。

SELECT * FROM users WHERE id IN (?);

以下のように IN で指定する代わりに一回ずつ取得することも可能ですが、データベースへのアクセスが増えてしまいパフォーマンスの劣化につながります。

SELECT * FROM users WHERE id = 1
SELECT * FROM users WHERE id = 2
SELECT * FROM users WHERE id = 3

まとめ

データベースにアクセスするコードを自動生成できて便利なのですが、本番で使うにはまだ機能が足りないかなという感じです。
マイグレーションだったり一部分だけ使うなど、工夫する必要がありそうです。

Discussion

FlowingSPDGFlowingSPDG

こんにちは!この記事を書かれた時点では無かったかもしれませんが、現在は sqlc.sliceという機能を用いることでスライスを使ったIN句の指定が出来るようになっています。
また触れられている機能不足に関しては、特にMySQLを対象生成した際が顕著でしてpgx(Postgresql)ではかなり多くの機能に対応しているなーという所感です。
ご参考になれば幸いです!

https://play.sqlc.dev/p/169eeffaffd403c366eb5387ad824929ce8614157e809bd944227dcd935d6ae4

-- query.sql
-- name: InsertUsers :exec
INSERT INTO users(name, age) VALUES ($1, $2);

-- name: SelectUsers :many
SELECT * FROM users WHERE id IN (sqlc.slice('ids'));

-- name: SelectUsersByAge :many
SELECT * FROM users WHERE age in (sqlc.slice('ages'));
woo-noowoo-noo

こんにちは!
今はできるようになっているんですね。ここ最近 sqlc を使っている会社のテックブログなどをよく見かけるのでまた勉強してみようと思います

また触れられている機能不足に関しては、特にMySQLを対象生成した際が顕著でしてpgx(Postgresql)ではかなり多くの機能に対応しているなーという所感です。

なるほど。自分が普段 MySQL を使うのでその前提で記事を書いていました。次からはその辺りも考慮して記事を書こうと思います。ありがとうございます