Golang如何从数据库中获取可用表

Golang如何从数据库中获取可用表 我正在尝试编写一个ODBC适配器,它将接收驱动程序类型和数据源,并建立与数据库的连接。传递数据源让我感到困惑。 有人能帮我理解如何实现这个目标吗?

package odbcstream

import (
	"database/sql"
	"fmt"
	_"github.com/alexbrainman/odbc"
)

// 指向数据库实例的指针
var DBClient *sql.DB

// 初始化数据源凭证的变量
var server, user, password, database string

type Table struct {
	name string
}

// 初始化与所选数据库的连接
func InitialiseDBConnection(driverType, dataSourceName string) error{
	dataSourceName = fmt.Sprintf("server=%s;user id=%s;password=%s;database=%s;",
        server, user, password, database)

	db, err := sql.Open(driverType, dataSourceName)
	if err != nil {
		panic(err.Error())
	}

	// 检查数据库是否已连接
	err = db.Ping()
	if err != nil {
		panic(err.Error())
	}

	DBClient = db
	return db.Close()
}

// 获取数据库中的表列表
func List(t *Table) {
	var tables []Table
	ta, err := DBClient.Query("SELECT * FROM sys.Table") 
		if err != nil {
			panic(err.Error())
		}
	for ta.Next() {
		var table Table
		if err := ta.Scan(&table.name); err != nil {
			fmt.Println("An error scanning for tables", err)
			return
		}
		tables = append(tables, table )
	}
}


package main

import (
	"odbcstream/odbcstream"
	// "fmt"
)

func main() {
       // psql 是 "driver" 类型,"france" 是数据源
	odbcstream.InitialiseDBConnection("psql",	"france")
	// fmt.Println("Hello Go")
}

更多关于Golang如何从数据库中获取可用表的实战教程也可以访问 https://www.itying.com/category-94-b0.html

7 回复

非常感谢您分享的知识和经验。我实际上是Go语言和数据库管理系统的新手,但我非常感激您的澄清和解释。

更多关于Golang如何从数据库中获取可用表的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


感谢您的努力。实际上,我想通过ODBC适配器接口安全地连接到任何数据库(例如:PostgreSQL、MySQL、MSSQL等),并检索在指定为数据库源的源中可用的表。

我原以为ODBC可以替代底层的驱动程序类型,无论是PostgreSQL、MSSQL等,我只需在函数参数中指定数据库使用的驱动程序类型即可。 是否有更好的方法来实现这一点? 我乐于学习,请指教。

Oyemechi:

将数据源传递到一个幻象中对我来说难以理解。

我不知道你在寻找什么,但我正在努力解决一个类似的问题。有些部分已经接近解决。 http://94.237.92.101:6060/api

实际上,我希望与任何数据库(例如:PostgreSQL、MySQL、MSSQL……)建立安全连接。

Home

Home

用 Go 编写的 ODBC 驱动程序。通过在 GitHub 上创建帐户,为 alexbrainman/odbc 的开发做出贡献。

但是,你为什么想使用 ODBC?根据我的经验,每一个中间层都只会使事情复杂化,而不是简化。

Oyemechi:

我以为ODBC可以替代底层的驱动程序类型。

我个人认为,专注于一个特定的数据库系统,而非使用通用驱动程序,才能最大程度地发挥关系型数据库管理系统的优势。举个例子,PostgreSQL 中的简单 WITH 子句功能已经存在多年,而 MySQL 直到最近才支持。缺少某些功能会限制你编写高效 SQL 查询的能力。如果你使用通用驱动程序,我猜你只能使用所有数据库都支持的最基础功能,这无疑会限制查询的可能性。

同样的观点也适用于任何通用的对象关系映射工具,它们同样会限制编写复杂 SQL 查询的能力。这种“魔法”迟早会带来限制。

我使用 PostgreSQL 已近十年,它一直非常稳定可靠,并且允许你在同一个数据库中混合使用 NoSQL 和 SQL 操作。

最后,我认为还有一点很重要。当你需要社区帮助时,我注意到,对于使用任何形式的未知“中间件”所提出的问题,得到的回答往往较少,因为特定代码组合的知识是有限的。

在Golang中从数据库获取可用表的方法取决于具体的数据库类型。以下是针对不同数据库的通用实现方法:

1. 修复你的ODBC连接实现

首先,你的数据源传递方式需要调整:

func InitialiseDBConnection(driverType, dataSourceName string) error {
    // 直接使用传入的dataSourceName,而不是重新构建
    db, err := sql.Open(driverType, dataSourceName)
    if err != nil {
        return fmt.Errorf("failed to open database: %v", err)
    }

    // 检查数据库是否已连接
    if err = db.Ping(); err != nil {
        return fmt.Errorf("failed to ping database: %v", err)
    }

    DBClient = db
    return nil // 不要在这里关闭连接!
}

2. 通用表查询方法

// 获取所有表名
func GetAllTables() ([]string, error) {
    var tables []string
    
    // 通用SQL查询(需要根据数据库类型调整)
    query := `
        SELECT table_name 
        FROM information_schema.tables 
        WHERE table_schema = 'public' 
        AND table_type = 'BASE TABLE'
    `
    
    rows, err := DBClient.Query(query)
    if err != nil {
        // 如果通用查询失败,尝试特定数据库的查询
        return getTablesByDriver()
    }
    defer rows.Close()
    
    for rows.Next() {
        var tableName string
        if err := rows.Scan(&tableName); err != nil {
            return nil, fmt.Errorf("scan error: %v", err)
        }
        tables = append(tables, tableName)
    }
    
    return tables, nil
}

3. 根据数据库类型获取表

func getTablesByDriver() ([]string, error) {
    var tables []string
    var query string
    
    // 根据驱动类型使用不同的系统表查询
    switch driverType {
    case "postgres", "pgx":
        query = "SELECT tablename FROM pg_catalog.pg_tables WHERE schemaname != 'pg_catalog' AND schemaname != 'information_schema'"
    
    case "mysql":
        query = "SHOW TABLES"
    
    case "sqlserver", "mssql":
        query = "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE'"
    
    case "sqlite3":
        query = "SELECT name FROM sqlite_master WHERE type='table'"
    
    case "odbc":
        // ODBC通用查询
        query = "SELECT table_name FROM information_schema.tables"
    
    default:
        return nil, fmt.Errorf("unsupported driver type: %s", driverType)
    }
    
    rows, err := DBClient.Query(query)
    if err != nil {
        return nil, fmt.Errorf("query error: %v", err)
    }
    defer rows.Close()
    
    for rows.Next() {
        var tableName string
        if err := rows.Scan(&tableName); err != nil {
            return nil, fmt.Errorf("scan error: %v", err)
        }
        tables = append(tables, tableName)
    }
    
    return tables, nil
}

4. 改进的Table结构和方法

type TableInfo struct {
    TableName    string
    TableSchema  string
    TableType    string
    CreateTime   sql.NullTime
}

// 获取详细的表信息
func GetTableDetails() ([]TableInfo, error) {
    var tables []TableInfo
    
    query := `
        SELECT 
            table_name, 
            table_schema, 
            table_type,
            create_time
        FROM information_schema.tables 
        WHERE table_schema NOT IN ('information_schema', 'pg_catalog', 'sys')
        ORDER BY table_schema, table_name
    `
    
    rows, err := DBClient.Query(query)
    if err != nil {
        return nil, fmt.Errorf("query error: %v", err)
    }
    defer rows.Close()
    
    for rows.Next() {
        var table TableInfo
        if err := rows.Scan(&table.TableName, &table.TableSchema, 
                           &table.TableType, &table.CreateTime); err != nil {
            return nil, fmt.Errorf("scan error: %v", err)
        }
        tables = append(tables, table)
    }
    
    return tables, nil
}

5. 使用示例

package main

import (
    "database/sql"
    "fmt"
    "log"
    _ "github.com/alexbrainman/odbc"
)

func main() {
    // ODBC连接字符串示例
    dsn := "Driver={SQL Server};Server=localhost;Database=testdb;Trusted_Connection=yes;"
    
    // 初始化连接
    db, err := sql.Open("odbc", dsn)
    if err != nil {
        log.Fatal(err)
    }
    defer db.Close()
    
    // 获取所有表
    tables, err := GetAllTables(db)
    if err != nil {
        log.Fatal(err)
    }
    
    fmt.Println("Available tables:")
    for _, table := range tables {
        fmt.Printf("- %s\n", table)
    }
}

// 独立函数版本
func GetAllTables(db *sql.DB) ([]string, error) {
    var tables []string
    
    // 尝试多种查询方法
    queries := []string{
        "SELECT name FROM sysobjects WHERE xtype='U'", // SQL Server
        "SELECT table_name FROM information_schema.tables",
        "SHOW TABLES", // MySQL
    }
    
    for _, query := range queries {
        rows, err := db.Query(query)
        if err == nil {
            defer rows.Close()
            for rows.Next() {
                var tableName string
                if err := rows.Scan(&tableName); err == nil {
                    tables = append(tables, tableName)
                }
            }
            if len(tables) > 0 {
                return tables, nil
            }
        }
    }
    
    return tables, fmt.Errorf("unable to retrieve tables")
}

6. 处理特定数据库的系统表

// 获取SQL Server的表
func GetSQLServerTables(db *sql.DB) ([]string, error) {
    var tables []string
    
    rows, err := db.Query(`
        SELECT s.name + '.' + t.name 
        FROM sys.tables t
        INNER JOIN sys.schemas s ON t.schema_id = s.schema_id
        ORDER BY s.name, t.name
    `)
    if err != nil {
        return nil, err
    }
    defer rows.Close()
    
    for rows.Next() {
        var tableName string
        if err := rows.Scan(&tableName); err != nil {
            return nil, err
        }
        tables = append(tables, tableName)
    }
    
    return tables, nil
}

关键点:

  1. 数据源名称应该直接传递给sql.Open(),而不是在函数内部重新构建
  2. 不同数据库有不同的系统表查询方式
  3. 使用information_schema.tables是跨数据库的通用方法
  4. 需要处理不同驱动类型的特定查询语法
回到顶部