Getting started with Spanner in Go database/sql Stay organized with collections Save and categorize content based on your preferences.
Objectives
This tutorial walks you through the following steps using the Spannerdatabase/sql driver:
- Create a Spanner instance and database.
- Write, read, and execute SQL queries on data in the database.
- Update the database schema.
- Update data using a read-write transaction.
- Add a secondary index to the database.
- Use the index to read and execute SQL queries on data.
- Retrieve data using a read-only transaction.
Costs
This tutorial uses Spanner, which is a billable component of theGoogle Cloud. For information on the cost of using Spanner, seePricing.
Before you begin
Complete the steps described inSet up, which cover creating andsetting a default Google Cloud project, enabling billing, enabling theCloud Spanner API, and setting up OAuth 2.0 to get authentication credentials to usethe Cloud Spanner API.
In particular, make sure that you rungcloud authapplication-default loginto set up your local development environment with authenticationcredentials.
Prepare your local database/sql environment
Download and installGo on your developmentmachine if it isn't already installed.
Clone the sample repository to your local machine:
gitclonehttps://github.com/googleapis/go-sql-spanner.gitChange to the directory that contains the Spanner sample code:
cdgo-sql-spanner/snippets
Create an instance
When you first use Spanner, you must create an instance, which is anallocation of resources that are used by Spanner databases. When youcreate an instance, you choose aninstance configuration, which determineswhere your data is stored, and also the number of nodes to use, which determinesthe amount of serving and storage resources in your instance.
SeeCreate an instanceto learn how to create a Spanner instance using any of thefollowing methods. You can name your instancetest-instance to use it withother topics in this document that reference an instance namedtest-instance.
- The Google Cloud CLI
- The Google Cloud console
- A client library (C++, C#, Go, Java, Node.js, PHP, Python, or Ruby)
Look through sample files
The samples repository contains a sample that shows how to use Spannerwith database/sql.
Take a look through thegetting_started_guide.go file, which shows how to useSpanner. The code shows how to create and use a new database. The datauses the example schema shown in theSchema and data model page.Create a database
GoogleSQL
gcloudspannerdatabasescreateexample-db--instance=test-instancePostgreSQL
gcloudspannerdatabasescreateexample-db--instance=test-instance \--database-dialect=POSTGRESQLYou should see:
Creatingdatabase...done.Create tables
The following code creates two tables in the database.Note: The subsequent code samples use these two tables. If you don't executethis code, then create the tables by using the Google Cloud console or thegcloud CLI. For more information, see theexample schema. Run the sample with the following command: The next step is to write data to your database. GoogleSQL
import("context""database/sql""fmt""io"_"github.com/googleapis/go-sql-spanner")funcCreateTables(ctxcontext.Context,wio.Writer,databaseNamestring)error{db,err:=sql.Open("spanner",databaseName)iferr!=nil{returnerr}deferdb.Close()//CreatetwotablesinonebatchonSpanner.conn,err:=db.Conn(ctx)deferconn.Close()//StartaDDLbatchontheconnection.//ThisinstructstheconnectiontobufferallDDLstatementsuntilthe//command`runbatch`isexecuted.if_,err:=conn.ExecContext(ctx,"start batch ddl");err!=nil{returnerr}if_,err:=conn.ExecContext(ctx,`CREATETABLESingers(SingerIdINT64NOTNULL,FirstNameSTRING(1024),LastNameSTRING(1024),SingerInfoBYTES(MAX))PRIMARYKEY(SingerId)`);err!=nil{returnerr}if_,err:=conn.ExecContext(ctx,`CREATETABLEAlbums(SingerIdINT64NOTNULL,AlbumIdINT64NOTNULL,AlbumTitleSTRING(MAX))PRIMARYKEY(SingerId,AlbumId),INTERLEAVEINPARENTSingersONDELETECASCADE`);err!=nil{returnerr}//`runbatch`sendstheDDLstatementstoSpannerandblocksuntil//allstatementshavefinishedexecuting.if_,err:=conn.ExecContext(ctx,"run batch");err!=nil{returnerr}fmt.Fprintf(w,"Created Singers & Albums tables in database: [%s]\n",databaseName)returnnil} PostgreSQL
import("context""database/sql""fmt""io"_"github.com/googleapis/go-sql-spanner")funcCreateTablesPostgreSQL(ctxcontext.Context,wio.Writer,databaseNamestring)error{db,err:=sql.Open("spanner",databaseName)iferr!=nil{returnerr}deferfunc(){_=db.Close()}()//CreatetwotablesinonebatchonSpannerPostgreSQL.conn,err:=db.Conn(ctx)iferr!=nil{returnerr}deferfunc(){_=conn.Close()}()//StartaDDLbatchontheconnection.//ThisinstructstheconnectiontobufferallDDLstatementsuntilthe//command`runbatch`isexecuted.if_,err:=conn.ExecContext(ctx,"start batch ddl");err!=nil{returnerr}if_,err:=conn.ExecContext(ctx,`createtablesingers(singer_idbigintnotnullprimarykey,first_namevarchar(1024),last_namevarchar(1024),singer_infobytea)`);err!=nil{returnerr}if_,err:=conn.ExecContext(ctx,`createtablealbums(singer_idbigintnotnull,album_idbigintnotnull,album_titlevarchar,primarykey(singer_id,album_id))interleaveinparentsingersondeletecascade`);err!=nil{returnerr}//`runbatch`sendstheDDLstatementstoSpannerandblocksuntil//allstatementshavefinishedexecuting.if_,err:=conn.ExecContext(ctx,"run batch");err!=nil{returnerr}_,_=fmt.Fprintf(w,"Created singers & albums tables in database: [%s]\n",databaseName)returnnil} GoogleSQL
gorungetting_started_guide.gocreatetablesprojects/GCLOUD_PROJECT/instances/test-instance/databases/example-db PostgreSQL
gorungetting_started_guide.gocreatetablespgprojects/GCLOUD_PROJECT/instances/test-instance/databases/example-dbCreate a connection
Before you can do reads or writes, you must create asql.DB.sql.DB contains a connection poolthat can be used to interact with Spanner. The database name andother connection properties are specified in the database/sql data source name.
GoogleSQL
import("context""database/sql""fmt""io"_"github.com/googleapis/go-sql-spanner")funcCreateConnection(ctxcontext.Context,wio.Writer,databaseNamestring)error{//ThedataSourceNameshouldstartwithafullyqualifiedSpannerdatabasename//intheformat`projects/my-project/instances/my-instance/databases/my-database`.//Additionalpropertiescanbeaddedafterthedatabasenameby//addingoneormore`;name=value`pairs.dsn:=fmt.Sprintf("%s;numChannels=8",databaseName)db,err:=sql.Open("spanner",dsn)iferr!=nil{returnerr}deferdb.Close()row:=db.QueryRowContext(ctx,"select 'Hello world!' as hello")varmsgstringiferr:=row.Scan(&msg);err!=nil{returnerr}fmt.Fprintf(w,"Greeting from Spanner:%s\n",msg)returnnil}PostgreSQL
import("context""database/sql""fmt""io"_"github.com/googleapis/go-sql-spanner")funcCreateConnectionPostgreSQL(ctxcontext.Context,wio.Writer,databaseNamestring)error{//ThedataSourceNameshouldstartwithafullyqualifiedSpannerdatabasename//intheformat`projects/my-project/instances/my-instance/databases/my-database`.//Additionalpropertiescanbeaddedafterthedatabasenameby//addingoneormore`;name=value`pairs.dsn:=fmt.Sprintf("%s;numChannels=8",databaseName)db,err:=sql.Open("spanner",dsn)iferr!=nil{returnerr}deferfunc(){_=db.Close()}()//TheSpannerdatabase/sqldriversupportsbothPostgreSQL-stylequery//parameters($1,$2,...)andpositionalqueryparameters(?,?,...).row:=db.QueryRowContext(ctx,"select $1 as hello","Hello world!")varmsgstringiferr:=row.Scan(&msg);err!=nil{returnerr}_,_=fmt.Fprintf(w,"Greeting from Spanner PostgreSQL:%s\n",msg)returnnil}Write data with DML
You can insert data using Data Manipulation Language (DML) in a read-writetransaction.
You use theExecContext function to execute a DML statement.
GoogleSQL
import("context""database/sql""fmt""io"_"github.com/googleapis/go-sql-spanner")funcWriteDataWithDml(ctxcontext.Context,wio.Writer,databaseNamestring)error{db,err:=sql.Open("spanner",databaseName)iferr!=nil{returnerr}deferdb.Close()//Add4rowsinonestatement.//Thedatabase/sqldriversupportspositionalqueryparameters.res,err:=db.ExecContext(ctx,"INSERT INTO Singers (SingerId, FirstName, LastName) "+"VALUES (?, ?, ?), (?, ?, ?), "+" (?, ?, ?), (?, ?, ?)",12,"Melissa","Garcia",13,"Russel","Morales",14,"Jacqueline","Long",15,"Dylan","Shaw")iferr!=nil{returnerr}c,err:=res.RowsAffected()iferr!=nil{returnerr}fmt.Fprintf(w,"%v records inserted\n",c)returnnil}PostgreSQL
import("context""database/sql""fmt""io"_"github.com/googleapis/go-sql-spanner")funcWriteDataWithDmlPostgreSQL(ctxcontext.Context,wio.Writer,databaseNamestring)error{db,err:=sql.Open("spanner",databaseName)iferr!=nil{returnerr}deferfunc(){_=db.Close()}()//Add4rowsinonestatement.//Thedatabase/sqldriversupportspositionalqueryparameters.res,err:=db.ExecContext(ctx,"insert into singers (singer_id, first_name, last_name) "+"values (?, ?, ?), (?, ?, ?), "+" (?, ?, ?), (?, ?, ?)",12,"Melissa","Garcia",13,"Russel","Morales",14,"Jacqueline","Long",15,"Dylan","Shaw")iferr!=nil{returnerr}c,err:=res.RowsAffected()iferr!=nil{returnerr}_,_=fmt.Fprintf(w,"%v records inserted\n",c)returnnil}Run the sample with the following command:
GoogleSQL
gorungetting_started_guide.godmlwriteprojects/GCLOUD_PROJECT/instances/test-instance/databases/example-dbPostgreSQL
gorungetting_started_guide.godmlwritepgprojects/GCLOUD_PROJECT/instances/test-instance/databases/example-dbThe result shows:
4recordsinserted.Write data with mutations
You can also insert data usingmutations.
AMutation isa container for mutation operations. AMutation represents a sequence ofinserts, updates, and deletes that Spanner applies atomically todifferent rows and tables in a Spanner database.
UseMutation.InsertOrUpdate()to construct anINSERT_OR_UPDATE mutation, which adds a new row or updatescolumn values if the row already exists. Alternatively, use theMutation.Insert()method to construct anINSERT mutation, which adds a new row.
conn.Raw function to get a reference to the underlyingSpanner connection. TheSpannerConn.Apply function appliesmutations atomically to the database.The following code shows how to write the data using mutations:
GoogleSQL
import("context""database/sql""fmt""io""cloud.google.com/go/spanner"spannerdriver"github.com/googleapis/go-sql-spanner")funcWriteDataWithMutations(ctxcontext.Context,wio.Writer,databaseNamestring)error{db,err:=sql.Open("spanner",databaseName)iferr!=nil{returnerr}deferdb.Close()//GetaconnectionsothatwecangetaccesstotheSpannerspecific//connectioninterfaceSpannerConn.conn,err:=db.Conn(ctx)iferr!=nil{returnerr}deferconn.Close()singerColumns:=[]string{"SingerId","FirstName","LastName"}albumColumns:=[]string{"SingerId","AlbumId","AlbumTitle"}mutations:=[]*spanner.Mutation{spanner.Insert("Singers",singerColumns,[]interface{}{int64(1),"Marc","Richards"}),spanner.Insert("Singers",singerColumns,[]interface{}{int64(2),"Catalina","Smith"}),spanner.Insert("Singers",singerColumns,[]interface{}{int64(3),"Alice","Trentor"}),spanner.Insert("Singers",singerColumns,[]interface{}{int64(4),"Lea","Martin"}),spanner.Insert("Singers",singerColumns,[]interface{}{int64(5),"David","Lomond"}),spanner.Insert("Albums",albumColumns,[]interface{}{int64(1),int64(1),"Total Junk"}),spanner.Insert("Albums",albumColumns,[]interface{}{int64(1),int64(2),"Go, Go, Go"}),spanner.Insert("Albums",albumColumns,[]interface{}{int64(2),int64(1),"Green"}),spanner.Insert("Albums",albumColumns,[]interface{}{int64(2),int64(2),"Forever Hold Your Peace"}),spanner.Insert("Albums",albumColumns,[]interface{}{int64(2),int64(3),"Terrified"}),}//MutationscanbewrittenoutsideanexplicittransactionusingSpannerConn#Apply.iferr:=conn.Raw(func(driverConninterface{})error{spannerConn,ok:=driverConn.(spannerdriver.SpannerConn)if!ok{returnfmt.Errorf("unexpected driver connection %v, expected SpannerConn",driverConn)}_,err=spannerConn.Apply(ctx,mutations)returnerr});err!=nil{returnerr}fmt.Fprintf(w,"Inserted %v rows\n",len(mutations))returnnil}PostgreSQL
import("context""database/sql""fmt""io""cloud.google.com/go/spanner"spannerdriver"github.com/googleapis/go-sql-spanner")funcWriteDataWithMutationsPostgreSQL(ctxcontext.Context,wio.Writer,databaseNamestring)error{db,err:=sql.Open("spanner",databaseName)iferr!=nil{returnerr}deferfunc(){_=db.Close()}()//GetaconnectionsothatwecangetaccesstotheSpannerspecific//connectioninterfaceSpannerConn.conn,err:=db.Conn(ctx)iferr!=nil{returnerr}deferfunc(){_=conn.Close()}()singerColumns:=[]string{"singer_id","first_name","last_name"}albumColumns:=[]string{"singer_id","album_id","album_title"}mutations:=[]*spanner.Mutation{spanner.Insert("singers",singerColumns,[]interface{}{int64(1),"Marc","Richards"}),spanner.Insert("singers",singerColumns,[]interface{}{int64(2),"Catalina","Smith"}),spanner.Insert("singers",singerColumns,[]interface{}{int64(3),"Alice","Trentor"}),spanner.Insert("singers",singerColumns,[]interface{}{int64(4),"Lea","Martin"}),spanner.Insert("singers",singerColumns,[]interface{}{int64(5),"David","Lomond"}),spanner.Insert("albums",albumColumns,[]interface{}{int64(1),int64(1),"Total Junk"}),spanner.Insert("albums",albumColumns,[]interface{}{int64(1),int64(2),"Go, Go, Go"}),spanner.Insert("albums",albumColumns,[]interface{}{int64(2),int64(1),"Green"}),spanner.Insert("albums",albumColumns,[]interface{}{int64(2),int64(2),"Forever Hold Your Peace"}),spanner.Insert("albums",albumColumns,[]interface{}{int64(2),int64(3),"Terrified"}),}//MutationscanbewrittenoutsideanexplicittransactionusingSpannerConn#Apply.iferr:=conn.Raw(func(driverConninterface{})error{spannerConn,ok:=driverConn.(spannerdriver.SpannerConn)if!ok{returnfmt.Errorf("unexpected driver connection %v, expected SpannerConn",driverConn)}_,err=spannerConn.Apply(ctx,mutations)returnerr});err!=nil{returnerr}_,_=fmt.Fprintf(w,"Inserted %v rows\n",len(mutations))returnnil}Run the following example using thewrite argument:
GoogleSQL
gorungetting_started_guide.gowriteprojects/GCLOUD_PROJECT/instances/test-instance/databases/example-dbPostgreSQL
gorungetting_started_guide.gowritepgprojects/GCLOUD_PROJECT/instances/test-instance/databases/example-dbQuery data using SQL
Spanner supports a SQL interface for reading data, which you canaccess on the command line using the Google Cloud CLI orprogrammatically usingthe Spanner database/sql driver.
On the command line
Execute the following SQL statement to read the values of all columns from theAlbums table:
GoogleSQL
gcloudspannerdatabasesexecute-sqlexample-db--instance=test-instance \--sql='SELECT SingerId, AlbumId, AlbumTitle FROM Albums'PostgreSQL
gcloudspannerdatabasesexecute-sqlexample-db--instance=test-instance \--sql='SELECT singer_id, album_id, album_title FROM albums'The result shows:
SingerIdAlbumIdAlbumTitle11TotalJunk12Go,Go,Go21Green22ForeverHoldYourPeace23TerrifiedUse the Spanner database/sql driver
In addition to executing a SQL statement on the command line, you can issue thesame SQL statement programmatically using the Spanner database/sql driver.
The following functions and structs are used to execute a SQL query:- The
QueryContextfunction in theDBstruct: use this to execute a SQL statement that returnsrows, such as a query or a DML statement with aTHEN RETURNclause. - The
Rowsstruct: use this to access thedata returned by a SQL statement.
The following example uses theQueryContext function:
GoogleSQL
import("context""database/sql""fmt""io"_"github.com/googleapis/go-sql-spanner")funcQueryData(ctxcontext.Context,wio.Writer,databaseNamestring)error{db,err:=sql.Open("spanner",databaseName)iferr!=nil{returnerr}deferdb.Close()rows,err:=db.QueryContext(ctx,`SELECTSingerId,AlbumId,AlbumTitleFROMAlbumsORDERBYSingerId,AlbumId`)deferrows.Close()iferr!=nil{returnerr}forrows.Next(){varsingerId,albumIdint64vartitlestringerr=rows.Scan(&singerId, &albumId, &title)iferr!=nil{returnerr}fmt.Fprintf(w,"%v %v %v\n",singerId,albumId,title)}ifrows.Err()!=nil{returnrows.Err()}returnrows.Close()}PostgreSQL
import("context""database/sql""fmt""io"_"github.com/googleapis/go-sql-spanner")funcQueryDataPostgreSQL(ctxcontext.Context,wio.Writer,databaseNamestring)error{db,err:=sql.Open("spanner",databaseName)iferr!=nil{returnerr}deferfunc(){_=db.Close()}()rows,err:=db.QueryContext(ctx,`selectsinger_id,album_id,album_titlefromalbumsorderbysinger_id,album_id`)deferfunc(){_=rows.Close()}()iferr!=nil{returnerr}forrows.Next(){varsingerId,albumIdint64vartitlestringerr=rows.Scan(&singerId, &albumId, &title)iferr!=nil{returnerr}_,_=fmt.Fprintf(w,"%v %v %v\n",singerId,albumId,title)}ifrows.Err()!=nil{returnrows.Err()}returnrows.Close()}Run the example with the following command:
GoogleSQL
gorungetting_started_guide.goqueryprojects/GCLOUD_PROJECT/instances/test-instance/databases/example-dbPostgreSQL
gorungetting_started_guide.goquerypgprojects/GCLOUD_PROJECT/instances/test-instance/databases/example-dbThe result shows:
11TotalJunk12Go,Go,Go21Green22ForeverHoldYourPeace23TerrifiedQuery using a SQL parameter
If your application has a frequently executed query, you can improve its performanceby parameterizing it. The resulting parametric query can be cached and reused, whichreduces compilation costs. For more information, seeUse query parameters to speed up frequently executed queries.
Here is an example of using a parameter in theWHERE clause toquery records containing a specific value forLastName.
The Spanner database/sql driver supports both positional and namedquery parameters. A? in a SQL statement indicates a positional queryparameter. Pass the query parameter values as additional arguments to theQueryContext function. For example:
GoogleSQL
import("context""database/sql""fmt""io"_"github.com/googleapis/go-sql-spanner")funcQueryDataWithParameter(ctxcontext.Context,wio.Writer,databaseNamestring)error{db,err:=sql.Open("spanner",databaseName)iferr!=nil{returnerr}deferdb.Close()rows,err:=db.QueryContext(ctx,`SELECTSingerId,FirstName,LastNameFROMSingersWHERELastName=?`,"Garcia")deferrows.Close()iferr!=nil{returnerr}forrows.Next(){varsingerIdint64varfirstName,lastNamestringerr=rows.Scan(&singerId, &firstName, &lastName)iferr!=nil{returnerr}fmt.Fprintf(w,"%v %v %v\n",singerId,firstName,lastName)}ifrows.Err()!=nil{returnrows.Err()}returnrows.Close()}PostgreSQL
import("context""database/sql""fmt""io"_"github.com/googleapis/go-sql-spanner")funcQueryDataWithParameterPostgreSQL(ctxcontext.Context,wio.Writer,databaseNamestring)error{db,err:=sql.Open("spanner",databaseName)iferr!=nil{returnerr}deferfunc(){_=db.Close()}()rows,err:=db.QueryContext(ctx,`selectsinger_id,first_name,last_namefromsingerswherelast_name=?`,"Garcia")deferfunc(){_=rows.Close()}()iferr!=nil{returnerr}forrows.Next(){varsingerIdint64varfirstName,lastNamestringerr=rows.Scan(&singerId, &firstName, &lastName)iferr!=nil{returnerr}_,_=fmt.Fprintf(w,"%v %v %v\n",singerId,firstName,lastName)}ifrows.Err()!=nil{returnrows.Err()}returnrows.Close()}Run the example with the following command:
GoogleSQL
gorungetting_started_guide.goquerywithparameterprojects/GCLOUD_PROJECT/instances/test-instance/databases/example-dbPostgreSQL
gorungetting_started_guide.goquerywithparameterpgprojects/GCLOUD_PROJECT/instances/test-instance/databases/example-dbThe result shows:
12MelissaGarciaUpdate the database schema
Assume you need to add a new column calledMarketingBudget to theAlbumstable. Adding a new column to an existing table requires an update to yourdatabase schema. Spanner supports schema updates to a database while thedatabase continues to serve traffic. Schema updates don't require taking thedatabase offline and they don't lock entire tables or columns; you can continuewriting data to the database during the schema update. Read more about supportedschema updates and schema change performance inMake schema updates.
Add a column
You can add a column on the command line using the Google Cloud CLI orprogrammatically usingthe Spanner database/sql driver.
On the command line
Use the followingALTER TABLE command toadd the new column to the table:
GoogleSQL
gcloudspannerdatabasesddlupdateexample-db--instance=test-instance \--ddl='ALTER TABLE Albums ADD COLUMN MarketingBudget INT64'PostgreSQL
gcloudspannerdatabasesddlupdateexample-db--instance=test-instance \--ddl='alter table albums add column marketing_budget bigint'You should see:
Schemaupdating...done.Use the Spanner database/sql driver
Use theExecContext function tomodify the schema:GoogleSQL
import("context""database/sql""fmt""io"_"github.com/googleapis/go-sql-spanner")funcAddColumn(ctxcontext.Context,wio.Writer,databaseNamestring)error{db,err:=sql.Open("spanner",databaseName)iferr!=nil{returnerr}deferdb.Close()_,err=db.ExecContext(ctx,`ALTERTABLEAlbumsADDCOLUMNMarketingBudgetINT64`)iferr!=nil{returnerr}fmt.Fprint(w,"Added MarketingBudget column\n")returnnil}PostgreSQL
import("context""database/sql""fmt""io"_"github.com/googleapis/go-sql-spanner")funcAddColumnPostgreSQL(ctxcontext.Context,wio.Writer,databaseNamestring)error{db,err:=sql.Open("spanner",databaseName)iferr!=nil{returnerr}deferfunc(){_=db.Close()}()_,err=db.ExecContext(ctx,`altertablealbumsaddcolumnmarketing_budgetbigint`)iferr!=nil{returnerr}_,_=fmt.Fprint(w,"Added marketing_budget column\n")returnnil}Run the example with the following command:
GoogleSQL
gorungetting_started_guide.goaddcolumnprojects/GCLOUD_PROJECT/instances/test-instance/databases/example-dbPostgreSQL
gorungetting_started_guide.goaddcolumnpgprojects/GCLOUD_PROJECT/instances/test-instance/databases/example-dbThe result shows:
AddedMarketingBudgetcolumn.Execute a DDL batch
We recommend that you execute multiple schema modifications in one batch. UsetheSTART BATCH DDL andRUN BATCH commands to execute a DDL batch. Thefollowing example creates two tables in one batch:
GoogleSQL
import("context""database/sql""fmt""io"_"github.com/googleapis/go-sql-spanner")funcDdlBatch(ctxcontext.Context,wio.Writer,databaseNamestring)error{db,err:=sql.Open("spanner",databaseName)iferr!=nil{returnerr}deferdb.Close()//ExecutingmultipleDDLstatementsasonebatchis//moreefficientthanexecutingeachstatement//individually.conn,err:=db.Conn(ctx)deferconn.Close()if_,err:=conn.ExecContext(ctx,"start batch ddl");err!=nil{returnerr}if_,err:=conn.ExecContext(ctx,`CREATETABLEVenues(VenueIdINT64NOTNULL,NameSTRING(1024),DescriptionJSON,)PRIMARYKEY(VenueId)`);err!=nil{returnerr}if_,err:=conn.ExecContext(ctx,`CREATETABLEConcerts(ConcertIdINT64NOTNULL,VenueIdINT64NOTNULL,SingerIdINT64NOTNULL,StartTimeTIMESTAMP,EndTimeTIMESTAMP,CONSTRAINTFk_Concerts_VenuesFOREIGNKEY(VenueId)REFERENCESVenues(VenueId),CONSTRAINTFk_Concerts_SingersFOREIGNKEY(SingerId)REFERENCESSingers(SingerId),)PRIMARYKEY(ConcertId)`);err!=nil{returnerr}//`runbatch`sendstheDDLstatementstoSpannerandblocksuntil//allstatementshavefinishedexecuting.if_,err:=conn.ExecContext(ctx,"run batch");err!=nil{returnerr}fmt.Fprint(w,"Added Venues and Concerts tables\n")returnnil}PostgreSQL
import("context""database/sql""fmt""io"_"github.com/googleapis/go-sql-spanner")funcDdlBatchPostgreSQL(ctxcontext.Context,wio.Writer,databaseNamestring)error{db,err:=sql.Open("spanner",databaseName)iferr!=nil{returnerr}deferfunc(){_=db.Close()}()//ExecutingmultipleDDLstatementsasonebatchis//moreefficientthanexecutingeachstatement//individually.conn,err:=db.Conn(ctx)deferfunc(){_=conn.Close()}()if_,err:=conn.ExecContext(ctx,"start batch ddl");err!=nil{returnerr}if_,err:=conn.ExecContext(ctx,`createtablevenues(venue_idbigintnotnullprimarykey,namevarchar(1024),descriptionjsonb)`);err!=nil{returnerr}if_,err:=conn.ExecContext(ctx,`createtableconcerts(concert_idbigintnotnullprimarykey,venue_idbigintnotnull,singer_idbigintnotnull,start_timetimestamptz,end_timetimestamptz,constraintfk_concerts_venuesforeignkey(venue_id)referencesvenues(venue_id),constraintfk_concerts_singersforeignkey(singer_id)referencessingers(singer_id))`);err!=nil{returnerr}//`runbatch`sendstheDDLstatementstoSpannerandblocksuntil//allstatementshavefinishedexecuting.if_,err:=conn.ExecContext(ctx,"run batch");err!=nil{returnerr}_,_=fmt.Fprint(w,"Added venues and concerts tables\n")returnnil}Run the example with the following command:
GoogleSQL
gorungetting_started_guide.goddlbatchprojects/GCLOUD_PROJECT/instances/test-instance/databases/example-dbPostgreSQL
gorungetting_started_guide.goddlbatchpgprojects/GCLOUD_PROJECT/instances/test-instance/databases/example-dbThe result shows:
AddedVenuesandConcertstables.Write data to the new column
The following code writes data to the new column. It setsMarketingBudget to100000 for the row keyed byAlbums(1, 1) and to500000 for the row keyedbyAlbums(2, 2).
GoogleSQL
import("context""database/sql""fmt""io""cloud.google.com/go/spanner"spannerdriver"github.com/googleapis/go-sql-spanner")funcUpdateDataWithMutations(ctxcontext.Context,wio.Writer,databaseNamestring)error{db,err:=sql.Open("spanner",databaseName)iferr!=nil{returnerr}deferdb.Close()//GetaconnectionsothatwecangetaccesstotheSpannerspecific//connectioninterfaceSpannerConn.conn,err:=db.Conn(ctx)iferr!=nil{returnerr}deferconn.Close()cols:=[]string{"SingerId","AlbumId","MarketingBudget"}mutations:=[]*spanner.Mutation{spanner.Update("Albums",cols,[]interface{}{1,1,100000}),spanner.Update("Albums",cols,[]interface{}{2,2,500000}),}iferr:=conn.Raw(func(driverConninterface{})error{spannerConn,ok:=driverConn.(spannerdriver.SpannerConn)if!ok{returnfmt.Errorf("unexpected driver connection %v, "+"expected SpannerConn",driverConn)}_,err=spannerConn.Apply(ctx,mutations)returnerr});err!=nil{returnerr}fmt.Fprintf(w,"Updated %v albums\n",len(mutations))returnnil}PostgreSQL
import("context""database/sql""fmt""io""cloud.google.com/go/spanner"spannerdriver"github.com/googleapis/go-sql-spanner")funcUpdateDataWithMutationsPostgreSQL(ctxcontext.Context,wio.Writer,databaseNamestring)error{db,err:=sql.Open("spanner",databaseName)iferr!=nil{returnerr}deferfunc(){_=db.Close()}()//GetaconnectionsothatwecangetaccesstotheSpannerspecific//connectioninterfaceSpannerConn.conn,err:=db.Conn(ctx)iferr!=nil{returnerr}deferfunc(){_=conn.Close()}()cols:=[]string{"singer_id","album_id","marketing_budget"}mutations:=[]*spanner.Mutation{spanner.Update("albums",cols,[]interface{}{1,1,100000}),spanner.Update("albums",cols,[]interface{}{2,2,500000}),}iferr:=conn.Raw(func(driverConninterface{})error{spannerConn,ok:=driverConn.(spannerdriver.SpannerConn)if!ok{returnfmt.Errorf("unexpected driver connection %v, "+"expected SpannerConn",driverConn)}_,err=spannerConn.Apply(ctx,mutations)returnerr});err!=nil{returnerr}_,_=fmt.Fprintf(w,"Updated %v albums\n",len(mutations))returnnil}Run the example with the following command:
GoogleSQL
gorungetting_started_guide.goupdateprojects/GCLOUD_PROJECT/instances/test-instance/databases/example-dbPostgreSQL
gorungetting_started_guide.goupdatepgprojects/GCLOUD_PROJECT/instances/test-instance/databases/example-dbThe result shows:
Updated2albumsYou can also execute a SQL query to fetch the values that you just wrote.
The following example uses theQueryContext function to execute a query:
GoogleSQL
import("context""database/sql""fmt""io"_"github.com/googleapis/go-sql-spanner")funcQueryNewColumn(ctxcontext.Context,wio.Writer,databaseNamestring)error{db,err:=sql.Open("spanner",databaseName)iferr!=nil{returnerr}deferdb.Close()rows,err:=db.QueryContext(ctx,`SELECTSingerId,AlbumId,MarketingBudgetFROMAlbumsORDERBYSingerId,AlbumId`)deferrows.Close()iferr!=nil{returnerr}forrows.Next(){varsingerId,albumIdint64varmarketingBudgetsql.NullInt64err=rows.Scan(&singerId, &albumId, &marketingBudget)iferr!=nil{returnerr}budget:="NULL"ifmarketingBudget.Valid{budget=fmt.Sprintf("%v",marketingBudget.Int64)}fmt.Fprintf(w,"%v %v %v\n",singerId,albumId,budget)}ifrows.Err()!=nil{returnrows.Err()}returnrows.Close()}PostgreSQL
import("context""database/sql""fmt""io"_"github.com/googleapis/go-sql-spanner")funcQueryNewColumnPostgreSQL(ctxcontext.Context,wio.Writer,databaseNamestring)error{db,err:=sql.Open("spanner",databaseName)iferr!=nil{returnerr}deferfunc(){_=db.Close()}()rows,err:=db.QueryContext(ctx,`selectsinger_id,album_id,marketing_budgetfromalbumsorderbysinger_id,album_id`)deferfunc(){_=rows.Close()}()iferr!=nil{returnerr}forrows.Next(){varsingerId,albumIdint64varmarketingBudgetsql.NullInt64err=rows.Scan(&singerId, &albumId, &marketingBudget)iferr!=nil{returnerr}budget:="null"ifmarketingBudget.Valid{budget=fmt.Sprintf("%v",marketingBudget.Int64)}_,_=fmt.Fprintf(w,"%v %v %v\n",singerId,albumId,budget)}ifrows.Err()!=nil{returnrows.Err()}returnrows.Close()}To execute this query, run the following command:
GoogleSQL
gorungetting_started_guide.goquerymarketingbudgetprojects/GCLOUD_PROJECT/instances/test-instance/databases/example-dbPostgreSQL
gorungetting_started_guide.goquerymarketingbudgetpgprojects/GCLOUD_PROJECT/instances/test-instance/databases/example-dbYou should see:
1110000012null21null2250000023nullUpdate data
You can update data using DML in a read-write transaction.
CallDB.BeginTx to execute read-writetransactions in database/sql.
GoogleSQL
import("context""database/sql""fmt""io"_"github.com/googleapis/go-sql-spanner")funcWriteWithTransactionUsingDml(ctxcontext.Context,wio.Writer,databaseNamestring)error{db,err:=sql.Open("spanner",databaseName)iferr!=nil{returnerr}deferdb.Close()//Transfermarketingbudgetfromonealbumtoanother.Wedoitina//transactiontoensurethatthetransferisatomic.tx,err:=db.BeginTx(ctx, &sql.TxOptions{})iferr!=nil{returnerr}//TheSpannerdatabase/sqldriversupportsbothpositionalandnamed//queryparameters.Thisqueryusesnamedqueryparameters.constselectSql="SELECT MarketingBudget "+"FROM Albums "+"WHERE SingerId = @singerId and AlbumId = @albumId"//Getthemarketing_budgetofsinger2/album2.row:=tx.QueryRowContext(ctx,selectSql,sql.Named("singerId",2),sql.Named("albumId",2))varbudget2int64iferr:=row.Scan(&budget2);err!=nil{tx.Rollback()returnerr}consttransfer=20000//Thetransactionwillonlybecommittedifthisconditionstillholds//atthetimeofcommit.Otherwise,thetransactionwillbeaborted.ifbudget2 >=transfer{//Getthemarketing_budgetofsinger1/album1.row:=tx.QueryRowContext(ctx,selectSql,sql.Named("singerId",1),sql.Named("albumId",1))varbudget1int64iferr:=row.Scan(&budget1);err!=nil{tx.Rollback()returnerr}//TransferpartofthemarketingbudgetofAlbum2toAlbum1.budget1+=transferbudget2-=transferconstupdateSql="UPDATE Albums "+"SET MarketingBudget = @budget "+"WHERE SingerId = @singerId and AlbumId = @albumId"//StartaDMLbatchandexecuteitaspartofthecurrenttransaction.if_,err:=tx.ExecContext(ctx,"start batch dml");err!=nil{tx.Rollback()returnerr}if_,err:=tx.ExecContext(ctx,updateSql,sql.Named("singerId",1),sql.Named("albumId",1),sql.Named("budget",budget1));err!=nil{_,_=tx.ExecContext(ctx,"abort batch")tx.Rollback()returnerr}if_,err:=tx.ExecContext(ctx,updateSql,sql.Named("singerId",2),sql.Named("albumId",2),sql.Named("budget",budget2));err!=nil{_,_=tx.ExecContext(ctx,"abort batch")tx.Rollback()returnerr}//`runbatch`sendstheDMLstatementstoSpanner.//Theresultcontainsthetotalaffectedrowsacrosstheentirebatch.result,err:=tx.ExecContext(ctx,"run batch")iferr!=nil{tx.Rollback()returnerr}ifaffected,err:=result.RowsAffected();err!=nil{tx.Rollback()returnerr}elseifaffected!=2{//Thebatchshouldupdate2rows.tx.Rollback()returnfmt.Errorf("unexpected number of rows affected: %v",affected)}}//Committhecurrenttransaction.iferr:=tx.Commit();err!=nil{returnerr}fmt.Fprintln(w,"Transferred marketing budget from Album 2 to Album 1")returnnil}PostgreSQL
import("context""database/sql""fmt""io"_"github.com/googleapis/go-sql-spanner")funcWriteWithTransactionUsingDmlPostgreSQL(ctxcontext.Context,wio.Writer,databaseNamestring)error{db,err:=sql.Open("spanner",databaseName)iferr!=nil{returnerr}deferfunc(){_=db.Close()}()//Transfermarketingbudgetfromonealbumtoanother.Wedoitina//transactiontoensurethatthetransferisatomic.tx,err:=db.BeginTx(ctx, &sql.TxOptions{})iferr!=nil{returnerr}constselectSql="select marketing_budget "+"from albums "+"where singer_id = $1 and album_id = $2"//Getthemarketing_budgetofsinger2/album2.row:=tx.QueryRowContext(ctx,selectSql,2,2)varbudget2int64iferr:=row.Scan(&budget2);err!=nil{_=tx.Rollback()returnerr}consttransfer=20000//Thetransactionwillonlybecommittedifthisconditionstillholds//atthetimeofcommit.Otherwise,thetransactionwillbeaborted.ifbudget2 >=transfer{//Getthemarketing_budgetofsinger1/album1.row:=tx.QueryRowContext(ctx,selectSql,1,1)varbudget1int64iferr:=row.Scan(&budget1);err!=nil{_=tx.Rollback()returnerr}//TransferpartofthemarketingbudgetofAlbum2toAlbum1.budget1+=transferbudget2-=transferconstupdateSql="update albums "+"set marketing_budget = $1 "+"where singer_id = $2 and album_id = $3"//StartaDMLbatchandexecuteitaspartofthecurrenttransaction.if_,err:=tx.ExecContext(ctx,"start batch dml");err!=nil{_=tx.Rollback()returnerr}if_,err:=tx.ExecContext(ctx,updateSql,budget1,1,1);err!=nil{_,_=tx.ExecContext(ctx,"abort batch")_=tx.Rollback()returnerr}if_,err:=tx.ExecContext(ctx,updateSql,budget2,2,2);err!=nil{_,_=tx.ExecContext(ctx,"abort batch")_=tx.Rollback()returnerr}//`runbatch`sendstheDMLstatementstoSpanner.//Theresultcontainsthetotalaffectedrowsacrosstheentirebatch.result,err:=tx.ExecContext(ctx,"run batch")iferr!=nil{_=tx.Rollback()returnerr}ifaffected,err:=result.RowsAffected();err!=nil{_=tx.Rollback()returnerr}elseifaffected!=2{//Thebatchshouldupdate2rows._=tx.Rollback()returnfmt.Errorf("unexpected number of rows affected: %v",affected)}}//Committhecurrenttransaction.iferr:=tx.Commit();err!=nil{returnerr}_,_=fmt.Fprintln(w,"Transferred marketing budget from Album 2 to Album 1")returnnil}Run the example with the following command:
GoogleSQL
gorungetting_started_guide.gowritewithtransactionusingdmlprojects/GCLOUD_PROJECT/instances/test-instance/databases/example-dbPostgreSQL
gorungetting_started_guide.gowritewithtransactionusingdmlpgprojects/GCLOUD_PROJECT/instances/test-instance/databases/example-dbTransaction tags and request tags
Usetransaction tags and request tagsto troubleshoot transactions and queries in Spanner. You can passadditional transaction options to thespannerdriver.BeginReadWriteTransactionfunction.
Usespannerdriver.ExecOptions to pass additional query options for a SQLstatement. For example:
GoogleSQL
import("context""database/sql""fmt""io""cloud.google.com/go/spanner"spannerdriver"github.com/googleapis/go-sql-spanner")funcTags(ctxcontext.Context,wio.Writer,databaseNamestring)error{db,err:=sql.Open("spanner",databaseName)iferr!=nil{returnerr}deferdb.Close()//Usethespannerdriver.BeginReadWriteTransactionfunction//tospecifyspecificSpanneroptions,suchastransactiontags.tx,err:=spannerdriver.BeginReadWriteTransaction(ctx,db,spannerdriver.ReadWriteTransactionOptions{TransactionOptions:spanner.TransactionOptions{TransactionTag:"example-tx-tag",},})iferr!=nil{returnerr}//Passinanargumentoftypespannerdriver.ExecOptionstosupply//additionaloptionsforastatement.row:=tx.QueryRowContext(ctx,"SELECT MarketingBudget "+"FROM Albums "+"WHERE SingerId=? and AlbumId=?",spannerdriver.ExecOptions{QueryOptions:spanner.QueryOptions{RequestTag:"query-marketing-budget"},},1,1)varbudgetint64iferr:=row.Scan(&budget);err!=nil{tx.Rollback()returnerr}//Reducethemarketingbudgetby10%ifitismorethan1,000.ifbudget >1000{budget=int64(float64(budget)-float64(budget)*0.1)if_,err:=tx.ExecContext(ctx,`UPDATEAlbumsSETMarketingBudget=@budgetWHERESingerId=@singerIdANDAlbumId=@albumId`,spannerdriver.ExecOptions{QueryOptions:spanner.QueryOptions{RequestTag:"reduce-marketing-budget"},},sql.Named("budget",budget),sql.Named("singerId",1),sql.Named("albumId",1));err!=nil{tx.Rollback()returnerr}}//Committhecurrenttransaction.iferr:=tx.Commit();err!=nil{returnerr}fmt.Fprintln(w,"Reduced marketing budget")returnnil}PostgreSQL
import("context""database/sql""fmt""io""cloud.google.com/go/spanner"spannerdriver"github.com/googleapis/go-sql-spanner")funcTagsPostgreSQL(ctxcontext.Context,wio.Writer,databaseNamestring)error{db,err:=sql.Open("spanner",databaseName)iferr!=nil{returnerr}deferfunc(){_=db.Close()}()//Usethespannerdriver.BeginReadWriteTransactionfunction//tospecifyspecificSpanneroptions,suchastransactiontags.tx,err:=spannerdriver.BeginReadWriteTransaction(ctx,db,spannerdriver.ReadWriteTransactionOptions{TransactionOptions:spanner.TransactionOptions{TransactionTag:"example-tx-tag",},})iferr!=nil{returnerr}//Passinanargumentoftypespannerdriver.ExecOptionstosupply//additionaloptionsforastatement.row:=tx.QueryRowContext(ctx,"select marketing_budget "+"from albums "+"where singer_id=? and album_id=?",spannerdriver.ExecOptions{QueryOptions:spanner.QueryOptions{RequestTag:"query-marketing-budget"},},1,1)varbudgetint64iferr:=row.Scan(&budget);err!=nil{_=tx.Rollback()returnerr}//Reducethemarketingbudgetby10%ifitismorethan1,000.ifbudget >1000{budget=int64(float64(budget)-float64(budget)*0.1)if_,err:=tx.ExecContext(ctx,`updatealbumssetmarketing_budget=$1wheresinger_id=$2andalbum_id=$3`,spannerdriver.ExecOptions{QueryOptions:spanner.QueryOptions{RequestTag:"reduce-marketing-budget"},},budget,1,1);err!=nil{_=tx.Rollback()returnerr}}//Committhecurrenttransaction.iferr:=tx.Commit();err!=nil{returnerr}_,_=fmt.Fprintln(w,"Reduced marketing budget")returnnil}Run the example with the following command:
GoogleSQL
gorungetting_started_guide.gotagsprojects/GCLOUD_PROJECT/instances/test-instance/databases/example-dbPostgreSQL
gorungetting_started_guide.gotagspgprojects/GCLOUD_PROJECT/instances/test-instance/databases/example-dbRetrieve data using read-only transactions
Suppose you want to execute more than one read at the same timestamp.Read-onlytransactions observe a consistentprefix of the transaction commit history, so your application always getsconsistent data.Set theTxOptions.ReadOnly field totrue to execute a read-only transaction.
The following shows how to run a query and perform a read in the same read-onlytransaction:
GoogleSQL
import("context""database/sql""fmt""io"_"github.com/googleapis/go-sql-spanner")funcReadOnlyTransaction(ctxcontext.Context,wio.Writer,databaseNamestring)error{db,err:=sql.Open("spanner",databaseName)iferr!=nil{returnerr}deferdb.Close()//Startaread-onlytransactionbysupplyingadditionaltransactionoptions.tx,err:=db.BeginTx(ctx, &sql.TxOptions{ReadOnly:true})albumsOrderedById,err:=tx.QueryContext(ctx,`SELECTSingerId,AlbumId,AlbumTitleFROMAlbumsORDERBYSingerId,AlbumId`)deferalbumsOrderedById.Close()iferr!=nil{returnerr}foralbumsOrderedById.Next(){varsingerId,albumIdint64vartitlestringerr=albumsOrderedById.Scan(&singerId, &albumId, &title)iferr!=nil{returnerr}fmt.Fprintf(w,"%v %v %v\n",singerId,albumId,title)}albumsOrderedTitle,err:=tx.QueryContext(ctx,`SELECTSingerId,AlbumId,AlbumTitleFROMAlbumsORDERBYAlbumTitle`)deferalbumsOrderedTitle.Close()iferr!=nil{returnerr}foralbumsOrderedTitle.Next(){varsingerId,albumIdint64vartitlestringerr=albumsOrderedTitle.Scan(&singerId, &albumId, &title)iferr!=nil{returnerr}fmt.Fprintf(w,"%v %v %v\n",singerId,albumId,title)}//Endtheread-onlytransactionbycallingCommit.returntx.Commit()}PostgreSQL
import("context""database/sql""fmt""io"_"github.com/googleapis/go-sql-spanner")funcReadOnlyTransactionPostgreSQL(ctxcontext.Context,wio.Writer,databaseNamestring)error{db,err:=sql.Open("spanner",databaseName)iferr!=nil{returnerr}deferfunc(){_=db.Close()}()//Startaread-onlytransactionbysupplyingadditionaltransactionoptions.tx,err:=db.BeginTx(ctx, &sql.TxOptions{ReadOnly:true})iferr!=nil{returnerr}albumsOrderedById,err:=tx.QueryContext(ctx,`selectsinger_id,album_id,album_titlefromalbumsorderbysinger_id,album_id`)deferfunc(){_=albumsOrderedById.Close()}()iferr!=nil{returnerr}foralbumsOrderedById.Next(){varsingerId,albumIdint64vartitlestringerr=albumsOrderedById.Scan(&singerId, &albumId, &title)iferr!=nil{returnerr}_,_=fmt.Fprintf(w,"%v %v %v\n",singerId,albumId,title)}albumsOrderedTitle,err:=tx.QueryContext(ctx,`selectsinger_id,album_id,album_titlefromalbumsorderbyalbum_title`)deferfunc(){_=albumsOrderedTitle.Close()}()iferr!=nil{returnerr}foralbumsOrderedTitle.Next(){varsingerId,albumIdint64vartitlestringerr=albumsOrderedTitle.Scan(&singerId, &albumId, &title)iferr!=nil{returnerr}_,_=fmt.Fprintf(w,"%v %v %v\n",singerId,albumId,title)}//Endtheread-onlytransactionbycallingCommit.returntx.Commit()}Run the example with the following command:
GoogleSQL
gorungetting_started_guide.goreadonlytransactionprojects/GCLOUD_PROJECT/instances/test-instance/databases/example-dbPostgreSQL
gorungetting_started_guide.goreadonlytransactionpgprojects/GCLOUD_PROJECT/instances/test-instance/databases/example-dbThe result shows:
11TotalJunk12Go,Go,Go21Green22ForeverHoldYourPeace23Terrified22ForeverHoldYourPeace12Go,Go,Go21Green23Terrified11TotalJunkPartitioned queries and Data Boost
ThepartitionQueryAPI divides a query into smaller pieces, or partitions, and uses multiplemachines to fetch the partitions in parallel. Each partition is identified by apartition token. The partitionQuery API has higher latency than the standardquery API,because it's only intended for bulk operations such as exporting or scanning thewhole database.
Data Boostlets you execute analytics queries and data exports with near-zeroimpact to existing workloads on the provisioned Spanner instance.Data Boost only supportspartitioned queries.
The following example shows how to execute a partitioned query with Data Boostwith the database/sql driver:
GoogleSQL
import("context""database/sql""fmt""io""slices""cloud.google.com/go/spanner"spannerdriver"github.com/googleapis/go-sql-spanner")funcDataBoost(ctxcontext.Context,wio.Writer,databaseNamestring)error{db,err:=sql.Open("spanner",databaseName)iferr!=nil{returnerr}deferdb.Close()//RunapartitionedquerythatusesDataBoost.rows,err:=db.QueryContext(ctx,"SELECT SingerId, FirstName, LastName from Singers",spannerdriver.ExecOptions{PartitionedQueryOptions:spannerdriver.PartitionedQueryOptions{//AutoPartitionQueryinstructstheSpannerdatabase/sqldriverto//automaticallypartitionthequeryandexecuteeachpartitioninparallel.//Therowsarereturnedasoneresultsetinundefinedorder.AutoPartitionQuery:true,},QueryOptions:spanner.QueryOptions{//SetDataBoostEnabledtotruetoenableDataBoost.//Seehttps://cloud.google.com/spanner/docs/databoost/databoost-overview//formoreinformation.DataBoostEnabled:true,},})deferrows.Close()iferr!=nil{returnerr}typeSingerstruct{SingerIdint64FirstNamestringLastNamestring}varsingers[]Singerforrows.Next(){varsingerSingererr=rows.Scan(&singer.SingerId, &singer.FirstName, &singer.LastName)iferr!=nil{returnerr}singers=append(singers,singer)}//QueriesthatusetheAutoPartitionoptionreturnrowsinundefinedorder,//soweneedtosorttheminmemorytoguaranteetheoutputorder.slices.SortFunc(singers,func(a,bSinger)int{returnint(a.SingerId-b.SingerId)})for_,s:=rangesingers{fmt.Fprintf(w,"%v %v %v\n",s.SingerId,s.FirstName,s.LastName)}returnnil}PostgreSQL
import("context""database/sql""fmt""io""slices""cloud.google.com/go/spanner"spannerdriver"github.com/googleapis/go-sql-spanner")funcDataBoostPostgreSQL(ctxcontext.Context,wio.Writer,databaseNamestring)error{db,err:=sql.Open("spanner",databaseName)iferr!=nil{returnerr}deferfunc(){_=db.Close()}()//RunapartitionedquerythatusesDataBoost.rows,err:=db.QueryContext(ctx,"select singer_id, first_name, last_name from singers",spannerdriver.ExecOptions{PartitionedQueryOptions:spannerdriver.PartitionedQueryOptions{//AutoPartitionQueryinstructstheSpannerdatabase/sqldriverto//automaticallypartitionthequeryandexecuteeachpartitioninparallel.//Therowsarereturnedasoneresultsetinundefinedorder.AutoPartitionQuery:true,},QueryOptions:spanner.QueryOptions{//SetDataBoostEnabledtotruetoenableDataBoost.//Seehttps://cloud.google.com/spanner/docs/databoost/databoost-overview//formoreinformation.DataBoostEnabled:true,},})deferfunc(){_=rows.Close()}()iferr!=nil{returnerr}typeSingerstruct{SingerIdint64FirstNamestringLastNamestring}varsingers[]Singerforrows.Next(){varsingerSingererr=rows.Scan(&singer.SingerId, &singer.FirstName, &singer.LastName)iferr!=nil{returnerr}singers=append(singers,singer)}//QueriesthatusetheAutoPartitionoptionreturnrowsinundefinedorder,//soweneedtosorttheminmemorytoguaranteetheoutputorder.slices.SortFunc(singers,func(a,bSinger)int{returnint(a.SingerId-b.SingerId)})for_,s:=rangesingers{_,_=fmt.Fprintf(w,"%v %v %v\n",s.SingerId,s.FirstName,s.LastName)}returnnil}Run the example with the following command:
GoogleSQL
gorungetting_started_guide.godataboostprojects/GCLOUD_PROJECT/instances/test-instance/databases/example-dbPostgreSQL
gorungetting_started_guide.godataboostpgprojects/GCLOUD_PROJECT/instances/test-instance/databases/example-dbPartitioned DML
Partitioned Data Manipulation Language (DML) isdesigned for the following types of bulk updates and deletes:
- Periodic cleanup and garbage collection.
- Backfilling new columns with default values.
GoogleSQL
import("context""database/sql""fmt""io"_"github.com/googleapis/go-sql-spanner")funcPartitionedDml(ctxcontext.Context,wio.Writer,databaseNamestring)error{db,err:=sql.Open("spanner",databaseName)iferr!=nil{returnerr}deferdb.Close()conn,err:=db.Conn(ctx)iferr!=nil{returnerr}//EnablePartitionedDMLonthisconnection.if_,err:=conn.ExecContext(ctx,"SET AUTOCOMMIT_DML_MODE='PARTITIONED_NON_ATOMIC'");err!=nil{returnfmt.Errorf("failed to change DML mode to Partitioned_Non_Atomic: %v",err)}//Back-filladefaultvaluefortheMarketingBudgetcolumn.res,err:=conn.ExecContext(ctx,"UPDATE Albums SET MarketingBudget=0 WHERE MarketingBudget IS NULL")iferr!=nil{returnerr}affected,err:=res.RowsAffected()iferr!=nil{returnfmt.Errorf("failed to get affected rows: %v",err)}//PartitionedDMLreturnstheminimumnumberofrecordsthatwereaffected.fmt.Fprintf(w,"Updated at least %v albums\n",affected)//Closingtheconnectionwillreturnittotheconnectionpool.TheDMLmodewillautomaticallyberesettothe//defaultTRANSACTIONALmodewhentheconnectionisreturnedtothepool,sowedonotneedtochangeitback//manually._=conn.Close()returnnil}PostgreSQL
import("context""database/sql""fmt""io"_"github.com/googleapis/go-sql-spanner")funcPartitionedDmlPostgreSQL(ctxcontext.Context,wio.Writer,databaseNamestring)error{db,err:=sql.Open("spanner",databaseName)iferr!=nil{returnerr}deferfunc(){_=db.Close()}()conn,err:=db.Conn(ctx)iferr!=nil{returnerr}//EnablePartitionedDMLonthisconnection.if_,err:=conn.ExecContext(ctx,"set autocommit_dml_mode='partitioned_non_atomic'");err!=nil{returnfmt.Errorf("failed to change DML mode to Partitioned_Non_Atomic: %v",err)}//Back-filladefaultvalueforthemarketing_budgetcolumn.res,err:=conn.ExecContext(ctx,"update albums set marketing_budget=0 where marketing_budget is null")iferr!=nil{returnerr}affected,err:=res.RowsAffected()iferr!=nil{returnfmt.Errorf("failed to get affected rows: %v",err)}//PartitionedDMLreturnstheminimumnumberofrecordsthatwereaffected._,_=fmt.Fprintf(w,"Updated at least %v albums\n",affected)//Closingtheconnectionwillreturnittotheconnectionpool.TheDMLmodewillautomaticallyberesettothe//defaultTRANSACTIONALmodewhentheconnectionisreturnedtothepool,sowedonotneedtochangeitback//manually._=conn.Close()returnnil}Run the example with the following command:
GoogleSQL
gorungetting_started_guide.gopdmlprojects/GCLOUD_PROJECT/instances/test-instance/databases/example-dbPostgreSQL
gorungetting_started_guide.gopdmlpgprojects/GCLOUD_PROJECT/instances/test-instance/databases/example-dbCleanup
To avoid incurring additional charges to your Cloud Billing account for theresources used in this tutorial, drop the database and delete the instance thatyou created.
Delete the database
If you delete an instance, all databases within it are automatically deleted.This step shows how to delete a database without deleting an instance (you wouldstill incur charges for the instance).
On the command line
gcloudspannerdatabasesdeleteexample-db--instance=test-instanceUsing the Google Cloud console
Go to theSpanner Instances page in the Google Cloud console.
Click the instance.
Click the database that you want to delete.
In theDatabase details page, clickDelete.
Confirm that you want to delete the database and clickDelete.
Delete the instance
Deleting an instance automatically drops all databases created in that instance.
On the command line
gcloudspannerinstancesdeletetest-instanceUsing the Google Cloud console
Go to theSpanner Instances page in the Google Cloud console.
Click your instance.
ClickDelete.
Confirm that you want to delete the instance and clickDelete.
What's next
Learn how toaccess Spanner with a virtual machine instance.
Learn about authorization and authentication credentials inAuthenticate toCloud services using client libraries.
Learn more about SpannerSchema design best practices.
Except as otherwise noted, the content of this page is licensed under theCreative Commons Attribution 4.0 License, and code samples are licensed under theApache 2.0 License. For details, see theGoogle Developers Site Policies. Java is a registered trademark of Oracle and/or its affiliates.
Last updated 2025-12-17 UTC.