1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
|
<!--{
"Title": "Tutorial: Accessing a relational database",
"Breadcrumb": true
}-->
This tutorial introduces the basics of accessing a relational database with
Go and the `database/sql` package in its standard library.
You'll get the most out of this tutorial if you have a basic familiarity with
Go and its tooling. If this is your first exposure to Go, please see
[Tutorial: Get started with Go](/doc/tutorial/getting-started)
for a quick introduction.
The [`database/sql`](https://pkg.go.dev/database/sql) package you'll
be using includes types and functions for connecting to databases, executing
transactions, canceling an operation in progress, and more. For more details
on using the package, see
[Accessing databases](/doc/database/index).
In this tutorial, you'll create a database, then write code to access the
database. Your example project will be a repository of data about vintage
jazz records.
In this tutorial, you'll progress through the following sections:
1. Create a folder for your code.
2. Set up a database.
3. Import the database driver.
4. Get a database handle and connect.
5. Query for multiple rows.
6. Query for a single row.
7. Add data.
**Note:** For other tutorials, see [Tutorials](/doc/tutorial/index.html).
## Prerequisites {#prerequisites}
* **An installation of the [MySQL](https://dev.mysql.com/doc/mysql-installation-excerpt/5.7/en/)
relational database management system (DBMS).**
* **An installation of Go.** For installation instructions, see
[Installing Go](/doc/install).
* **A tool to edit your code.** Any text editor you have will work fine.
* **A command terminal.** Go works well using any terminal on Linux and Mac,
and on PowerShell or cmd in Windows.
## Create a folder for your code {#create_folder}
To begin, create a folder for the code you'll write.
1. Open a command prompt and change to your home directory.
On Linux or Mac:
```
$ cd
```
On Windows:
```
C:\> cd %HOMEPATH%
```
For the rest of the tutorial we will show a $ as the prompt. The
commands we use will work on Windows too.
2. From the command prompt, create a directory for your code called
data-access.
```
$ mkdir data-access
$ cd data-access
```
3. Create a module in which you can manage dependencies you will add during
this tutorial.
Run the `go mod init` command, giving it your new code's module path.
```
$ go mod init example/data-access
go: creating new go.mod: module example/data-access
```
This command creates a go.mod file in which dependencies you add will be
listed for tracking. For more, be sure to see
[Managing dependencies](/doc/modules/managing-dependencies).
**Note:** In actual development, you'd specify a module path that's
more specific to your own needs. For more, see
[Managing dependencies](/doc/modules/managing-dependencies#naming_module).
Next, you'll create a database.
## Set up a database {#set_up_database}
In this step, you'll create the database you'll be working with. You'll use
the CLI for the DBMS itself to create the database and table, as well as to
add data.
You'll be creating a database with data about vintage jazz recordings on vinyl.
The code here uses the [MySQL CLI](https://dev.mysql.com/doc/refman/8.0/en/mysql.html),
but most DBMSes have their own CLI with similar features.
1. Open a new command prompt.
2. At the command line, log into your DBMS, as in the following example for
MySQL.
```
$ mysql -u root -p
Enter password:
mysql>
```
3. At the `mysql` command prompt, create a database.
```
mysql> create database recordings;
```
4. Change to the database you just created so you can add tables.
```
mysql> use recordings;
Database changed
```
5. In your text editor, in the data-access folder, create a file called
create-tables.sql to hold SQL script for adding tables.
6. Into the file, paste the following SQL code, then save the file.
```
DROP TABLE IF EXISTS album;
CREATE TABLE album (
id INT AUTO_INCREMENT NOT NULL,
title VARCHAR(128) NOT NULL,
artist VARCHAR(255) NOT NULL,
price DECIMAL(5,2) NOT NULL,
PRIMARY KEY (`id`)
);
INSERT INTO album
(title, artist, price)
VALUES
('Blue Train', 'John Coltrane', 56.99),
('Giant Steps', 'John Coltrane', 63.99),
('Jeru', 'Gerry Mulligan', 17.99),
('Sarah Vaughan', 'Sarah Vaughan', 34.98);
```
In this SQL code, you:
* Delete (drop) a table called `album`. Executing this command first makes
it easier for you to re-run the script later if you want to start over
with the table.
* Create an `album` table with four columns: `title`, `artist`, and `price`.
Each row's `id` value is created automatically by the DBMS.
* Add four rows with values.
7. From the `mysql` command prompt, run the script you just created.
You'll use the `source` command in the following form:
```
mysql> source /path/to/create-tables.sql
```
8. At your DBMS command prompt, use a `SELECT` statement to verify you've
successfully created the table with data.
```
mysql> select * from album;
+----+---------------+----------------+-------+
| id | title | artist | price |
+----+---------------+----------------+-------+
| 1 | Blue Train | John Coltrane | 56.99 |
| 2 | Giant Steps | John Coltrane | 63.99 |
| 3 | Jeru | Gerry Mulligan | 17.99 |
| 4 | Sarah Vaughan | Sarah Vaughan | 34.98 |
+----+---------------+----------------+-------+
4 rows in set (0.00 sec)
```
Next, you'll write some Go code to connect so you can query.
## Find and import a database driver {#import_driver}
Now that you've got a database with some data, get your Go code started.
Locate and import a database driver that will translate requests you make
through functions in the `database/sql` package into requests the database
understands.
1. In your browser, visit the [SQLDrivers](/wiki/SQLDrivers)
wiki page to identify a driver you can use.
Use the list on the page to identify the driver you'll use. For accessing
MySQL in this tutorial, you'll use
[Go-MySQL-Driver](https://github.com/go-sql-driver/mysql/).
2. Note the package name for the driver -- here, `github.com/go-sql-driver/mysql`.
3. Using your text editor, create a file in which to write your Go code and
save the file as main.go in the data-access directory you created earlier.
4. Into main.go, paste the following code to import the driver package.
```
package main
import "github.com/go-sql-driver/mysql"
```
In this code, you:
* Add your code to a `main` package so you can execute it independently.
* Import the MySQL driver `github.com/go-sql-driver/mysql`.
With the driver imported, you'll start writing code to access the database.
## Get a database handle and connect {#get_handle}
Now write some Go code that gives you database access with a database handle.
You'll use a pointer to an `sql.DB` struct, which represents access to a
specific database.
#### Write the code
1. Into main.go, beneath the `import` code you just added, paste the following
Go code to create a database handle.
```
var db *sql.DB
func main() {
// Capture connection properties.
cfg := mysql.NewConfig()
cfg.User = os.Getenv("DBUSER")
cfg.Passwd = os.Getenv("DBPASS")
cfg.Net = "tcp"
cfg.Addr = "127.0.0.1:3306"
cfg.DBName = "recordings"
// Get a database handle.
var err error
db, err = sql.Open("mysql", cfg.FormatDSN())
if err != nil {
log.Fatal(err)
}
pingErr := db.Ping()
if pingErr != nil {
log.Fatal(pingErr)
}
fmt.Println("Connected!")
}
```
In this code, you:
* Declare a `db` variable of type [`*sql.DB`](https://pkg.go.dev/database/sql#DB).
This is your database handle.
Making `db` a global variable simplifies this example. In
production, you'd avoid the global variable, such as by passing the
variable to functions that need it or by wrapping it in a struct.
* Use the MySQL driver's [`Config`](https://pkg.go.dev/github.com/go-sql-driver/mysql#Config)
-- and the type's [`FormatDSN`](https://pkg.go.dev/github.com/go-sql-driver/mysql#Config.FormatDSN)
-– to collect connection properties and format them into a DSN for a connection string.
The `Config` struct makes for code that's easier to read than a
connection string would be.
* Call [`sql.Open`](https://pkg.go.dev/database/sql#Open)
to initialize the `db` variable, passing the return value of
`FormatDSN`.
* Check for an error from `sql.Open`. It could fail if, for
example, your database connection specifics weren't well-formed.
To simplify the code, you're calling `log.Fatal` to end
execution and print the error to the console. In production code, you'll
want to handle errors in a more graceful way.
* Call [`DB.Ping`](https://pkg.go.dev/database/sql#DB.Ping) to
confirm that connecting to the database works. At run time,
`sql.Open` might not immediately connect, depending on the
driver. You're using `Ping` here to confirm that the
`database/sql` package can connect when it needs to.
* Check for an error from `Ping`, in case the connection failed.
* Print a message if `Ping` connects successfully.
2. Near the top of the main.go file, just beneath the package declaration,
import the packages you'll need to support the code you've just written.
The top of the file should now look like this:
```
package main
import (
"database/sql"
"fmt"
"log"
"os"
"github.com/go-sql-driver/mysql"
)
```
3. Save main.go.
#### Run the code
1. Begin tracking the MySQL driver module as a dependency.
Use the [`go get`](/cmd/go/#hdr-Add_dependencies_to_current_module_and_install_them)
to add the github.com/go-sql-driver/mysql module as a dependency for your
own module. Use a dot argument to mean "get dependencies for code in the
current directory."
```
$ go get .
go: added filippo.io/edwards25519 v1.1.0
go: added github.com/go-sql-driver/mysql v1.8.1
```
Go downloaded this dependency because you added it to the `import`
declaration in the previous step. For more about dependency tracking,
see [Adding a dependency](/doc/modules/managing-dependencies#adding_dependency).
2. From the command prompt, set the `DBUSER` and `DBPASS` environment variables
for use by the Go program.
On Linux or Mac:
```
$ export DBUSER=username
$ export DBPASS=password
```
On Windows:
```
C:\Users\you\data-access> set DBUSER=username
C:\Users\you\data-access> set DBPASS=password
```
3. From the command line in the directory containing main.go, run the code by
typing `go run` with a dot argument to mean "run the package in the
current directory."
```
$ go run .
Connected!
```
You can connect! Next, you'll query for some data.
## Query for multiple rows {#multiple_rows}
In this section, you'll use Go to execute an SQL query designed to return
multiple rows.
For SQL statements that might return multiple rows, you use the `Query` method
from the `database/sql` package, then loop through the rows it returns. (You'll
learn how to query for a single row later, in the section
[Query for a single row](#single_row).)
#### Write the code
1. Into main.go, immediately above `func main`, paste the following definition
of an `Album` struct. You'll use this to hold row data returned from the
query.
```
type Album struct {
ID int64
Title string
Artist string
Price float32
}
```
2. Beneath `func main`, paste the following `albumsByArtist` function to query
the database.
```
// albumsByArtist queries for albums that have the specified artist name.
func albumsByArtist(name string) ([]Album, error) {
// An albums slice to hold data from returned rows.
var albums []Album
rows, err := db.Query("SELECT * FROM album WHERE artist = ?", name)
if err != nil {
return nil, fmt.Errorf("albumsByArtist %q: %v", name, err)
}
defer rows.Close()
// Loop through rows, using Scan to assign column data to struct fields.
for rows.Next() {
var alb Album
if err := rows.Scan(&alb.ID, &alb.Title, &alb.Artist, &alb.Price); err != nil {
return nil, fmt.Errorf("albumsByArtist %q: %v", name, err)
}
albums = append(albums, alb)
}
if err := rows.Err(); err != nil {
return nil, fmt.Errorf("albumsByArtist %q: %v", name, err)
}
return albums, nil
}
```
In this code, you:
* Declare an `albums` slice of the `Album` type you defined. This will hold
data from returned rows. Struct field names and types correspond to
database column names and types.
* Use [`DB.Query`](https://pkg.go.dev/database/sql#DB.Query) to
execute a `SELECT` statement to query for albums with the
specified artist name.
`Query`'s first parameter is the SQL statement. After the
parameter, you can pass zero or more parameters of any type. These provide
a place for you to specify the values for parameters in your SQL statement.
By separating the SQL statement from parameter values (rather than
concatenating them with, say, `fmt.Sprintf`), you enable the
`database/sql` package to send the values separate from the SQL
text, removing any SQL injection risk.
* Defer closing `rows` so that any resources it holds will be released when
the function exits.
* Loop through the returned rows, using
[`Rows.Scan`](https://pkg.go.dev/database/sql#Rows.Scan) to
assign each row’s column values to `Album` struct fields.
`Scan` takes a list of pointers to Go values, where the column
values will be written. Here, you pass pointers to fields in the
`alb` variable, created using the `&` operator.
`Scan` writes through the pointers to update the struct fields.
* Inside the loop, check for an error from scanning column values into the
struct fields.
* Inside the loop, append the new `alb` to the `albums` slice.
* After the loop, check for an error from the overall query, using
`rows.Err`. Note that if the query itself fails, checking for an error
here is the only way to find out that the results are incomplete.
3. Update your `main` function to call `albumsByArtist`.
To the end of `func main`, add the following code.
```
albums, err := albumsByArtist("John Coltrane")
if err != nil {
log.Fatal(err)
}
fmt.Printf("Albums found: %v\n", albums)
```
In the new code, you now:
* Call the `albumsByArtist` function you added, assigning its return value to
a new `albums` variable.
* Print the result.
#### Run the code
From the command line in the directory containing main.go, run the code.
```
$ go run .
Connected!
Albums found: [{1 Blue Train John Coltrane 56.99} {2 Giant Steps John Coltrane 63.99}]
```
Next, you'll query for a single row.
## Query for a single row {#single_row}
In this section, you'll use Go to query for a single row in the database.
For SQL statements you know will return at most a single row, you can use
`QueryRow`, which is simpler than using a `Query` loop.
#### Write the code
1. Beneath `albumsByArtist`, paste the following `albumByID` function.
```
// albumByID queries for the album with the specified ID.
func albumByID(id int64) (Album, error) {
// An album to hold data from the returned row.
var alb Album
row := db.QueryRow("SELECT * FROM album WHERE id = ?", id)
if err := row.Scan(&alb.ID, &alb.Title, &alb.Artist, &alb.Price); err != nil {
if err == sql.ErrNoRows {
return alb, fmt.Errorf("albumsById %d: no such album", id)
}
return alb, fmt.Errorf("albumsById %d: %v", id, err)
}
return alb, nil
}
```
In this code, you:
* Use [`DB.QueryRow`](https://pkg.go.dev/database/sql#DB.QueryRow)
to execute a `SELECT` statement to query for an album with the
specified ID.
It returns an `sql.Row`. To simplify the calling code
(your code!), `QueryRow` doesn't return an error. Instead,
it arranges to return any query error (such as `sql.ErrNoRows`)
from `Rows.Scan` later.
* Use [`Row.Scan`](https://pkg.go.dev/database/sql#Row.Scan) to copy
column values into struct fields.
* Check for an error from `Scan`.
The special error `sql.ErrNoRows` indicates that the query returned no
rows. Typically that error is worth replacing with more specific text,
such as “no such album” here.
2. Update `main` to call `albumByID`.
To the end of `func main`, add the following code.
```
// Hard-code ID 2 here to test the query.
alb, err := albumByID(2)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Album found: %v\n", alb)
```
In the new code, you now:
* Call the `albumByID` function you added.
* Print the album ID returned.
#### Run the code
From the command line in the directory containing main.go, run the code.
```
$ go run .
Connected!
Albums found: [{1 Blue Train John Coltrane 56.99} {2 Giant Steps John Coltrane 63.99}]
Album found: {2 Giant Steps John Coltrane 63.99}
```
Next, you'll add an album to the database.
## Add data {#add_data}
In this section, you'll use Go to execute an SQL `INSERT` statement to add a
new row to the database.
You’ve seen how to use `Query` and `QueryRow` with SQL statements that
return data. To execute SQL statements that _don't_ return data, you use `Exec`.
#### Write the code
1. Beneath `albumByID`, paste the following `addAlbum` function to insert a new
album in the database, then save the main.go.
```
// addAlbum adds the specified album to the database,
// returning the album ID of the new entry
func addAlbum(alb Album) (int64, error) {
result, err := db.Exec("INSERT INTO album (title, artist, price) VALUES (?, ?, ?)", alb.Title, alb.Artist, alb.Price)
if err != nil {
return 0, fmt.Errorf("addAlbum: %v", err)
}
id, err := result.LastInsertId()
if err != nil {
return 0, fmt.Errorf("addAlbum: %v", err)
}
return id, nil
}
```
In this code, you:
* Use [`DB.Exec`](https://pkg.go.dev/database/sql#DB.Exec) to
execute an `INSERT` statement.
Like `Query`, `Exec` takes an SQL statement followed
by parameter values for the SQL statement.
* Check for an error from the attempt to `INSERT`.
* Retrieve the ID of the inserted database row using
[`Result.LastInsertId`](https://pkg.go.dev/database/sql#Result.LastInsertId).
* Check for an error from the attempt to retrieve the ID.
2. Update `main` to call the new `addAlbum` function.
To the end of `func main`, add the following code.
```
albID, err := addAlbum(Album{
Title: "The Modern Sound of Betty Carter",
Artist: "Betty Carter",
Price: 49.99,
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("ID of added album: %v\n", albID)
```
In the new code, you now:
* Call `addAlbum` with a new album, assigning the ID of the album you're
adding to an `albID` variable.
#### Run the code
From the command line in the directory containing main.go, run the code.
```
$ go run .
Connected!
Albums found: [{1 Blue Train John Coltrane 56.99} {2 Giant Steps John Coltrane 63.99}]
Album found: {2 Giant Steps John Coltrane 63.99}
ID of added album: 5
```
## Conclusion {#conclusion}
Congratulations! You've just used Go to perform simple actions with a
relational database.
Suggested next topics:
* Take a look at the data access guide, which includes more information
about the subjects only touched on here.
* If you're new to Go, you'll find useful best practices described in
[Effective Go](/doc/effective_go) and [How to write Go code](/doc/code).
* The [Go Tour](/tour/) is a great step-by-step
introduction to Go fundamentals.
## Completed code {#completed_code}
This section contains the code for the application you build with this tutorial.
```
package main
import (
"database/sql"
"fmt"
"log"
"os"
"github.com/go-sql-driver/mysql"
)
var db *sql.DB
type Album struct {
ID int64
Title string
Artist string
Price float32
}
func main() {
// Capture connection properties.
cfg := mysql.NewConfig()
cfg.User = os.Getenv("DBUSER")
cfg.Passwd = os.Getenv("DBPASS")
cfg.Net = "tcp"
cfg.Addr = "127.0.0.1:3306"
cfg.DBName = "recordings"
// Get a database handle.
var err error
db, err = sql.Open("mysql", cfg.FormatDSN())
if err != nil {
log.Fatal(err)
}
pingErr := db.Ping()
if pingErr != nil {
log.Fatal(pingErr)
}
fmt.Println("Connected!")
albums, err := albumsByArtist("John Coltrane")
if err != nil {
log.Fatal(err)
}
fmt.Printf("Albums found: %v\n", albums)
// Hard-code ID 2 here to test the query.
alb, err := albumByID(2)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Album found: %v\n", alb)
albID, err := addAlbum(Album{
Title: "The Modern Sound of Betty Carter",
Artist: "Betty Carter",
Price: 49.99,
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("ID of added album: %v\n", albID)
}
// albumsByArtist queries for albums that have the specified artist name.
func albumsByArtist(name string) ([]Album, error) {
// An albums slice to hold data from returned rows.
var albums []Album
rows, err := db.Query("SELECT * FROM album WHERE artist = ?", name)
if err != nil {
return nil, fmt.Errorf("albumsByArtist %q: %v", name, err)
}
defer rows.Close()
// Loop through rows, using Scan to assign column data to struct fields.
for rows.Next() {
var alb Album
if err := rows.Scan(&alb.ID, &alb.Title, &alb.Artist, &alb.Price); err != nil {
return nil, fmt.Errorf("albumsByArtist %q: %v", name, err)
}
albums = append(albums, alb)
}
if err := rows.Err(); err != nil {
return nil, fmt.Errorf("albumsByArtist %q: %v", name, err)
}
return albums, nil
}
// albumByID queries for the album with the specified ID.
func albumByID(id int64) (Album, error) {
// An album to hold data from the returned row.
var alb Album
row := db.QueryRow("SELECT * FROM album WHERE id = ?", id)
if err := row.Scan(&alb.ID, &alb.Title, &alb.Artist, &alb.Price); err != nil {
if err == sql.ErrNoRows {
return alb, fmt.Errorf("albumsById %d: no such album", id)
}
return alb, fmt.Errorf("albumsById %d: %v", id, err)
}
return alb, nil
}
// addAlbum adds the specified album to the database,
// returning the album ID of the new entry
func addAlbum(alb Album) (int64, error) {
result, err := db.Exec("INSERT INTO album (title, artist, price) VALUES (?, ?, ?)", alb.Title, alb.Artist, alb.Price)
if err != nil {
return 0, fmt.Errorf("addAlbum: %v", err)
}
id, err := result.LastInsertId()
if err != nil {
return 0, fmt.Errorf("addAlbum: %v", err)
}
return id, nil
}
```
|