问题描述
我正在处理Go中的多对多关系。为此,我使用的是pgx
Postgresql驱动程序。
为了使这个问题尽可能简单,我们假设一个简单的博客文章可以包含一些标签:
CREATE TABLE IF NOT EXISTS tag (
id bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,tagName varchar(255) UNIQUE NOT NULL,);
CREATE TABLE IF NOT EXISTS post (
id bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,title varchar(255) NOT NULL,description varchar(255),);
CREATE TABLE IF NOT EXISTS post_tag (
post_id bigint REFERENCES post (id) ON UPDATE CASCADE ON DELETE CASCADE,tag_id bigint REFERENCES tag (id) ON UPDATE CASCADE ON DELETE CASCADE,CONSTRAINT post_tag_pkey PRIMARY KEY (post_id,tag_id)
);
要检索帖子及其标签,我使用类似以下查询的内容(在视图中便于在Go中查询):
SELECT p.id as post_id,p.title,p.description,t.id as tag_id,t.tagName as tag_name
FROM post p
LEFT JOIN post_tag pt
ON p.id = pt.post_id
LEFT JOIN tag t
ON t.id = pt.tag_id;
此查询可能会返回一些行,其中tag_id
和tag_name
为空。我当前的处理方式如下(为简单起见,删除了错误处理):
func ReadPosts() ([]*model.Post,error) {
var posts []*model.Post
var postWithTags = make(map[uint64]*model.Post)
statement := `SELECT *
FROM post_with_tag` // pgsql view with joins to get tags
rows,_ := db.Query(
context.Background(),statement,)
for rows.Next() {
var (
post model.Post
tag model.Tag
tagID pgtype.Numeric
tagName pgtype.Varchar
tagValid bool
)
_ = rows.Scan(
&post.ID,&post.Title,&post.Description,&tagID,&tagName,)
if tagID.Status == pgtype.Present {
tag.ID = tagID.Int.Uint64()
tag.Name = tagName.String
tagValid = true
} else {
tagValid = false
}
if _,ok := postWithTags[post.ID]; ok {
if tagValid {
postWithTags[post.ID].Tags = append(postWithTags[post.ID].Tags,&tag)
}
} else {
post.Tags = []*model.Tag{}
if tagValid {
post.Tags = []*model.Tag{
&tag,}
}
postWithTags[post.ID] = &post
}
}
for _,v := range postWithTags {
posts = append(posts,v)
}
return posts,nil
}
如您所见,我正在使用pgtype
处理潜在的空值。我应该提到此解决方案有效。但是,我有两个问题:
- 此解决方案似乎很笨拙且混乱;读起来很复杂(至少对我来说)。 是否有更好的,更惯用的Go方式?
- 调用
tagID.Int.Uint64()
时,总是返回0
作为标签ID,这是不正确的。 我在这里做错什么了吗?(我使用pgtype.Numeric
是因为数据库中的标记ID是pgsqlbigint
)。
解决方法
如果您使用的是表格视图,而无需按<html>
<head>
<title>Question 2</title>
<script>
var images = ["frog.jpg","lion.jpg"] //I Created an array with both images inside
var imgInterval;
var imgi = 'empty';
var score = document.getElementById('score');
function start(){
imgInterval = setInterval(displayImage,500); //Sets an interval for the func displayImage and for it to loop ever 500ms
}
function displayImage() {
if (imgi == 'empty') { //If the index is empty set the interval to 0 or frog.jpg
imgi = 0;
} else if (imgi == 0) { // If the index is set to 0 set it to 1 or lion.jpg
imgi = 1;
} else if (imgi == 1) { // If the idex is set to 1 set it back to 0,this creates a loop that will be run every 500ms
imgi = 0;
}
document.getElementById('image').src = images[imgi];
}
function stop(){
clearInterval(imgInterval);
}
function scoreNumb() { //This is where i think i am having issues and am unsure what to do.
if (imgi.onclick == 0) {
score + 1;
}
}
</script>
</head>
<body>
<button onclick=start()>Start Animation</button>
<button onclick=stop()>Stop Animation</button>
<br/> <br/>
<span>
<img id="image" onclick=scoreNumb() width="250px" />
</span>
<br/>
<span id="score">0</span>
</body>
</html>
(例如NULL
)进行过滤,则可能只想在视图中使用WHERE col IS [NOT] NULL
,您可以在Go结束时省掉一些头痛的方法。如果您直接处理表,仍然可以在Go中构造的SQL COALESCE
字符串中使用COALESCE
,但是如果选择这样做,将无法使用它与statement
一起使用,而是必须显式列出这些列。
如果您不喜欢SELECT *
方法,则可以实现自己的COALESCE
,该方法不需要一个值字段,而是一个指针字段,该指针字段允许您通过以下方式设置模型字段:间接扫描与您扫描列所在的同一行。
sql.Scanner
_ = rows.Scan(
&post.ID,&post.Title,&post.Description,MyNullNumeric{&tag.ID},MyNullString{&tag.Name},)
可能看起来像这样:
MyNullXxx