Postgres 数组在 Golang 结构中的应用
怎么入门Golang编程?需要学习哪些知识点?这是新手们刚接触编程时常见的问题;下面golang学习网就来给大家整理分享一些知识点,希望能够给初学者一些帮助。本篇文章就来介绍《Postgres 数组在 Golang 结构中的应用》,涉及到,有需要的可以收藏一下
我有以下 go 结构:
type bar struct { stuff string `db:"stuff"` other string `db:"other"` } type foo struct { id int `db:"id"` bars []*bar `db:"bars"` }
因此 foo
包含 bar
指针的切片。我在 postgres 中还有以下表格:
create table foo ( id int ) create table bar ( id int, stuff varchar, other varchar, trash varchar )
我想在表 bar
上进行 left join
并将其聚合为一个数组以存储在结构 foo
中。我试过:
SELECT f.*, ARRAY_AGG(b.stuff, b.other) AS bars FROM foo f LEFT JOIN bar b ON f.id = b.id WHERE f.id = $1 GROUP BY f.id
但看起来 array_agg
函数签名不正确(function array_agg(charactervaring,charactervarying)不存在
)。有没有办法在不单独查询 bar
的情况下执行此操作?
解决方案
正如您所知,array_agg
接受一个单个参数并返回该参数类型的数组。因此,如果您希望行的所有列都包含在数组的元素中,您可以直接传递行引用,例如:
select array_agg(b) from b
但是,如果您只想在数组元素中包含特定列,则可以使用 ROW
构造函数,例如:
select array_agg(row(b.stuff, b.other)) from b
go 的标准库为仅扫描标量值提供开箱即用的支持。要扫描更复杂的值(例如任意对象和数组),必须寻找第 3 方解决方案,或实现他们自己的 sql.Scanner
。
为了能够实现您自己的 sql.scanner
并正确解析 postgres 行数组,您首先需要知道 postgres 使用什么格式输出值,您可以直接使用 psql
和一些查询来找到这一点: p>
-- simple values select array[row(123,'foo'),row(456,'bar')]; -- output: {"(123,foo)","(456,bar)"} -- not so simple values select array[row(1,'a b'),row(2,'a,b'),row(3,'a",b'),row(4,'(a,b)'),row(5,'"','""')]; -- output: {"(1,\"a b\")","(2,\"a,b\")","(3,\"a\"\",b\")","(4,\"(a,b)\")","(5,\"\"\"\",\"\"\"\"\"\")"}
正如你所看到的,这可能会变得非常复杂,但尽管如此,它是可以解析的,语法看起来像这样:
{"(column_value[, ...])"[, ...]}
其中 column_value
是一个不带引号的值,或者是带有转义双引号的带引号的值,并且这样的带引号的值本身可以包含转义双引号,但只能包含两个,即单个转义双引号将不会出现在 column_value
内部。因此,解析器的粗略且不完整的实现可能如下所示:
注意:可能还有其他我不知道的语法规则,在解析过程中需要考虑。除此之外,下面的代码无法正确处理 null。
func parserowarray(a []byte) (out [][]string) { a = a[1 : len(a)-1] // drop surrounding curlies for i := 0; i < len(a); i++ { if a[i] == '"' { // start of row element row := []string{} i += 2 // skip over current '"' and the following '(' for j := i; j < len(a); j++ { if a[j] == '\\' && a[j+1] == '"' { // start of quoted column value var col string // column value j += 2 // skip over current '\' and following '"' for k := j; k < len(a); k++ { if a[k] == '\\' && a[k+1] == '"' { // end of quoted column, maybe if a[k+2] == '\\' && a[k+3] == '"' { // nope, just escaped quote col += string(a[j:k]) + `"` k += 3 // skip over `\"\` (the k++ in the for statement will skip over the `"`) j = k + 1 // skip over `\"\"` continue // go to k loop } else { // yes, end of quoted column col += string(a[j:k]) row = append(row, col) j = k + 2 // skip over `\"` break // go back to j loop } } } if a[j] == ')' { // row end out = append(out, row) i = j + 1 // advance i to j's position and skip the potential ',' break // go to back i loop } } else { // assume non quoted column value for k := j; k < len(a); k++ { if a[k] == ',' || a[k] == ')' { // column value end col := string(a[j:k]) row = append(row, col) j = k // advance j to k's position break // go back to j loop } } if a[j] == ')' { // row end out = append(out, row) i = j + 1 // advance i to j's position and skip the potential ',' break // go to back i loop } } } } } return out }
拨打 playground
试试。
有了类似的东西,你就可以为你的 go 条形切片实现 sql.scanner
。
type barlist []*bar func (ls *barlist) scan(src interface{}) error { switch data := src.(type) { case []byte: a := praserowarray(data) res := make(barlist, len(a)) for i := 0; i < len(a); i++ { bar := new(bar) // here i'm assuming the parser produced a slice of at least two // strings, if there are cases where this may not be the true you // should add proper length checks to avoid unnecessary panics. bar.stuff = a[i][0] bar.other = a[i][1] res[i] = bar } *ls = res } return nil }
现在,如果您将 foo
类型中的 bars
字段的类型从 []*bar
更改为 barlist
,您将能够直接将该字段的指针传递给 (*sql.row|*sql) .rows).scan
调用:
rows.scan(&f.bars)
如果您不想更改字段的类型,您仍然可以通过在将指针传递给 scan
方法时转换指针来使其工作:
rows.scan((*barlist)(&f.bars))
json
henry woody 建议的 json 解决方案的 sql.scanner
实现如下所示:
type barlist []*bar func (ls *barlist) scan(src interface{}) error { if b, ok := src.([]byte); ok { return json.unmarshal(b, ls) } return nil }
看起来你想要的是 bars
成为一个 bar 对象数组来匹配你的 go 类型。为此,您应该使用 json_agg
而不是 array_agg
,因为 array_agg
仅适用于单列,并且在这种情况下会生成文本类型的数组 (text[]
)。另一方面,json_agg
创建一个 json 对象数组。您可以将其与 json_build_object
结合使用,以仅选择所需的列。
这是一个例子:
select f.*, json_agg(json_build_object('stuff', b.stuff, 'other', b.other)) as bars from foo f left join bar b on f.id = b.id where f.id = $1 group by f.id
然后你必须在 go 中处理对 json 的解组,但除此之外你应该可以开始了。
另请注意,在将 json 解组到结构体时,go 会忽略未使用的键,因此如果需要,您可以通过选择 bar
表上的所有字段来简化查询。就像这样:
select f.*, json_agg(to_json(b.*)) as bars -- or json_agg(b.*) from foo f left join bar b on f.id = b.id where f.id = $1 group by f.id
如果您还想处理 bar
中没有 foo
记录的条目的情况,您可以使用:
SELECT f.*, COALESCE( JSON_AGG(TO_JSON(b.*)) FILTER (WHERE b.id IS NOT NULL), '[]'::JSON ) AS bars FROM foo f LEFT JOIN bar b ON f.id = b.id WHERE f.id = $1 GROUP BY f.id
如果没有 filter
,您将获得 [null]
,其中 foo
中的行在 bar
中没有对应的行,而 filter
只提供 null
,然后只需使用 zqbc zqbcoalesce 转换为空 json 数组。
文中关于的知识介绍,希望对你的学习有所帮助!若是受益匪浅,那就动动鼠标收藏这篇《Postgres 数组在 Golang 结构中的应用》文章吧,也可关注golang学习网公众号了解相关技术文章。

- 上一篇
- Revel 模板如何访问列表中的值

- 下一篇
- 修改苹果设备的MAC地址文件
-
- Golang · Go问答 | 1年前 |
- 在读取缓冲通道中的内容之前退出
- 139浏览 收藏
-
- Golang · Go问答 | 1年前 |
- 戈兰岛的全球 GOPRIVATE 设置
- 204浏览 收藏
-
- Golang · Go问答 | 1年前 |
- 如何将结构作为参数传递给 xml-rpc
- 325浏览 收藏
-
- Golang · Go问答 | 1年前 |
- 如何用golang获得小数点以下两位长度?
- 477浏览 收藏
-
- Golang · Go问答 | 1年前 |
- 如何通过 client-go 和 golang 检索 Kubernetes 指标
- 486浏览 收藏
-
- Golang · Go问答 | 1年前 |
- 将多个“参数”映射到单个可变参数的习惯用法
- 439浏览 收藏
-
- Golang · Go问答 | 1年前 |
- 将 HTTP 响应正文写入文件后出现 EOF 错误
- 357浏览 收藏
-
- Golang · Go问答 | 1年前 |
- 结构中映射的匿名列表的“复合文字中缺少类型”
- 352浏览 收藏
-
- Golang · Go问答 | 1年前 |
- NATS Jetstream 的性能
- 101浏览 收藏
-
- Golang · Go问答 | 1年前 |
- 如何将复杂的字符串输入转换为mapstring?
- 440浏览 收藏
-
- Golang · Go问答 | 1年前 |
- 相当于GoLang中Java将Object作为方法参数传递
- 212浏览 收藏
-
- Golang · Go问答 | 1年前 |
- 如何确保所有 goroutine 在没有 time.Sleep 的情况下终止?
- 143浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 542次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 508次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 497次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 484次学习
-
- 笔灵AI生成答辩PPT
- 探索笔灵AI生成答辩PPT的强大功能,快速制作高质量答辩PPT。精准内容提取、多样模板匹配、数据可视化、配套自述稿生成,让您的学术和职场展示更加专业与高效。
- 24次使用
-
- 知网AIGC检测服务系统
- 知网AIGC检测服务系统,专注于检测学术文本中的疑似AI生成内容。依托知网海量高质量文献资源,结合先进的“知识增强AIGC检测技术”,系统能够从语言模式和语义逻辑两方面精准识别AI生成内容,适用于学术研究、教育和企业领域,确保文本的真实性和原创性。
- 38次使用
-
- AIGC检测-Aibiye
- AIbiye官网推出的AIGC检测服务,专注于检测ChatGPT、Gemini、Claude等AIGC工具生成的文本,帮助用户确保论文的原创性和学术规范。支持txt和doc(x)格式,检测范围为论文正文,提供高准确性和便捷的用户体验。
- 37次使用
-
- 易笔AI论文
- 易笔AI论文平台提供自动写作、格式校对、查重检测等功能,支持多种学术领域的论文生成。价格优惠,界面友好,操作简便,适用于学术研究者、学生及论文辅导机构。
- 48次使用
-
- 笔启AI论文写作平台
- 笔启AI论文写作平台提供多类型论文生成服务,支持多语言写作,满足学术研究者、学生和职场人士的需求。平台采用AI 4.0版本,确保论文质量和原创性,并提供查重保障和隐私保护。
- 41次使用
-
- GoLand调式动态执行代码
- 2023-01-13 502浏览
-
- 用Nginx反向代理部署go写的网站。
- 2023-01-17 502浏览
-
- Golang取得代码运行时间的问题
- 2023-02-24 501浏览
-
- 请问 go 代码如何实现在代码改动后不需要Ctrl+c,然后重新 go run *.go 文件?
- 2023-01-08 501浏览
-
- 如何从同一个 io.Reader 读取多次
- 2023-04-11 501浏览