Connecting Go with SQL: A Beginner’s Guide

When building backend applications, databases are essential for storing and managing data. In this guide, we’ll explore how to interact with an SQL database using Go (Golang) and the database/sql package. By the end, you’ll understand how to connect to a database, execute queries, and manage data effectively.

Databases are crucial for applications that require persistent data storage. Whether you’re building a web app, an API, or a data processing system, you will likely need a way to interact with a database. Go provides a robust database/sql package that enables developers to work with various SQL databases, including PostgreSQL, MySQL, and SQLite.

1. Setting Up the Go SQL Connection

Before diving into queries, let’s set up a connection to a PostgreSQL database (instead of MySQL used in the video). Ensure you have the PostgreSQL driver installed:

go get github.com/lib/pq

Now, import the required packages and connect to the database:

package main

import (
    "database/sql"
    "fmt"
    "log"
    _ "github.com/lib/pq" // PostgreSQL driver
)

func main() {
    connStr := "user=youruser password=yourpassword dbname=yourdb sslmode=disable"
    db, err := sql.Open("postgres", connStr)
    if err != nil {
        log.Fatal(err)
    }
    defer db.Close()

    err = db.Ping()
    if err != nil {
        log.Fatal("Could not connect to the database", err)
    }
    fmt.Println("Successfully connected to the database")
}

Explanation:

  • sql.Open("postgres", connStr): Opens a connection to PostgreSQL.
  • db.Ping(): Verifies the database connection.
  • defer db.Close(): Ensures the connection is closed when the function exits.

Connecting to a database is the first step in any database-driven application. Ensuring a proper connection allows seamless interaction with the database for reading and writing data.

2. Creating a Table

Now, let’s create a products table to store product details:

_, err = db.Exec(`
    CREATE TABLE IF NOT EXISTS products (
        id SERIAL PRIMARY KEY,
        name TEXT NOT NULL,
        price DECIMAL(10,2) NOT NULL
    )
`)
if err != nil {
    log.Fatal(err)
}
fmt.Println("Table created successfully")

This creates a table with an id, name, and price column. The SERIAL PRIMARY KEY ensures each product has a unique identifier, and TEXT NOT NULL enforces that the name field must have a value.

3. Inserting Data

Now, let’s insert a new product into the table:

result, err := db.Exec("INSERT INTO products (name, price) VALUES ($1, $2)", "Laptop", 999.99)
if err != nil {
    log.Fatal(err)
}

id, err := result.LastInsertId()
if err != nil {
    log.Fatal(err)
}
fmt.Printf("Inserted product with ID: %d\n", id)

Key Points:

  • $1, $2 are placeholders for values.
  • LastInsertId() gets the ID of the newly inserted row.

Inserting data is a fundamental operation when dealing with databases. Ensuring data integrity and security by using prepared statements can prevent SQL injection attacks.

4. Retrieving Data

Fetching records from the products table:

rows, err := db.Query("SELECT id, name, price FROM products")
if err != nil {
    log.Fatal(err)
}
defer rows.Close()

for rows.Next() {
    var id int
    var name string
    var price float64
    if err := rows.Scan(&id, &name, &price); err != nil {
        log.Fatal(err)
    }
    fmt.Printf("ID: %d, Name: %s, Price: %.2f\n", id, name, price)
}

Important: Always use defer rows.Close() to close the result set.

Retrieving data is essential for displaying information to users. Using a loop to iterate through query results ensures all records are properly processed.

5. Updating a Record

To update an existing product:

_, err = db.Exec("UPDATE products SET price = $1 WHERE name = $2", 1099.99, "Laptop")
if err != nil {
    log.Fatal(err)
}
fmt.Println("Product updated successfully")

Updating records allows modifying existing data while maintaining the integrity of the database. Be cautious when updating without specifying constraints to prevent unintended modifications.

6. Deleting a Record

To delete a product:

_, err = db.Exec("DELETE FROM products WHERE name = $1", "Laptop")
if err != nil {
    log.Fatal(err)
}
fmt.Println("Product deleted successfully")

Deleting records should be handled carefully, especially in production environments. It’s a good practice to implement soft deletes (marking records as inactive instead of removing them permanently).

Conclusion

We have successfully connected Go with PostgreSQL, created a table, and performed CRUD (Create, Read, Update, Delete) operations. This guide provides a solid foundation for integrating SQL databases into your Go applications.

🔹 Next Steps: Try extending this code by adding error handling, transaction support, or a REST API using Gorilla Mux!

Understanding how to work with databases in Go is a crucial skill for backend developers. Whether you are building a simple web application or a large-scale system, mastering database/sql will enhance your ability to manage data efficiently.