Golang查询中无记录返回的陷阱与解决方案

Golang查询中无记录返回的陷阱与解决方案 是否有办法捕获空接口?

我从Postgresql获取数据并填充一个列表。如果列表为空,我希望重定向到另一个页面。以下是我的尝试(未成功):

list := Get(query)

switch list {
case nil:
    returns []
    fmt.Println("no result")
default:
    returns [map[post_id:1 post_subject:Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit...] map[post_id:2 post_subject:Vestibulum ante ipsum primis in faucibus orci luctus et ultrices]]
    fmt.Println("show result")
}

更多关于Golang查询中无记录返回的陷阱与解决方案的实战教程也可以访问 https://www.itying.com/category-94-b0.html

10 回复

嗨,@Sibert,哪里出问题了?是构建时还是使用时出错?错误信息是什么?

更多关于Golang查询中无记录返回的陷阱与解决方案的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


skillian:

哪里出了问题?

无论是否为空,结果都完全一样。没有错误。

[ ] 给出 OK [map[post_id:1] 给出 OK

nil 切片与空切片并不相同;切片可能已经初始化但没有任何元素。与其使用:

switch list {
case nil:
    // ...

我建议:

switch {
case len(list) == 0:
    // 没有结果。
default:
    // 至少有一个结果
}

skillian:

我推荐:

Go Playground - The Go Programming Language

Go Playground - The Go Programming Language

len 的参数列表无效(类型为 interface {})

skillian:

你能把这个完整的例子放到 Go Playground 和/或某个可公开访问的源代码控制仓库中吗?

目的是在用户搜索时呈现“无数据”,并且没有记录可显示。

http://94.237.92.101:3030/mytopics

这不是完整的例子,但包含了核心部分。

Go Playground - The Go Programming Language

我需要在某个地方检查列表是否不包含数据。据我所知,errNoRows 不起作用。

如果 Get(query) 返回空接口

这个方向是正确的还是死胡同?

Go Playground

Go Playground - The Go Programming Language

package main

import (
	"fmt"
)

func main() {

	list := map[string]string{"id": "kjh"}
	listempty := map[string]string{"id": ""}

	result1 := check(len(list["id"]))
	result2 := check(len(listempty["id"]))

	fmt.Println(result1)
	fmt.Println(result2)

}

func check(ok int) string {
	switch ok {
	case 0:
		return ("empty")
	default:
		return ("OK")
	}
}

如果 Get(query) 返回空接口,并且在无结果时不返回 nil,我不确定正确的处理方式。我会考虑重构那个 Get 函数以返回更合适的类型,但在此期间,你可以尝试:

import "reflect"

func isEmpty(v interface{}) bool {
    if v == nil {
        return true
    }
    switch v := v.(type) {
    case []interface{}:    // 也许在回退到反射之前先测试一些其他类型
        return len(v) == 0
    default:
        rv := reflect.ValueOf(v)
        switch rv.Kind() {
        case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice, reflect.String:
            return rv.Len() == 0
        }
        return false
    }
}

list := Get(query)

switch {
case isEmpty(list):
    // ...
default:
    // else
}

skillian:

你最新的例子与我最初的设想有很大不同。

我在最初的问题中写道:

“我从Postgresql获取数据并填充一个列表。如果列表为空,我想重定向到另一个页面。” 目的是告诉用户没有获取到数据。

我能想到的有两种选择:

  1. 在查询过程中检查数据库,看结果是否为空。
  2. 在查询结果返回后检查结果。

在其他一些语言中,你可以用一种简单的方式同时完成这两件事,但Go语言让这变得困难。例如,选项#1和#2都可以像 If list.$linecount=0… 这样实现。

由于Go语言是另一种“野兽”,而我完全是个新手,我很难理解为什么检查查询结果是否为空不能更简单一些。

比如,当使用 db.QueryRow 时,Scan 会返回 ErrNoRows,但不知为何,对于 db.Query(…) 生成的列表却不这样。

@Sibert 那是在检查映射中是否有任何键值对,但我推测你在第一篇帖子中打印的 list 表示形式:

Sibert:

returns [map[post_id:1 post_subject:Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit...] map[post_id:2 post_subject:Vestibulum ante ipsum primis in faucibus orci luctus et ultrices]]

看起来是一个映射切片,所以检查单个映射的长度不会告诉你是否存在任何映射。

我试图根据伪代码和 panic 消息对你想要做的事情做出合理的猜测,但你最新的例子与我最初的假设有很大不同。如果你试图检查实际映射的长度,你最新的 Go Playground 示例会起作用,但如果你仍在寻找其他东西,你能把完整的例子放到 Go playground 和/或某个可公开访问的源代码控制仓库中吗?

在Golang中处理数据库查询无记录的情况,关键在于正确区分不同类型的"空值"。以下是几种常见的解决方案:

1. 检查切片长度(推荐)

list := Get(query)

if len(list) == 0 {
    // 重定向到其他页面
    fmt.Println("no result")
    // return redirectToOtherPage()
} else {
    fmt.Println("show result")
    // 处理数据
    for _, item := range list {
        fmt.Printf("Post ID: %v\n", item["post_id"])
    }
}

2. 使用类型断言检查nil

如果你的Get()函数可能返回nil切片:

list := Get(query)

if list == nil {
    fmt.Println("nil slice - no result")
} else if len(list) == 0 {
    fmt.Println("empty slice - no result")
} else {
    fmt.Println("show result")
}

3. 改进Get函数返回错误

更健壮的做法是让Get()函数返回错误:

func Get(query string) ([]map[string]interface{}, error) {
    rows, err := db.Query(query)
    if err != nil {
        return nil, err
    }
    defer rows.Close()
    
    var results []map[string]interface{}
    for rows.Next() {
        // 解析行数据到map
        result := make(map[string]interface{})
        // ... 填充数据
        results = append(results, result)
    }
    
    if err = rows.Err(); err != nil {
        return nil, err
    }
    
    return results, nil
}

// 使用方式
list, err := Get(query)
if err != nil {
    // 处理数据库错误
    log.Printf("Query error: %v", err)
    return
}

if len(list) == 0 {
    fmt.Println("no result")
    // 重定向逻辑
} else {
    fmt.Println("show result")
}

4. 使用结构体代替map(类型安全)

type Post struct {
    ID      int    `db:"post_id"`
    Subject string `db:"post_subject"`
}

func GetPosts(query string) ([]Post, error) {
    var posts []Post
    err := db.Select(&posts, query)
    if err != nil {
        return nil, err
    }
    return posts, nil
}

// 使用方式
posts, err := GetPosts(query)
if err != nil {
    // 处理错误
}

if len(posts) == 0 {
    fmt.Println("no posts found")
    // 重定向
} else {
    fmt.Printf("Found %d posts\n", len(posts))
    for _, post := range posts {
        fmt.Printf("ID: %d, Subject: %s\n", post.ID, post.Subject)
    }
}

5. 针对你的switch语句修正

如果你想使用switch语句,可以这样写:

list := Get(query)

switch {
case list == nil:
    fmt.Println("nil - no result")
case len(list) == 0:
    fmt.Println("empty - no result")
    // http.Redirect(w, r, "/other-page", http.StatusFound)
default:
    fmt.Println("show result")
    fmt.Printf("Results: %v\n", list)
}

关键点:

  • 空切片[]和nil切片在长度上都是0,但len()函数对两者都适用
  • 使用len(list) == 0可以同时处理nil切片和空切片的情况
  • 对于数据库操作,建议始终返回错误以便正确处理异常情况
回到顶部