Commit timestamps in GoogleSQL-dialect databases

This topic describes how to write a commit timestamp for each insert and updateoperation that you perform with Spanner. To use this feature,set theallow_commit_timestamp option on aTIMESTAMP column, then writethe timestamp as part of each transaction.

Overview

The commit timestamp, based onTrueTimetechnology, is the time when atransaction is committed in the database. Theallow_commit_timestamp columnoption allows you to atomically store the commit timestamp into a column.Using the commit timestamps stored in tables, you can determine the exactordering of mutations and build features like changelogs.

To insert commit timestamps in your database, complete the following steps:

  1. Create a column with typeTIMESTAMP with the columnoptionallow_commit_timestamp set totrue in the schema definition. Forexample:

    CREATETABLEPerformances(...LastUpdateTimeTIMESTAMPNOTNULLOPTIONS(allow_commit_timestamp=true)...)PRIMARYKEY(...);
  2. If you are performing inserts or updates with DML,use thePENDING_COMMIT_TIMESTAMP function to write the committimestamp.

    If you are performing inserts or updates with mutations,use the placeholder stringspanner.commit_timestamp()on insertions or updates to your committimestamp column. You can also use thecommit timestamp constant provided by the client library. For example, thisconstant in the Java client isValue.COMMIT_TIMESTAMP.

When Spanner commits the transaction using these placeholders ascolumn values, the actual commit timestamp is written to the specified column(For example: theLastUpdateTime column). You could then use this column valueto create a history of updates to the table.

Commit timestamp values are not guaranteed to be unique. Transactions thatwrite to non-overlapping sets of fields might have the same timestamp.Transactions that write to overlapping sets of fields have unique timestamps.

Spanner commit timestamps have microsecond granularity,and they are converted to nanoseconds when stored inTIMESTAMP columns.

Create and delete a commit timestamp column

Use theallow_commit_timestamp column option to add and remove support forcommit timestamps:

  • Whencreating a new tableto specify that a column supports commit timestamps.
  • Whenaltering an existing table:
    • to add a new column supporting commit timestamps,
    • to alter an existingTIMESTAMP column to support commit timestamps,
    • to alter an existingTIMESTAMP column to remove commit timestampsupport

Keys and indexes

You can use a commit timestamp column as a primary key column or as a non-keycolumn. Primary keys can be defined asASC orDESC.

  • ASC (default) - Ascending keys are ideal for answering queries from aspecific time forward.
  • DESC - Descending keys keep the latest rows at the top of the table.They provide quick access to the latest records.

Theallow_commit_timestamp option must be consistent across the primarykeys of parent and child tables. If the option is not consistent acrossprimary keys, Spanner returns an error. The only time the optioncan be inconsistent is when you are creating or updating the schema.

Using commit timestamps under the following scenarios createshotspots whichreduce data performance:

  • Commit timestamp column as the first part of the primary key of a table:

    CREATETABLEUsers(LastAccessTIMESTAMPNOTNULL,UserIdINT64NOTNULL,...)PRIMARYKEY(LastAccess,UserId);
  • The first part of the primary key of a secondary index:

    CREATEINDEXUsersByLastAccessONUsers(LastAccess)

    or

    CREATEINDEXUsersByLastAccessAndNameONUsers(LastAccess,FirstName)

Hotspots reducedata performance, even with low write rates. There is no performance overheadif commit timestamps are enabled on non-key columns that are not indexed.

Create a commit timestamp column

The following DDL creates a table with a column that supports committimestamps.

CREATETABLEPerformances(SingerIdINT64NOTNULL,VenueIdINT64NOTNULL,EventDateDate,RevenueINT64,LastUpdateTimeTIMESTAMPNOTNULLOPTIONS(allow_commit_timestamp=true))PRIMARYKEY(SingerId,VenueId,EventDate),INTERLEAVEINPARENTSingersONDELETECASCADE

Adding the option changes the timestamp column as follows:

  • You can use thespanner.commit_timestamp() placeholder string (or a constantprovided by the client library) for inserts and updates.
  • The column can only contain values in the past. For more information, seeProviding your own value for the timestamp.

The optionallow_commit_timestamp is case sensitive.

Add a commit timestamp column to an existing table

To add a commit timestamp column to an existing table, use theALTER TABLEstatement. For example to add aLastUpdateTime column to thePerformancestable, use the following statement:

ALTERTABLEPerformancesADDCOLUMNLastUpdateTimeTIMESTAMPNOTNULLOPTIONS(allow_commit_timestamp=true)

Convert a timestamp column to a commit timestamp column

You can convert an existing timestamp column into a commit timestamp column,but doing so requires Spanner to validate that the existingtimestamp values are in the past. For example:

ALTERTABLEPerformancesALTERCOLUMNLastUpdateTimeSETOPTIONS(allow_commit_timestamp=true)

You cannot change the data type orNULL annotation of a column in anALTER TABLE statement that includesSET OPTIONS. For details, seeData Definition Language.

Remove the commit timestamp option

If you want to remove commit timestamp support from a column, use the optionallow_commit_timestamp=null in anALTER TABLEstatement. The commit timestamp behavior is removed, but the column is stilla timestamp. Changing the option does not alter any other characteristics of thecolumn, such as type or nullability (NOT NULL). For example:

ALTERTABLEPerformancesALTERCOLUMNLastUpdateTimeSETOPTIONS(allow_commit_timestamp=null)

Write a commit timestamp using a DML statement

You use thePENDING_COMMIT_TIMESTAMP function to write the committimestamp in a DML statement. Spanner selects the commit timestamp when the transactioncommits.Note: After you call thePENDING_COMMIT_TIMESTAMP function,the table and any derived index is unreadable to any future SQL statements in the transaction.Because of this, the change stream can't extract the previous value for thecolumn that has a pending commit timestamp, if the coloumn is modified againlater in the same transaction. You must write commit timestamps as the laststatement in a transaction to prevent the possibility of trying to read thetable. If you try to read the table, then Spanner produces an error.

The following DML statement updates theLastUpdateTime column in thePerformances table with the commit timestamp:

UPDATEPerformancesSETLastUpdateTime=PENDING_COMMIT_TIMESTAMP()WHERESingerId=1ANDVenueId=2ANDEventDate="2015-10-21"

The following code example uses thePENDING_COMMIT_TIMESTAMPfunction to write the commit timestamp in theLastUpdateTime column.

C++

voidDmlStandardUpdateWithTimestamp(google::cloud::spanner::Clientclient){using::google::cloud::StatusOr;namespacespanner=::google::cloud::spanner;autocommit_result=client.Commit([&client](spanner::Transactiontxn)->StatusOr<spanner::Mutations>{autoupdate=client.ExecuteDml(std::move(txn),spanner::SqlStatement("UPDATE Albums SET LastUpdateTime = PENDING_COMMIT_TIMESTAMP()""  WHERE SingerId = 1"));if(!update)returnstd::move(update).status();returnspanner::Mutations{};});if(!commit_result)throwstd::move(commit_result).status();std::cout <<"Update was successful "            <<"[spanner_dml_standard_update_with_timestamp]\n";}

C#

usingGoogle.Cloud.Spanner.Data;usingSystem;usingSystem.Threading.Tasks;publicclassUpdateUsingDmlWithTimestampCoreAsyncSample{publicasyncTask<int>UpdateUsingDmlWithTimestampCoreAsync(stringprojectId,stringinstanceId,stringdatabaseId){stringconnectionString=$"Data Source=projects/{projectId}/instances/{instanceId}/databases/{databaseId}";usingvarconnection=newSpannerConnection(connectionString);awaitconnection.OpenAsync();usingvarcmd=connection.CreateDmlCommand("UPDATE Albums SET LastUpdateTime = PENDING_COMMIT_TIMESTAMP() WHERE SingerId = 1");introwCount=awaitcmd.ExecuteNonQueryAsync();Console.WriteLine($"{rowCount} row(s) updated...");returnrowCount;}}

Go

import("context""fmt""io""cloud.google.com/go/spanner")funcupdateUsingDMLWithTimestamp(wio.Writer,dbstring)error{ctx:=context.Background()client,err:=spanner.NewClient(ctx,db)iferr!=nil{returnerr}deferclient.Close()_,err=client.ReadWriteTransaction(ctx,func(ctxcontext.Context,txn*spanner.ReadWriteTransaction)error{stmt:=spanner.Statement{SQL:`UPDATE AlbumsSET LastUpdateTime = PENDING_COMMIT_TIMESTAMP()WHERE SingerId = 1`,}rowCount,err:=txn.Update(ctx,stmt)iferr!=nil{returnerr}fmt.Fprintf(w,"%d record(s) updated.\n",rowCount)returnnil})returnerr}

Java

staticvoidupdateUsingDmlWithTimestamp(DatabaseClientdbClient){dbClient.readWriteTransaction().run(transaction->{Stringsql="UPDATE Albums "+"SET LastUpdateTime = PENDING_COMMIT_TIMESTAMP() WHERE SingerId = 1";longrowCount=transaction.executeUpdate(Statement.of(sql));System.out.printf("%d records updated.\n",rowCount);returnnull;});}

Node.js

// Imports the Google Cloud client libraryconst{Spanner}=require('@google-cloud/spanner');/** * TODO(developer): Uncomment the following lines before running the sample. */// const projectId = 'my-project-id';// const instanceId = 'my-instance';// const databaseId = 'my-database';// Creates a clientconstspanner=newSpanner({projectId:projectId,});// Gets a reference to a Cloud Spanner instance and databaseconstinstance=spanner.instance(instanceId);constdatabase=instance.database(databaseId);database.runTransaction(async(err,transaction)=>{if(err){console.error(err);return;}try{const[rowCount]=awaittransaction.runUpdate({sql:`UPDATE Albums        SET LastUpdateTime = PENDING_COMMIT_TIMESTAMP()        WHERE SingerId = 1`,});console.log(`Successfully updated${rowCount} records.`);awaittransaction.commit();}catch(err){console.error('ERROR:',err);}finally{// Close the database when finished.database.close();}});

PHP

use Google\Cloud\Spanner\SpannerClient;use Google\Cloud\Spanner\Transaction;/** * Update data with a DML statement using timestamps. * * The database and table must already exist and can be created using * `create_database`. * Example: * ``` * insert_data($instanceId, $databaseId); * ``` * * @param string $instanceId The Spanner instance ID. * @param string $databaseId The Spanner database ID. */function update_data_with_dml_timestamp(string $instanceId, string $databaseId): void{    $spanner = new SpannerClient();    $instance = $spanner->instance($instanceId);    $database = $instance->database($databaseId);    $database->runTransaction(function (Transaction $t) {        $rowCount = $t->executeUpdate(            'UPDATE Albums '            . 'SET LastUpdateTime = PENDING_COMMIT_TIMESTAMP() WHERE SingerId = 1');        $t->commit();        printf('Updated %d row(s).' . PHP_EOL, $rowCount);    });}

Python

# instance_id = "your-spanner-instance"# database_id = "your-spanner-db-id"spanner_client=spanner.Client()instance=spanner_client.instance(instance_id)database=instance.database(database_id)defupdate_albums(transaction):row_ct=transaction.execute_update("UPDATE Albums ""SET LastUpdateTime = PENDING_COMMIT_TIMESTAMP() ""WHERE SingerId = 1")print("{} record(s) updated.".format(row_ct))database.run_in_transaction(update_albums)

Ruby

# project_id  = "Your Google Cloud project ID"# instance_id = "Your Spanner instance ID"# database_id = "Your Spanner database ID"require"google/cloud/spanner"spanner=Google::Cloud::Spanner.newproject:project_idclient=spanner.clientinstance_id,database_idrow_count=0client.transactiondo|transaction|row_count=transaction.execute_update("UPDATE Albums SET LastUpdateTime = PENDING_COMMIT_TIMESTAMP() WHERE SingerId = 1")endputs"#{row_count} records updated."

Ruby

# project_id  = "Your Google Cloud project ID"# instance_id = "Your Spanner instance ID"# database_id = "Your Spanner database ID"require"google/cloud/spanner"spanner=Google::Cloud::Spanner.newproject:project_idclient=spanner.clientinstance_id,database_idcommit_timestamp=client.commit_timestampclient.commitdo|c|c.update"Albums",[{SingerId:1,AlbumId:1,MarketingBudget:100_000,LastUpdateTime:commit_timestamp},{SingerId:2,AlbumId:2,MarketingBudget:750_000,LastUpdateTime:commit_timestamp}]endputs"Updated data"

Commit timestamps can only be written to columns annotated with theallow_commit_timestamp=true option.

If you have mutations on rows in multiple tables, you must specifyspanner.commit_timestamp() (or the client libraryconstant) for the commit timestamp column in each table.

Query a commit timestamp column

The following example queries the commit timestamp column of the table.

C++

voidQueryDataWithTimestamp(google::cloud::spanner::Clientclient){namespacespanner=::google::cloud::spanner;spanner::SqlStatementselect("SELECT SingerId, AlbumId, MarketingBudget, LastUpdateTime""  FROM Albums"" ORDER BY LastUpdateTime DESC");usingRowType=std::tuple<std::int64_t,std::int64_t,absl::optional<std::int64_t>,absl::optional<spanner::Timestamp>>;autorows=client.ExecuteQuery(std::move(select));for(auto&row:spanner::StreamOf<RowType>(rows)){if(!row)throwstd::move(row).status();std::cout <<std::get<0>(*row) <<" " <<std::get<1>(*row);automarketing_budget=std::get<2>(*row);if(!marketing_budget){std::cout <<" NULL";}else{std::cout <<' ' <<*marketing_budget;}autolast_update_time=std::get<3>(*row);if(!last_update_time){std::cout <<" NULL";}else{std::cout <<' ' <<*last_update_time;}std::cout <<"\n";}}

C#

usingGoogle.Cloud.Spanner.Data;usingSystem;usingSystem.Collections.Generic;usingSystem.Threading.Tasks;publicclassQueryDataWithTimestampColumnAsyncSample{publicclassAlbum{publicintSingerId{get;set;}publicintAlbumId{get;set;}publicDateTime?LastUpdateTime{get;set;}publiclong?MarketingBudget{get;set;}}publicasyncTask<List<Album>>QueryDataWithTimestampColumnAsync(stringprojectId,stringinstanceId,stringdatabaseId){stringconnectionString=$"Data Source=projects/{projectId}/instances/{instanceId}/databases/{databaseId}";usingvarconnection=newSpannerConnection(connectionString);usingvarcmd=connection.CreateSelectCommand("SELECT SingerId, AlbumId, MarketingBudget, LastUpdateTime FROM Albums ORDER BY LastUpdateTime DESC");varalbums=newList<Album>();usingvarreader=awaitcmd.ExecuteReaderAsync();while(awaitreader.ReadAsync()){albums.Add(newAlbum{SingerId=reader.GetFieldValue<int>("SingerId"),AlbumId=reader.GetFieldValue<int>("AlbumId"),LastUpdateTime=reader.IsDBNull(reader.GetOrdinal("LastUpdateTime"))?(DateTime?)null:reader.GetFieldValue<DateTime>("LastUpdateTime"),MarketingBudget=reader.IsDBNull(reader.GetOrdinal("MarketingBudget"))?0:reader.GetFieldValue<long>("MarketingBudget")});}returnalbums;}}

Go

import("context""fmt""io""strconv""cloud.google.com/go/spanner""google.golang.org/api/iterator")funcqueryWithTimestamp(wio.Writer,dbstring)error{ctx:=context.Background()client,err:=spanner.NewClient(ctx,db)iferr!=nil{returnerr}deferclient.Close()stmt:=spanner.Statement{SQL:`SELECT SingerId, AlbumId, MarketingBudget, LastUpdateTimeFROM Albums ORDER BY LastUpdateTime DESC`}iter:=client.Single().Query(ctx,stmt)deferiter.Stop()for{row,err:=iter.Next()iferr==iterator.Done{returnnil}iferr!=nil{returnerr}varsingerID,albumIDint64varmarketingBudgetspanner.NullInt64varlastUpdateTimespanner.NullTimeiferr:=row.ColumnByName("SingerId",&singerID);err!=nil{returnerr}iferr:=row.ColumnByName("AlbumId",&albumID);err!=nil{returnerr}iferr:=row.ColumnByName("MarketingBudget",&marketingBudget);err!=nil{returnerr}budget:="NULL"ifmarketingBudget.Valid{budget=strconv.FormatInt(marketingBudget.Int64,10)}iferr:=row.ColumnByName("LastUpdateTime",&lastUpdateTime);err!=nil{returnerr}timestamp:="NULL"iflastUpdateTime.Valid{timestamp=lastUpdateTime.String()}fmt.Fprintf(w,"%d %d %s %s\n",singerID,albumID,budget,timestamp)}}

Java

staticvoidqueryMarketingBudgetWithTimestamp(DatabaseClientdbClient){// Rows without an explicit value for MarketingBudget will have a MarketingBudget equal to// null. A try-with-resource block is used to automatically release resources held by// ResultSet.try(ResultSetresultSet=dbClient.singleUse().executeQuery(Statement.of("SELECT SingerId, AlbumId, MarketingBudget, LastUpdateTime FROM Albums"+" ORDER BY LastUpdateTime DESC"))){while(resultSet.next()){System.out.printf("%d %d %s %s\n",resultSet.getLong("SingerId"),resultSet.getLong("AlbumId"),// We check that the value is non null. ResultSet getters can only be used to retrieve// non null values.resultSet.isNull("MarketingBudget")?"NULL":resultSet.getLong("MarketingBudget"),resultSet.isNull("LastUpdateTime")?"NULL":resultSet.getTimestamp("LastUpdateTime"));}}}

Node.js

// ...// Imports the Google Cloud client libraryconst{Spanner}=require('@google-cloud/spanner');/** * TODO(developer): Uncomment the following lines before running the sample. */// const projectId = 'my-project-id';// const instanceId = 'my-instance';// const databaseId = 'my-database';// Creates a clientconstspanner=newSpanner({projectId:projectId,});// Gets a reference to a Cloud Spanner instance and databaseconstinstance=spanner.instance(instanceId);constdatabase=instance.database(databaseId);constquery={sql:`SELECT SingerId, AlbumId, MarketingBudget, LastUpdateTime          FROM Albums ORDER BY LastUpdateTime DESC`,};// Queries rows from the Albums tabletry{const[rows]=awaitdatabase.run(query);rows.forEach(row=>{constjson=row.toJSON();console.log(`SingerId:${json.SingerId}, AlbumId:${json.AlbumId}, MarketingBudget:${json.MarketingBudget?json.MarketingBudget:null}, LastUpdateTime:${json.LastUpdateTime}`,);});}catch(err){console.error('ERROR:',err);}finally{// Close the database when finisheddatabase.close();}

PHP

use Google\Cloud\Spanner\SpannerClient;/** * Queries sample data from a database with a commit timestamp column. * * This sample uses the `MarketingBudget` column. You can add the column * by running the `add_column` sample or by running this DDL statement against * your database: * *      ALTER TABLE Albums ADD COLUMN MarketingBudget INT64 * * This sample also uses the 'LastUpdateTime' commit timestamp column. You can * add the column by running the `add_timestamp_column` sample or by running * this DDL statement against your database: * * ALTER TABLE Albums ADD COLUMN LastUpdateTime TIMESTAMP OPTIONS (allow_commit_timestamp=true) * * Example: * ``` * query_data_with_timestamp_column($instanceId, $databaseId); * ``` * * @param string $instanceId The Spanner instance ID. * @param string $databaseId The Spanner database ID. */function query_data_with_timestamp_column(string $instanceId, string $databaseId): void{    $spanner = new SpannerClient();    $instance = $spanner->instance($instanceId);    $database = $instance->database($databaseId);    $results = $database->execute(        'SELECT SingerId, AlbumId, MarketingBudget, LastUpdateTime ' .        ' FROM Albums ORDER BY LastUpdateTime DESC'    );    foreach ($results as $row) {        if ($row['MarketingBudget'] == null) {            $row['MarketingBudget'] = 'NULL';        }        if ($row['LastUpdateTime'] == null) {            $row['LastUpdateTime'] = 'NULL';        }        printf('SingerId: %s, AlbumId: %s, MarketingBudget: %s, LastUpdateTime: %s' . PHP_EOL,            $row['SingerId'], $row['AlbumId'], $row['MarketingBudget'], $row['LastUpdateTime']);    }}

Python

defquery_data_with_timestamp(instance_id,database_id):"""Queries sample data from the database using SQL.    This updates the `LastUpdateTime` column which must be created before    running this sample. You can add the column by running the    `add_timestamp_column` sample or by running this DDL statement    against your database:        ALTER TABLE Performances ADD COLUMN LastUpdateTime TIMESTAMP        OPTIONS (allow_commit_timestamp=true)    """spanner_client=spanner.Client()instance=spanner_client.instance(instance_id)database=instance.database(database_id)withdatabase.snapshot()assnapshot:results=snapshot.execute_sql("SELECT SingerId, AlbumId, MarketingBudget FROM Albums ""ORDER BY LastUpdateTime DESC")forrowinresults:print("SingerId:{}, AlbumId:{}, MarketingBudget:{}".format(*row))

Ruby

# project_id  = "Your Google Cloud project ID"# instance_id = "Your Spanner instance ID"# database_id = "Your Spanner database ID"require"google/cloud/spanner"spanner=Google::Cloud::Spanner.newproject:project_idclient=spanner.clientinstance_id,database_idclient.execute("SELECT SingerId, AlbumId, MarketingBudget, LastUpdateTime                FROM Albums ORDER BY LastUpdateTime DESC").rows.eachdo|row|puts"#{row[:SingerId]}#{row[:AlbumId]}#{row[:MarketingBudget]}#{row[:LastUpdateTime]}"end

Provide your own value for the commit timestamp column

You can provide your own value for the commit timestamp column, instead ofpassingspanner.commit_timestamp() (or client library constant) as thecolumn value. The value must be a timestamp in the past. This restrictionensures that writing timestamps is an inexpensive and fast operation. The serverreturns aFailedPrecondition error if a future timestamp is specified.

Note: TheCURRENT_TIMESTAMP value is not based on true time. So, the value itreturns is not necessarily in the past and cannot be used to compare with acommit timestamp value.

Create a changelog

Suppose that you want to create a changelog of every mutation that happens to atable and then use that changelog for auditing. An example would be a tablethat stores the history of changes to word processing documents.The commit timestamp makes creating the changelog easier, because thetimestamps can enforce ordering of the changelog entries. You could build achangelog that stores the history of changes to a given document usinga schema like the following example:

CREATETABLEDocuments(UserIdINT64NOTNULL,DocumentIdINT64NOTNULL,ContentsSTRING(MAX)NOTNULL,)PRIMARYKEY(UserId,DocumentId);CREATETABLEDocumentHistory(UserIdINT64NOTNULL,DocumentIdINT64NOTNULL,TsTIMESTAMPNOTNULLOPTIONS(allow_commit_timestamp=true),DeltaSTRING(MAX),)PRIMARYKEY(UserId,DocumentId,Ts),INTERLEAVEINPARENTDocumentsONDELETENOACTION;

To create a changelog, insert a new row inDocumentHistory in the sametransaction in which you insert or update a row inDocument. In the insertionof the new row inDocumentHistory, use the placeholderspanner.commit_timestamp() (or client library constant) to tellSpanner to write the commit timestamp into columnTs. InterleavingtheDocumentsHistory table with theDocuments table will allow for datalocality and more efficient inserts and updates. However, it also adds theconstraint that the parent and child rowsmust be deleted together. To keep the rows inDocumentHistory after rowsinDocuments are deleted, do not interleave the tables.

Optimize recent-data queries with commit timestamps

Commit timestamps enable a Spanner optimization thatcan reduce query I/O when retrieving data written after a particulartime.

To activate this optimization, a query'sWHERE clause must include acomparison between a table's commit timestamp column and a specific timethat you provide, with the following attributes:

  • Provide the specific time as aconstant expression: a literal, aparameter, or a function whose own arguments evaluate to constants.

  • Compare whether the commit timestamp is more recent than thegiven time, through either the> or>= operators.

  • Optionally, add further restrictions to theWHERE clause withAND.Extending the clause withOR disqualifies the query from thisoptimization.

For example, consider the followingPerformances table, which includesa commit timestamp column:

CREATETABLEPerformances(SingerIdINT64NOTNULL,VenueIdINT64NOTNULL,EventDateDATE,RevenueINT64,LastUpdateTimeTIMESTAMPNOTNULLOPTIONS(allow_commit_timestamp=true))PRIMARYKEY(SingerId,VenueId,EventDate);

This query benefits from the commit-timestamp optimization describedearlier, because it has a greater-than-or-equal-to comparison betweenthe table's commit timestamp column and a constant expression—in thiscase, a literal:

SELECT*FROMPerformancesWHERELastUpdateTime>="2022-05-01";

The following query also qualifies for the optimization, since it has agreater-than comparison between the commit timestamp and a functionwhose arguments all evaluate to constants during the query's execution:

SELECT*FROMPerformancesWHERELastUpdateTime >TIMESTAMP_SUB(CURRENT_TIMESTAMP(),INTERVAL30DAY);

What's next

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.