Time series functions Stay organized with collections Save and categorize content based on your preferences.
GoogleSQL for BigQuery supports the following time series functions.
Function list
| Name | Summary |
|---|---|
APPENDS | Returns all rows appended to a table for a given time range. |
CHANGES | Returns all rows that have changed in a table for a given time range. |
DATE_BUCKET | Gets the lower bound of the date bucket that contains a date. |
DATETIME_BUCKET | Gets the lower bound of the datetime bucket that contains a datetime. |
GAP_FILL | Finds and fills gaps in a time series. |
TIMESTAMP_BUCKET | Gets the lower bound of the timestamp bucket that contains a timestamp. |
APPENDS
Preview
This product or feature is subject to the "Pre-GA Offerings Terms" in the General Service Terms section of theService Specific Terms. Pre-GA products and features are available "as is" and might have limited support. For more information, see thelaunch stage descriptions.
Note: To provide feedback or request support for this feature, send an email tobq-change-history-feedback@google.com.APPENDS(TABLEtable,start_timestampDEFAULTNULL,end_timestampDEFAULTNULL)Description
TheAPPENDS function returns all rows appended to a table for a giventime range.
The following operations add rows to theAPPENDS change history:
CREATE TABLEDDL statementINSERTDML statement- Data appended as part of a
MERGEDML statement - Loading data into BigQuery
- Streaming ingestion
Definitions
table: the BigQuery table name. This must be a regularBigQuery table. This argument must be preceded by the wordTABLE.start_timestamp: aTIMESTAMPvalue indicating the earliest time at which achange is included in the output. If the value isNULL, all changes since thetable creation are returned. If the table was created after thestart_timestampvalue, the actual table creation time is used instead. An erroris returned if the time specified is earlier than allowed bytime travel, orif the table was created earlier than allowed by time travel if thestart_timestampvalue isNULL. For standard tables, this window is seven days,but you canconfigure the time travel window to be less thanthat.end_timestamp: aTIMESTAMPvalue indicating the latest time atwhich a change is included in the output.end_timestampis exclusive; forexample, if you specify2023-12-31 08:00:00forstart_timestampand2023-12-31 12:00:00forend_timestamp, all changes made from8 AM December 31, 2023 through 11:59 AM December 31, 2023 are returned.If the
end_timestampvalue isNULL, all changes madeuntil the start of the query are included.
Details
Records of inserted rows persist even if that data is later deleted. Deletionsaren't reflected in theAPPENDS function. If a tableis copied, calling theAPPENDS function on the copied table returns every rowas inserted at the time of table creation. If a row is modified due to anUPDATE operation, there's no effect.
Output
TheAPPENDS function returns a table with the following columns:
- All columns of the input table at the time the query is run. If a column isadded after the
end_timestampvalue, it appears withNULLvalues populated in anyof the rows that were inserted before the addition of the column. _CHANGE_TYPE: aSTRINGvalue indicating the type of change that producedthe row. ForAPPENDS, the only supported value isINSERT._CHANGE_TIMESTAMP: aTIMESTAMPvalue indicating the commit time of thetransaction that made the change.
Limitations
- The data returned by the
APPENDSfunction is limited to the time travelwindow of the table. - The data returned by the
APPENDSfunction is limited to the table's currentschema. - You can't call the
APPENDSfunction within a multi-statement transaction. APPENDSfunction may not capture all rows appended within a multi-statementtransaction if some appended rows are updated or deleted within the sametransaction.- You can only use the
APPENDSfunction with regular BigQuerytables. Clones, snapshots, views, materialized views, external tables, andwildcard tables aren't supported. - Partition pseudo-columns for ingestion-time partitioned tables, such as
_PARTITIONTIMEand_PARTITIONDATE, aren't included in the function'soutput.
Example
This example shows the change history returned by theAPPENDS function as variouschanges are made to a table calledProduce. First, create the table:
CREATETABLEmydataset.Produce(productSTRING,inventoryINT64)AS(SELECT'apples'ASproduct,10ASinventory);Next, insert two rows into the table:
INSERTINTOmydataset.ProduceVALUES('bananas',20),('carrots',30);To view the full change history of appends, useNULL values to get the fullhistory within the time travel window:
SELECTproduct,inventory,_CHANGE_TYPEASchange_type,_CHANGE_TIMESTAMPASchange_timeFROMAPPENDS(TABLEmydataset.Produce,NULL,NULL);The output is similar to the following:
+---------+-----------+-------------+--------------------------------+| product | inventory | change_type | change_time |+---------+-----------+-------------+--------------------------------+| apples | 10 | INSERT | 2022-04-15 20:06:00.488000 UTC || bananas | 20 | INSERT | 2022-04-15 20:06:08.490000 UTC || carrots | 30 | INSERT | 2022-04-15 20:06:08.490000 UTC |+---------+-----------+-------------+--------------------------------+
Next, add a column, insert a new row of values, update the inventory, and deletethebananas row:
ALTERTABLEmydataset.ProduceADDCOLUMNcolorSTRING;INSERTINTOmydataset.ProduceVALUES('grapes',40,'purple');UPDATEmydataset.ProduceSETinventory=inventory+5WHERETRUE;DELETEmydataset.ProduceWHEREproduct='bananas';View the new table:
SELECT*FROMmydataset.Produce;The output is similar to the following:
+---------+-----------+--------+| product | inventory | color |+---------+-----------+--------+| apples | 15 | NULL || carrots | 35 | NULL || grapes | 45 | purple |+---------+-----------+--------+
View the full change history of appends:
SELECTproduct,inventory,color,_CHANGE_TYPEASchange_type,_CHANGE_TIMESTAMPASchange_timeFROMAPPENDS(TABLEmydataset.Produce,NULL,NULL);The output is similar to the following:
+---------+-----------+--------+-------------+--------------------------------+| product | inventory | color | change_type | change_time |+---------+-----------+--------+-------------+--------------------------------+| apples | 10 | NULL | INSERT | 2022-04-15 20:06:00.488000 UTC || bananas | 20 | NULL | INSERT | 2022-04-15 20:06:08.490000 UTC || carrots | 30 | NULL | INSERT | 2022-04-15 20:06:08.490000 UTC || grapes | 40 | purple | INSERT | 2022-04-15 20:07:45.751000 UTC |+---------+-----------+--------+-------------+--------------------------------+
Theinventory column displays the values that were set when the rows wereoriginally inserted into to the table. It doesn't show the changes from theUPDATE statement. The row with information on bananas is still present becausetheAPPENDS function only captures additions to tables, not deletions.
CHANGES
Preview
This product or feature is subject to the "Pre-GA Offerings Terms" in the General Service Terms section of theService Specific Terms. Pre-GA products and features are available "as is" and might have limited support. For more information, see thelaunch stage descriptions.
Note: To provide feedback or request support for this feature, send an email tobq-change-history-feedback@google.com.CHANGES(TABLEtable,start_timestampDEFAULTNULL,end_timestamp)Description
TheCHANGES function returns all rows that have changed in a table for a giventime range. To use theCHANGES function on a table, you must set the table'senable_change_history optiontoTRUE.
The following operations add rows to theCHANGES change history:
CREATE TABLEDDL statementINSERTDML statement- Data appended, changed or deleted as part of a
MERGEDML statement UPDATEDML statementDELETEDML statement- Loading data into BigQuery
- Streaming ingestion
TRUNCATE TABLEDML statement- Jobs configured with a
writeDispositionofWRITE_TRUNCATE - Individualtable partition deletions
Definitions
table: the BigQuery table name. This must be a regularBigQuery table, and must have theenable_change_historyoption set toTRUE. Enabling this table option has animpact on costs; for more information seePricing and costs. Thisargument must be preceded by the wordTABLE.start_timestamp: aTIMESTAMPvalue indicating the earliesttime at which a change is included in the output. If the value isNULL,all changes since the table creation are returned. If you set theenable_change_historyoption after setting thestart_timestampoption,the history before the enablement time might be incomplete. If the table wascreated after thestart_timestampvalue, the actual table creation time isused instead. An error is returned if the time specified is earlier thanallowed bytime travel, or if the table was created earlierthan allowed by time travel if thestart_timestampvalue isNULL. Forstandard tables, this window is seven days, but you canconfigure the timetravel window to be less than that.end_timestamp: aTIMESTAMPvalue indicating the latest time at which achange is included in the output.end_timestampis exclusive; for example,if you specify2023-12-31 08:00:00forstart_timestampand2023-12-3112:00:00forend_timestamp, all changes made from 8 AM December 31, 2023through 11:59 AM December 31, 2023 are returned. The maximum time rangeallowed betweenstart_timestampandend_timestampis one day. For abatch query,end_timestampmust be at least ten minutes prior to thecurrent time. For acontinuous query, set this asNULLto return the latest data.
Details
If a row is inserted, a record of the new row with anINSERT change type isproduced.
If a row is deleted, a record of the deleted row with aDELETE change type isproduced.
If a row is updated, a record of the old row with aDELETE change type and arecord of the new row with anUPDATE change type are produced.
Output
TheCHANGES function returns a table with the following columns:
- All columns of the input table at the time that the query is run. If acolumn is added after the
end_timestampvalue, it appears withNULLvaluespopulated in of the any rows that were changed before the addition ofthe column. _CHANGE_TYPE: aSTRINGvalue indicating the type of change that producedthe row. ForCHANGES, the supported values areINSERT,UPDATE, andDELETE._CHANGE_TIMESTAMP: aTIMESTAMPvalue indicating the commit time of thetransaction that made the change.
Limitations
- The data returned by the
CHANGESfunction is limited to the timetravel window of the table. - The data returned by the
CHANGESfunction is limited to the table's currentschema. - The maximum allowed time range between the
start_timestampandend_timestamparguments you specify for the function is one day. - The
CHANGESfunction can't query the last ten minutes of table history.Therefore, theend_timestampargument value must be at least ten minutesprior to the current time. - You can't call the
CHANGESfunction within a multi-statement transaction. - You can't use the
CHANGESfunction with tables that have had multi-statementtransactions committed to them within the requested time window. - You can only use the
CHANGESfunction with regular BigQuery tables.Views, materialized views, external tables, and wildcard tables aren'tsupported. - For tables that have been cloned or snapshotted, and for tables that arerestored from a clone or snapshot, change history from the source table isn'tcarried over to the new table, clone, or snapshot.
- You can't use the
CHANGESfunction with a table that haschange data capture enabled. - Partition pseudo-columns for ingestion-time partitioned tables, such as
_PARTITIONTIMEand_PARTITIONDATE, aren't included in the function'soutput. - Change history isn't captured for table deletions made due to table partitionexpiration.
- Performingdata manipulation language (DML) statements over recently streamed datafails on tables that have the
enable_change_historyoption set toTRUE.
Example
This example shows the change history returned by theCHANGES function asvarious changes are made to a table calledProduce. First, create the table:
CREATETABLEmydataset.Produce(productSTRING,inventoryINT64)OPTIONS(enable_change_history=true);Insert two rows into the table:
INSERTINTOmydataset.ProduceVALUES('bananas',20),('carrots',30);Delete one row from the table:
DELETEmydataset.ProduceWHEREproduct='bananas';Update one row of the table:
UPDATEmydataset.ProduceSETinventory=inventory-10WHEREproduct='carrots';Wait for 10 minutes and view the full change history of the changes:
SELECTproduct,inventory,_CHANGE_TYPEASchange_type,_CHANGE_TIMESTAMPASchange_timeFROMCHANGES(TABLEmydataset.Produce,NULL,TIMESTAMP_SUB(CURRENT_TIMESTAMP(),INTERVAL601SECOND))ORDERBYchange_time,product;The output is similar to the following:
+---------+-----------+-------------+---------------------+| product | inventory | change_type | change_time |+---------+-----------+-------------+---------------------+| bananas | 20 | INSERT | 2024-01-09 17:13:58 || carrots | 30 | INSERT | 2024-01-09 17:13:58 || bananas | 20 | DELETE | 2024-01-09 17:14:30 || carrots | 30 | DELETE | 2024-01-09 17:15:24 || carrots | 20 | UPDATE | 2024-01-09 17:15:24 |+---------+-----------+-------------+---------------------+
Enabling change history for an existing table
To set theenable_change_history optiontoTRUE for an existing table, use theALTER TABLE SET OPTIONS DDL statement.The following example updates the change history option formy_table toTRUE:
ALTERTABLE`my_dataset.my_table`SETOPTIONS(enable_change_history=TRUE);DATE_BUCKET
DATE_BUCKET(date_in_bucket,bucket_width)DATE_BUCKET(date_in_bucket,bucket_width,bucket_origin_date)Description
Gets the lower bound of the date bucket that contains a date.
Definitions
date_in_bucket: ADATEvalue that you can use to look up a date bucket.bucket_width: AnINTERVALvalue that represents the width ofa date bucket. Asingle interval withdate parts is supported.bucket_origin_date: ADATEvalue that represents a point in time. Allbuckets expand left and right from this point. If this argument isn't set,1950-01-01is used by default.
Return type
DATE
Examples
In the following example, the origin is omitted and the default origin,1950-01-01 is used. All buckets expand in both directions from the origin,and the size of each bucket is two days. The lower bound of the bucket inwhichmy_date belongs is returned.
WITHsome_datesAS(SELECTDATE'1949-12-29'ASmy_dateUNIONALLSELECTDATE'1949-12-30'UNIONALLSELECTDATE'1949-12-31'UNIONALLSELECTDATE'1950-01-01'UNIONALLSELECTDATE'1950-01-02'UNIONALLSELECTDATE'1950-01-03')SELECTDATE_BUCKET(my_date,INTERVAL2DAY)ASbucket_lower_boundFROMsome_dates;/*--------------------+ | bucket_lower_bound | +--------------------+ | 1949-12-28 | | 1949-12-30 | | 1949-12-30 | | 1950-01-01 | | 1950-01-01 | | 1950-01-03 | +--------------------*/-- Some date buckets that originate from 1950-01-01:-- + Bucket: ...-- + Bucket: [1949-12-28, 1949-12-30)-- + Bucket: [1949-12-30, 1950-01-01)-- + Origin: [1950-01-01]-- + Bucket: [1950-01-01, 1950-01-03)-- + Bucket: [1950-01-03, 1950-01-05)-- + Bucket: ...In the following example, the origin has been changed to2000-12-24,and all buckets expand in both directions from this point. The size of eachbucket is seven days. The lower bound of the bucket in whichmy_date belongsis returned:
WITHsome_datesAS(SELECTDATE'2000-12-20'ASmy_dateUNIONALLSELECTDATE'2000-12-21'UNIONALLSELECTDATE'2000-12-22'UNIONALLSELECTDATE'2000-12-23'UNIONALLSELECTDATE'2000-12-24'UNIONALLSELECTDATE'2000-12-25')SELECTDATE_BUCKET(my_date,INTERVAL7DAY,DATE'2000-12-24')ASbucket_lower_boundFROMsome_dates;/*--------------------+ | bucket_lower_bound | +--------------------+ | 2000-12-17 | | 2000-12-17 | | 2000-12-17 | | 2000-12-17 | | 2000-12-24 | | 2000-12-24 | +--------------------*/-- Some date buckets that originate from 2000-12-24:-- + Bucket: ...-- + Bucket: [2000-12-10, 2000-12-17)-- + Bucket: [2000-12-17, 2000-12-24)-- + Origin: [2000-12-24]-- + Bucket: [2000-12-24, 2000-12-31)-- + Bucket: [2000-12-31, 2000-01-07)-- + Bucket: ...DATETIME_BUCKET
DATETIME_BUCKET(datetime_in_bucket,bucket_width)DATETIME_BUCKET(datetime_in_bucket,bucket_width,bucket_origin_datetime)Description
Gets the lower bound of the datetime bucket that contains a datetime.
Definitions
datetime_in_bucket: ADATETIMEvalue that you can use to look up adatetime bucket.bucket_width: AnINTERVALvalue that represents the width ofa datetime bucket. Asingle interval withdate and time parts is supported.bucket_origin_datetime: ADATETIMEvalue that represents a point intime. All buckets expand left and right from this point. If this argumentisn't set,1950-01-01 00:00:00is used by default.
Return type
DATETIME
Examples
In the following example, the origin is omitted and the default origin,1950-01-01 00:00:00 is used. All buckets expand in both directions from theorigin, and the size of each bucket is 12 hours. The lower bound of the bucketin whichmy_datetime belongs is returned:
WITHsome_datetimesAS(SELECTDATETIME'1949-12-30 13:00:00'ASmy_datetimeUNIONALLSELECTDATETIME'1949-12-31 00:00:00'UNIONALLSELECTDATETIME'1949-12-31 13:00:00'UNIONALLSELECTDATETIME'1950-01-01 00:00:00'UNIONALLSELECTDATETIME'1950-01-01 13:00:00'UNIONALLSELECTDATETIME'1950-01-02 00:00:00')SELECTDATETIME_BUCKET(my_datetime,INTERVAL12HOUR)ASbucket_lower_boundFROMsome_datetimes;/*---------------------+ | bucket_lower_bound | +---------------------+ | 1949-12-30T12:00:00 | | 1949-12-31T00:00:00 | | 1949-12-31T12:00:00 | | 1950-01-01T00:00:00 | | 1950-01-01T12:00:00 | | 1950-01-02T00:00:00 | +---------------------*/-- Some datetime buckets that originate from 1950-01-01 00:00:00:-- + Bucket: ...-- + Bucket: [1949-12-30 00:00:00, 1949-12-30 12:00:00)-- + Bucket: [1949-12-30 12:00:00, 1950-01-01 00:00:00)-- + Origin: [1950-01-01 00:00:00]-- + Bucket: [1950-01-01 00:00:00, 1950-01-01 12:00:00)-- + Bucket: [1950-01-01 12:00:00, 1950-02-00 00:00:00)-- + Bucket: ...In the following example, the origin has been changed to2000-12-24 12:00:00,and all buckets expand in both directions from this point. The size of eachbucket is seven days. The lower bound of the bucket in whichmy_datetimebelongs is returned:
WITHsome_datetimesAS(SELECTDATETIME'2000-12-20 00:00:00'ASmy_datetimeUNIONALLSELECTDATETIME'2000-12-21 00:00:00'UNIONALLSELECTDATETIME'2000-12-22 00:00:00'UNIONALLSELECTDATETIME'2000-12-23 00:00:00'UNIONALLSELECTDATETIME'2000-12-24 00:00:00'UNIONALLSELECTDATETIME'2000-12-25 00:00:00')SELECTDATETIME_BUCKET(my_datetime,INTERVAL7DAY,DATETIME'2000-12-22 12:00:00')ASbucket_lower_boundFROMsome_datetimes;/*--------------------+ | bucket_lower_bound | +--------------------+ | 2000-12-15T12:00:00 | | 2000-12-15T12:00:00 | | 2000-12-15T12:00:00 | | 2000-12-22T12:00:00 | | 2000-12-22T12:00:00 | | 2000-12-22T12:00:00 | +--------------------*/-- Some datetime buckets that originate from 2000-12-22 12:00:00:-- + Bucket: ...-- + Bucket: [2000-12-08 12:00:00, 2000-12-15 12:00:00)-- + Bucket: [2000-12-15 12:00:00, 2000-12-22 12:00:00)-- + Origin: [2000-12-22 12:00:00]-- + Bucket: [2000-12-22 12:00:00, 2000-12-29 12:00:00)-- + Bucket: [2000-12-29 12:00:00, 2000-01-05 12:00:00)-- + Bucket: ...GAP_FILL
GAP_FILL(TABLEtime_series_table,time_series_column,bucket_width,[,partitioning_columns=>value][,value_columns=>value][,origin=>value][,ignore_null_values=>{TRUE|FALSE}])GAP_FILL((time_series_subquery),time_series_column,bucket_width,[,partitioning_columns=>values][,value_columns=>value][,origin=>value][,ignore_null_values=>{TRUE|FALSE}])Description
Finds and fills gaps in a time series.
Definitions
time_series_table: The name of the table that contains thetime series data.time_series_subquery: The subquery that contains the time series data.time_series_column: The name of the column intime_series_tableortime_series_subquerythat contains the time points of thetime series data. This column must represent aDATE,DATETIME,orTIMESTAMPtype.bucket_width: TheINTERVALvalue that represents the selected widthof the time buckets. The interval can represent aDATE,DATETIME, orTIMESTAMPtype.partitioning_columns: A named argument with anARRAY<STRING>value.Represents an array of zero or more column names used to partition datainto individual time series (time series identity). This has the same columntype requirements as thePARTITION BYclause.value_columns: A named argument with anARRAY<STRUCT<STRING, STRING>>value. Represents an array of column name and gap-filling method pairs inthe following format:[(column_name,gap_filling_method),...]column_name: ASTRINGvalue that represents a valid column fromtime_series_table. A column name can only be used once invalue_columns.gap_filling_method: ASTRINGvalue that can be one of the followinggap-filling methods:null(default): Fill in missing values withNULLvalues.linear: Fill in missing values usinglinear interpolation. So, when a new value is added, it's based ona linear slope for a specific time bucket. When this method isused,column_namemust be a numeric data type.locf: Fill in missing values by carrying the last observed valueforward. So, when a new value is added, it's based onthe previous value.
origin: ADATE,DATETIMEorTIMESTAMPoptional named argument.Represents a point in time from which all time buckets expand ineach direction.If
originisn't provided, the data type fortime_series_columnisassumed, and the corresponding default value is used:DATE '1950-01-01'DATETIME '1950-01-01 00:00:00'TIMESTAMP '1950-01-01 00:00:00'
ignore_null_values: A named argument with aBOOLvalue. Indicateswhether the function ignoresNULLvalues in the input data when performinggap filling. By default, this value isTRUE.If
TRUE(default),NULLvalues are skipped during gap filling.nullis the gap-filling method for a column: If a value in acolumn isNULL, the output isNULLfor that column.locforlinearis the gap-filling method for a column: Theprevious or next non-NULLvalue is used. The side effect of thisis that output value columns are neverNULL, except for the edges.
If
FALSE,NULLvalues are included during gap filling.nullis the gap-filling method for a column: If a value in acolumn isNULL, the output isNULLfor that column.locfis the gap-filling method for a column: If the previousvalue in that column isNULL, the output isNULLfor thatcolumn.linearis the gap-filling method for a column: If either ofthe endpoints in that column isNULL, the output isNULLforthat column.
Details
Sometimes the fixed time intervals produced by time bucket functions have gaps,either due to irregular sampling intervals or an event that caused data lossfor some time period. This can cause irregularities in reporting. For example,a plot with irregular intervals might have visible discontinuity. You can usetheGAP_FILL function to employ various gap-filling methods to fill inthose missing data points.
time_series_column andorigin must be of the same data type.
Return type
TABLE
Examples
In the following query, thelocf gap-filling method is applied to gaps:
CREATETEMPTABLEdevice_dataASSELECT*FROMUNNEST(ARRAY<STRUCT<device_idINT64,timeDATETIME,signalINT64,stateSTRING>>[STRUCT(1,DATETIME'2023-11-01 09:34:01',74,'INACTIVE'),STRUCT(2,DATETIME'2023-11-01 09:36:00',77,'ACTIVE'),STRUCT(3,DATETIME'2023-11-01 09:37:00',78,'ACTIVE'),STRUCT(4,DATETIME'2023-11-01 09:38:01',80,'ACTIVE')]);SELECT*FROMGAP_FILL(TABLEdevice_data,ts_column=>'time',bucket_width=>INTERVAL1MINUTE,value_columns=>[('signal','locf')])ORDERBYtime;/*---------------------+--------+ | time | signal | +---------------------+--------+ | 2023-11-01T09:35:00 | 74 | | 2023-11-01T09:36:00 | 77 | | 2023-11-01T09:37:00 | 78 | | 2023-11-01T09:38:00 | 78 | +---------------------+--------*/In the following query, thelinear gap-filling method is applied to gaps:
CREATETEMPTABLEdevice_dataASSELECT*FROMUNNEST(ARRAY<STRUCT<device_idINT64,timeDATETIME,signalINT64,stateSTRING>>[STRUCT(1,DATETIME'2023-11-01 09:34:01',74,'INACTIVE'),STRUCT(2,DATETIME'2023-11-01 09:36:00',77,'ACTIVE'),STRUCT(3,DATETIME'2023-11-01 09:37:00',78,'ACTIVE'),STRUCT(4,DATETIME'2023-11-01 09:38:01',80,'ACTIVE')]);SELECT*FROMGAP_FILL(TABLEdevice_data,ts_column=>'time',bucket_width=>INTERVAL1MINUTE,value_columns=>[('signal','linear')])ORDERBYtime;/*---------------------+--------+ | time | signal | +---------------------+--------+ | 2023-11-01T09:35:00 | 75 | | 2023-11-01T09:36:00 | 77 | | 2023-11-01T09:37:00 | 78 | | 2023-11-01T09:38:00 | 80 | +---------------------+--------*/In the following query, thenull gap-filling method is applied to gaps:
CREATETEMPTABLEdevice_dataASSELECT*FROMUNNEST(ARRAY<STRUCT<device_idINT64,timeDATETIME,signalINT64,stateSTRING>>[STRUCT(1,DATETIME'2023-11-01 09:34:01',74,'INACTIVE'),STRUCT(2,DATETIME'2023-11-01 09:36:00',77,'ACTIVE'),STRUCT(3,DATETIME'2023-11-01 09:37:00',78,'ACTIVE'),STRUCT(4,DATETIME'2023-11-01 09:38:01',80,'ACTIVE')]);SELECT*FROMGAP_FILL(TABLEdevice_data,ts_column=>'time',bucket_width=>INTERVAL1MINUTE,value_columns=>[('signal','null')])ORDERBYtime;/*---------------------+--------+ | time | signal | +---------------------+--------+ | 2023-11-01T09:35:00 | NULL | | 2023-11-01T09:36:00 | 77 | | 2023-11-01T09:37:00 | 78 | | 2023-11-01T09:38:00 | NULL | +---------------------+--------*/In the following query,NULL values in the input data are ignored by default:
CREATETEMPTABLEdevice_dataASSELECT*FROMUNNEST(ARRAY<STRUCT<device_idINT64,timeDATETIME,signalINT64,stateSTRING>>[STRUCT(1,DATETIME'2023-11-01 09:34:01',74,'INACTIVE'),STRUCT(2,DATETIME'2023-11-01 09:36:00',77,'ACTIVE'),STRUCT(3,DATETIME'2023-11-01 09:37:00',NULL,'ACTIVE'),STRUCT(4,DATETIME'2023-11-01 09:38:01',80,'ACTIVE')]);SELECT*FROMGAP_FILL(TABLEdevice_data,ts_column=>'time',bucket_width=>INTERVAL1MINUTE,value_columns=>[('signal','linear')])ORDERBYtime;/*---------------------+--------+ | time | signal | +---------------------+--------+ | 2023-11-01T09:35:00 | 75 | | 2023-11-01T09:36:00 | 77 | | 2023-11-01T09:37:00 | 78 | | 2023-11-01T09:38:00 | 80 | +---------------------+--------*/In the following query,NULL values in the input data aren't ignored, usingtheignore_null_values argument:
CREATETEMPTABLEdevice_dataASSELECT*FROMUNNEST(ARRAY<STRUCT<device_idINT64,timeDATETIME,signalINT64,stateSTRING>>[STRUCT(1,DATETIME'2023-11-01 09:34:01',74,'INACTIVE'),STRUCT(2,DATETIME'2023-11-01 09:36:00',77,'ACTIVE'),STRUCT(3,DATETIME'2023-11-01 09:37:00',NULL,'ACTIVE'),STRUCT(4,DATETIME'2023-11-01 09:38:01',80,'ACTIVE')]);SELECT*FROMGAP_FILL(TABLEdevice_data,ts_column=>'time',bucket_width=>INTERVAL1MINUTE,value_columns=>[('signal','linear')],ignore_null_values=>FALSE)ORDERBYtime;/*---------------------+--------+ | time | signal | +---------------------+--------+ | 2023-11-01T09:35:00 | 75 | | 2023-11-01T09:36:00 | 77 | | 2023-11-01T09:37:00 | NULL | | 2023-11-01T09:38:00 | NULL | +---------------------+--------*/In the following query, when thevalue_columns argument isn't passed in,thenull gap-filling method is used on all columns:
CREATETEMPTABLEdevice_dataASSELECT*FROMUNNEST(ARRAY<STRUCT<device_idINT64,timeDATETIME,signalINT64,stateSTRING>>[STRUCT(1,DATETIME'2023-11-01 09:34:01',74,'INACTIVE'),STRUCT(2,DATETIME'2023-11-01 09:36:00',77,'ACTIVE'),STRUCT(3,DATETIME'2023-11-01 09:37:00',79,'ACTIVE'),STRUCT(4,DATETIME'2023-11-01 09:38:01',80,'ACTIVE')]);SELECT*FROMGAP_FILL(TABLEdevice_data,ts_column=>'time',bucket_width=>INTERVAL1MINUTE)ORDERBYtime;/*---------------------+-----------+--------+----------+ | time | device_id | signal | state | +---------------------+-----------+--------+----------+ | 2023-11-01T09:35:00 | NULL | NULL | NULL | | 2023-11-01T09:36:00 | 2 | 77 | ACTIVE | | 2023-11-01T09:37:00 | 3 | 79 | ACTIVE | | 2023-11-01T09:38:00 | NULL | NULL | NULL | +---------------------+-----------+--------+----------*/In the following query, rows (buckets) are added for gaps that are found:
CREATETEMPTABLEdevice_dataASSELECT*FROMUNNEST(ARRAY<STRUCT<device_idINT64,timeDATETIME,signalINT64,stateSTRING>>[STRUCT(1,DATETIME'2023-11-01 09:35:39',74,'INACTIVE'),STRUCT(2,DATETIME'2023-11-01 09:37:39',77,'ACTIVE'),STRUCT(3,DATETIME'2023-11-01 09:38:00',77,'ACTIVE'),STRUCT(4,DATETIME'2023-11-01 09:40:00',80,'ACTIVE')]);SELECT*FROMGAP_FILL(TABLEdevice_data,ts_column=>'time',bucket_width=>INTERVAL1MINUTE,value_columns=>[('signal','locf')])ORDERBYtime;/*---------------------+--------+ | time | signal | +---------------------+--------+ | 2023-11-01T09:36:00 | 74 | | 2023-11-01T09:37:00 | 74 | | 2023-11-01T09:38:00 | 74 | | 2023-11-01T09:39:00 | 77 | | 2023-11-01T09:40:00 | 77 | +---------------------+--------*/In the following query, data is condensed when it fits in the same bucket andhas the same values:
CREATETEMPTABLEdevice_dataASSELECT*FROMUNNEST(ARRAY<STRUCT<device_idINT64,timeDATETIME,signalINT64,stateSTRING>>[STRUCT(1,DATETIME'2023-11-01 09:35:39',74,'INACTIVE'),STRUCT(2,DATETIME'2023-11-01 09:36:60',77,'ACTIVE'),STRUCT(3,DATETIME'2023-11-01 09:37:00',77,'ACTIVE'),STRUCT(4,DATETIME'2023-11-01 09:37:20',80,'ACTIVE')]);SELECT*FROMGAP_FILL(TABLEdevice_data,ts_column=>'time',bucket_width=>INTERVAL1MINUTE,value_columns=>[('signal','locf')])ORDERBYtime;/*---------------------+--------+ | time | signal | +---------------------+--------+ | 2023-11-01T09:36:00 | 74 | | 2023-11-01T09:37:00 | 77 | +---------------------+--------*/In the following query, gap filling is applied to partitions:
CREATETEMPTABLEdevice_dataASSELECT*FROMUNNEST(ARRAY<STRUCT<device_idINT64,timeDATETIME,signalINT64,stateSTRING>>[STRUCT(2,DATETIME'2023-11-01 09:35:07',87,'ACTIVE'),STRUCT(1,DATETIME'2023-11-01 09:35:26',82,'ACTIVE'),STRUCT(3,DATETIME'2023-11-01 09:35:39',74,'INACTIVE'),STRUCT(2,DATETIME'2023-11-01 09:36:07',88,'ACTIVE'),STRUCT(1,DATETIME'2023-11-01 09:36:26',82,'ACTIVE'),STRUCT(2,DATETIME'2023-11-01 09:37:07',88,'ACTIVE'),STRUCT(1,DATETIME'2023-11-01 09:37:28',80,'ACTIVE'),STRUCT(3,DATETIME'2023-11-01 09:37:39',77,'ACTIVE'),STRUCT(2,DATETIME'2023-11-01 09:38:07',86,'ACTIVE'),STRUCT(1,DATETIME'2023-11-01 09:38:26',81,'ACTIVE'),STRUCT(3,DATETIME'2023-11-01 09:38:39',77,'ACTIVE')]);SELECT*FROMGAP_FILL(TABLEdevice_data,ts_column=>'time',bucket_width=>INTERVAL1MINUTE,partitioning_columns=>['device_id'],value_columns=>[('signal','locf')])ORDERBYdevice_id;/*---------------------+-----------+--------+ | time | device_id | signal | +---------------------+-----------+--------+ | 2023-11-01T09:36:00 | 1 | 82 | | 2023-11-01T09:37:00 | 1 | 82 | | 2023-11-01T09:38:00 | 1 | 80 | | 2023-11-01T09:36:00 | 2 | 87 | | 2023-11-01T09:37:00 | 2 | 88 | | 2023-11-01T09:38:00 | 2 | 88 | | 2023-11-01T09:36:00 | 3 | 74 | | 2023-11-01T09:37:00 | 3 | 74 | | 2023-11-01T09:38:00 | 3 | 77 | +---------------------+-----------+--------*/In the following query, gap filling is applied to multiple columns, and eachcolumn uses a different gap-filling method:
CREATETEMPTABLEdevice_dataASSELECT*FROMUNNEST(ARRAY<STRUCT<device_idINT64,timeDATETIME,signalINT64,stateSTRING>>[STRUCT(1,DATETIME'2023-11-01 09:34:01',74,'ACTIVE'),STRUCT(2,DATETIME'2023-11-01 09:36:00',77,'INACTIVE'),STRUCT(3,DATETIME'2023-11-01 09:38:00',78,'ACTIVE'),STRUCT(4,DATETIME'2023-11-01 09:39:01',80,'ACTIVE')]);SELECT*FROMGAP_FILL(TABLEdevice_data,ts_column=>'time',bucket_width=>INTERVAL1MINUTE,value_columns=>[('signal','linear'),('state','locf')])ORDERBYtime;/*---------------------+--------+----------+ | time | signal | state | +---------------------+--------+----------+ | 2023-11-01T09:35:00 | 75 | ACTIVE | | 2023-11-01T09:36:00 | 77 | INACTIVE | | 2023-11-01T09:37:00 | 78 | INACTIVE | | 2023-11-01T09:38:00 | 78 | ACTIVE | | 2023-11-01T09:39:00 | 80 | ACTIVE | +---------------------+--------+----------*/In the following query, the point of origin is changed in the gap-fillingresults to a custom origin, using theorigin argument:
CREATETEMPTABLEdevice_dataASSELECT*FROMUNNEST(ARRAY<STRUCT<device_idINT64,timeDATETIME,signalINT64,stateSTRING>>[STRUCT(1,DATETIME'2023-11-01 09:34:01',74,'ACTIVE'),STRUCT(2,DATETIME'2023-11-01 09:36:00',77,'INACTIVE'),STRUCT(3,DATETIME'2023-11-01 09:38:00',78,'ACTIVE'),STRUCT(4,DATETIME'2023-11-01 09:39:01',80,'ACTIVE')]);SELECT*FROMGAP_FILL(TABLEdevice_data,ts_column=>'time',bucket_width=>INTERVAL1MINUTE,value_columns=>[('signal','null')],origin=>DATETIME'2023-11-01 09:30:01')ORDERBYtime;/*---------------------+--------+ | time | signal | +---------------------+--------+ | 2023-11-01T09:34:01 | 74 | | 2023-11-01T09:35:01 | NULL | | 2023-11-01T09:36:01 | NULL | | 2023-11-01T09:37:01 | NULL | | 2023-11-01T09:38:01 | NULL | | 2023-11-01T09:39:01 | 80 | +---------------------+--------*/In the following query, a subquery is passed into the function instead of atable:
SELECT*FROMGAP_FILL((SELECT*FROMUNNEST(ARRAY<STRUCT<device_idINT64,timeDATETIME,signalINT64,stateSTRING>>[STRUCT(1,DATETIME'2023-11-01 09:34:01',74,'INACTIVE'),STRUCT(2,DATETIME'2023-11-01 09:36:00',77,'ACTIVE'),STRUCT(3,DATETIME'2023-11-01 09:37:00',78,'ACTIVE'),STRUCT(4,DATETIME'2023-11-01 09:38:01',80,'ACTIVE')])),ts_column=>'time',bucket_width=>INTERVAL1MINUTE,value_columns=>[('signal','linear')])ORDERBYtime;/*---------------------+--------+ | time | signal | +---------------------+--------+ | 2023-11-01T09:35:00 | 75 | | 2023-11-01T09:36:00 | 77 | | 2023-11-01T09:37:00 | 78 | | 2023-11-01T09:38:00 | 80 | +---------------------+--------*/TIMESTAMP_BUCKET
TIMESTAMP_BUCKET(timestamp_in_bucket,bucket_width)TIMESTAMP_BUCKET(timestamp_in_bucket,bucket_width,bucket_origin_timestamp)Description
Gets the lower bound of the timestamp bucket that contains a timestamp.
Definitions
timestamp_in_bucket: ATIMESTAMPvalue that you can use to look up atimestamp bucket.bucket_width: AnINTERVALvalue that represents the width ofa timestamp bucket. Asingle interval withdate and time parts is supported.bucket_origin_timestamp: ATIMESTAMPvalue that represents a point intime. All buckets expand left and right from this point. If this argumentisn't set,1950-01-01 00:00:00is used by default.
Return type
TIMESTAMP
Examples
In the following example, the origin is omitted and the default origin,1950-01-01 00:00:00 is used. All buckets expand in both directions from theorigin, and the size of each bucket is 12 hours. The default time zone,UTC, is included in the results. The lower bound of thebucket in whichmy_timestamp belongs is returned:
WITHsome_timestampsAS(SELECTTIMESTAMP'1949-12-30 13:00:00.00'ASmy_timestampUNIONALLSELECTTIMESTAMP'1949-12-31 00:00:00.00'UNIONALLSELECTTIMESTAMP'1949-12-31 13:00:00.00'UNIONALLSELECTTIMESTAMP'1950-01-01 00:00:00.00'UNIONALLSELECTTIMESTAMP'1950-01-01 13:00:00.00'UNIONALLSELECTTIMESTAMP'1950-01-02 00:00:00.00')SELECTTIMESTAMP_BUCKET(my_timestamp,INTERVAL12HOUR)ASbucket_lower_boundFROMsome_timestamps;-- Display of results may differ, depending upon the environment and-- time zone where this query was executed./*------------------------+ | bucket_lower_bound | +-------------------------+ | 1949-12-30 12:00:00 UTC | | 1949-12-31 00:00:00 UTC | | 1949-12-31 12:00:00 UTC | | 1950-01-01 00:00:00 UTC | | 1950-01-01 12:00:00 UTC | | 1950-01-02 00:00:00 UTC | +-------------------------*/-- Some timestamp buckets that originate from 1950-01-01 00:00:00:-- + Bucket: ...-- + Bucket: [1949-12-30 00:00:00.00 UTC, 1949-12-30 12:00:00.00 UTC)-- + Bucket: [1949-12-30 12:00:00.00 UTC, 1950-01-01 00:00:00.00 UTC)-- + Origin: [1950-01-01 00:00:00.00 UTC]-- + Bucket: [1950-01-01 00:00:00.00 UTC, 1950-01-01 12:00:00.00 UTC)-- + Bucket: [1950-01-01 12:00:00.00 UTC, 1950-02-00 00:00:00.00 UTC)-- + Bucket: ...In the following example, the origin has been changed to2000-12-24 12:00:00,and all buckets expand in both directions from this point. The size of eachbucket is seven days. The default time zone, UTC, is includedin the results. The lower bound of the bucket in whichmy_timestampbelongs is returned:
WITHsome_timestampsAS(SELECTTIMESTAMP'2000-12-20 00:00:00.00'ASmy_timestampUNIONALLSELECTTIMESTAMP'2000-12-21 00:00:00.00'UNIONALLSELECTTIMESTAMP'2000-12-22 00:00:00.00'UNIONALLSELECTTIMESTAMP'2000-12-23 00:00:00.00'UNIONALLSELECTTIMESTAMP'2000-12-24 00:00:00.00'UNIONALLSELECTTIMESTAMP'2000-12-25 00:00:00.00')SELECTTIMESTAMP_BUCKET(my_timestamp,INTERVAL7DAY,TIMESTAMP'2000-12-22 12:00:00.00')ASbucket_lower_boundFROMsome_timestamps;-- Display of results may differ, depending upon the environment and-- time zone where this query was executed./*------------------------+ | bucket_lower_bound | +-------------------------+ | 2000-12-15 12:00:00 UTC | | 2000-12-15 12:00:00 UTC | | 2000-12-15 12:00:00 UTC | | 2000-12-22 12:00:00 UTC | | 2000-12-22 12:00:00 UTC | | 2000-12-22 12:00:00 UTC | +-------------------------*/-- Some timestamp buckets that originate from 2000-12-22 12:00:00:-- + Bucket: ...-- + Bucket: [2000-12-08 12:00:00.00 UTC, 2000-12-15 12:00:00.00 UTC)-- + Bucket: [2000-12-15 12:00:00.00 UTC, 2000-12-22 12:00:00.00 UTC)-- + Origin: [2000-12-22 12:00:00.00 UTC]-- + Bucket: [2000-12-22 12:00:00.00 UTC, 2000-12-29 12:00:00.00 UTC)-- + Bucket: [2000-12-29 12:00:00.00 UTC, 2000-01-05 12:00:00.00 UTC)-- + Bucket: ...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-11-24 UTC.