在Golang中执行带有可变数量命名参数的SQL查询

huangapple go评论141阅读模式
英文:

Executing SQL query with variable number of named parameters in Golang

问题

我有一个 PostgreSQL 函数,它接受可变数量的命名参数,并返回相应项的列表:

  1. CREATE OR REPLACE FUNCTION read_user(
  2. _id BIGINT DEFAULT NULL,
  3. _phone VARCHAR(30) DEFAULT NULL,
  4. _type VARCHAR(15) DEFAULT NULL,
  5. _last VARCHAR(50) DEFAULT NULL,
  6. _first VARCHAR(50) DEFAULT NULL
  7. )
  8. RETURNS setof T_USERS
  9. AS $$
  10. BEGIN
  11. RETURN QUERY
  12. SELECT * FROM T_USERS
  13. WHERE ( id = _id OR _id IS NULL )
  14. AND ( phone = _phone OR _phone IS NULL )
  15. AND ( type = _type OR _type IS NULL )
  16. AND ( last = _last OR _last IS NULL )
  17. AND ( first = _first OR _first IS NULL );
  18. EXCEPTION WHEN others THEN
  19. RAISE WARNING 'Transaction failed and was rolled back';
  20. RAISE NOTICE '% %', SQLERRM, SQLSTATE;
  21. END
  22. $$ LANGUAGE plpgsql;

我可以运行类似这样的多态查询:

  1. SELECT read_user(_id := 2);
  2. SELECT read_user(_first := 'John', _last := 'Doe');

在 Golang 中,我可以这样做:

  1. stmt, err := db.Prepare("SELECT read_user(_id = ?)")

但是,如何使用可变数量的 read_user 参数做同样的事情呢?我正在使用 pq 驱动程序 https://github.com/lib/pq

英文:

So I have this PostgreSQL function, which takes variable number of named arguments and returns list of corresponding items:

  1. CREATE OR REPLACE FUNCTION read_user(
  2. _id BIGINT DEFAULT NULL,
  3. _phone VARCHAR(30) DEFAULT NULL,
  4. _type VARCHAR(15) DEFAULT NULL,
  5. _last VARCHAR(50) DEFAULT NULL,
  6. _first VARCHAR(50) DEFAULT NULL
  7. )
  8. RETURNS setof T_USERS
  9. AS $$
  10. BEGIN
  11. RETURN QUERY
  12. SELECT * FROM T_USERS
  13. WHERE ( id = _id OR _id IS NULL )
  14. AND ( phone = _phone OR _phone IS NULL )
  15. AND ( type = _type OR _type IS NULL )
  16. AND ( last = _last OR _last IS NULL )
  17. AND ( first = _first OR _first IS NULL );
  18. EXCEPTION WHEN others THEN
  19. RAISE WARNING 'Transaction failed and was rolled back';
  20. RAISE NOTICE '% %', SQLERRM, SQLSTATE;
  21. END
  22. $$ LANGUAGE plpgsql;

So I can run polymorphic queries like these:

  1. SELECT read_user(_id := 2);
  2. SELECT read_user(_first := 'John', _last := 'Doe');

In Golang I can make something like:

  1. stmt, err := db.Prepare("SELECT read_user(_id = ?)")

But how can I do the same, but with variable amount of read_user arguments? I'm using pq driver https://github.com/lib/pq.

答案1

得分: 4

你可以通过列举所有参数及其占位符来构建自己的语句,然后在没有参数值的地方显式地传递nil

  1. stmt, err := db.Prepare("SELECT read_user(_id := $1, _phone := $2, _type := $3, _last := $4, _first := $5)")
  2. if err != nil {
  3. // ...
  4. }
  5. stmt.Query(2, nil, nil, nil, nil) // 结果应等同于 `SELECT read_user(_id := 2)`
  6. stmt.Query(nil, nil, nil, "Doe", "John") // 结果应等同于 `SELECT read_user(_first := 'John', _last := 'Doe')`

如果你也想在Go中使用命名参数,你可以创建一个结构体类型来表示参数,并创建一个包装函数,将该参数类型的字段映射到查询中:

  1. type readUserParams struct {
  2. Id interface{}
  3. Phone interface{}
  4. Type interface{}
  5. Last interface{}
  6. First interface{}
  7. }
  8. func readUser(p *readUserParams) {
  9. stmt.Query(p.Id, p.Phone, p.Type, p.Last, p.First)
  10. // ...
  11. }
  12. readUser(&readUserParams{Id: 2})
  13. readUser(&readUserParams{First: "John", Last: "Doe"})
英文:

You can construct your one statement by enumerating all the parameters with their placeholders and then you could pass nil explicitly where you don't have the parameter value.

  1. stmt, err := db.Prepare("SELECT read_user(_id := $1, _phone := $2, _type := $3, _last := $4, _first := $5)")
  2. if err != nil {
  3. // ...
  4. }
  5. stmt.Query(2, nil, nil, nil, nil) // result should be equivalent to `SELECT read_user(_id := 2)`
  6. stmt.Query(nil, nil, nil, "Doe", "John") // result should be equivalent to `SELECT read_user(_first := 'John', _last := 'Doe')`

And if you want to have named parameters in Go as well, you can create a struct type to represent the parameters and a wrapper func that'll map that parameter type's fields into the query:

  1. type readUserParams struct {
  2. Id interface{}
  3. Phone interface{}
  4. Type interface{}
  5. Last interface{}
  6. First interface{}
  7. }
  8. func readUser(p *readUserParams) {
  9. stmt.Query(p.Id, p.Phone, p.Type, p.Last, p.First)
  10. // ...
  11. }
  12. readUser(&readUserParams{Id: 2})
  13. readUser(&readUserParams{First: "John", Last:"Doe"})

huangapple
  • 本文由 发表于 2017年4月30日 05:20:08
  • 转载请务必保留本文链接:https://go.coder-hub.com/43700597.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定