This page describes the dialect differences between GoogleSQL and PostgreSQL and offers recommendations for using PostgreSQL approaches for specific GoogleSQL features.
GoogleSQL dialect feature differences
| GoogleSQL feature | PostgreSQL dialect recommendation | 
|---|---|
| Sample datasets | No recommendation available. | 
| BigQuery external datasets | Use Spanner federated queries. | 
| ENUM | Use TEXTcolumns with checked
      constraints instead. UnlikeENUMS, the sort order of aTEXTcolumn can't be user-defined.
      The following example restricts the column to only support the'C','B', and'A'values.
CREATE TABLE singers (
 singer_id BIGINT PRIMARY KEY,
 type TEXT NOT NULL CHECK (type IN ('C', 'B', 'A'))
);
        | 
| Graph | No recommendation available. | 
| HAVING MAXorHAVING MIN | Use a JOINor a subquery to filter
      for theMAXorMINvalue for the aggregation.
      The following example requires filteringMAXorMINin a subquery.WITH amount_per_year AS ( SELECT 1000 AS amount, 2025 AS year UNION ALL SELECT 10000, 2024 UNION ALL SELECT 500, 2023 UNION ALL SELECT 1500, 2025 UNION ALL SELECT 20000, 2024 ) SELECT SUM(amount) AS max_year_amount_sum FROM amount_per_year WHERE year = (SELECT MAX(year) FROM amount_per_year); | 
| Informational foreign keys | No recommendation available. | 
| JSONdata type | Use the JSONBdata type. | 
| SELECT to_json(table) FROM table | We recommend explicitly mapping each column with the jsonb_build_objectfunction:
WITH singers AS (
  SELECT 1::int8 AS id, 'Singer First Name'::text AS first_name
)
SELECT jsonb_build_object('id', id, 'first_name', first_name)
FROM singers; | 
| ORDER BY … COLLATE … | No recommendation available. | 
| NUMERICcolumn as a primary key, secondary index, or
    foreign key | We recommend using an index over a TEXTgenerated column,
    as shown in the following example:CREATE TABLE singers( id numeric NOT NULL, pk text GENERATED ALWAYS AS (id::text) STORED, PRIMARY KEY(pk) ); | 
| Protocol buffer data type | You can store serialized protocol buffers as the PostgreSQL . | 
| PRIMARY KEY DESC | No recommendation available. | 
| SELECT AS VALUE | |
| SELECT * EXCEPT | We recommend that you spell out all columns in the SELECTstatement. | 
| SELECT * REPLACE | We recommend that you spell out all columns in the SELECTstatement. | 
| The following columns in the SPANNER_SYSstatistics tables:
 | No recommendation available. | 
| TABLESAMPLE | We recommend that you apply a custom function F, which
      converts a row toTEXTorBYTEA. You can then
      usespanner.farm_fingerprintto sample your
      data.In the following example, we use CONCATas our functionF:
-- Given the following schema
CREATE TABLE singers (
 singer_id BIGINT PRIMARY KEY,
 first_name VARCHAR(1024),
 last_name VARCHAR(1024),
 singer_info BYTEA
);
-- Create a hash for each row (using all columns)
WITH hashed_rows AS (
  SELECT
    *,
    ABS(MOD(spanner.farm_fingerprint(
      CONCAT(
        singer_id::text,
        first_name,
        last_name,
        singer_info::text
      )
    ), 100)) AS hash_value
  FROM singers
)
-- Sample data
SELECT *
FROM hashed_rows
WHERE hash_value < 10 -- sample roughly 10%
LIMIT 10; /* Optional: LIMIT to a max of 10 rows
             to be returned */ | 
| VALUE IN UNNEST(ARRAY(...)) | Use the equality operator with the ANYfunction, as shown in the following example:SELECT value = any(array[...]) | 
GoogleSQL dialect function differences
| GoogleSQL function | PostgreSQL dialect recommendation | 
|---|---|
| ACOSH | Use the formula of the function explicitly, as
      shown in the following example: SELECT LN(x + SQRT(x*x - 1)); | 
| APPROX_COSINE_DISTANCE | No recommendation available. | 
| APPROX_DOT_PRODUCT | |
| APPROX_EUCLIDEAN_DISTANCE | |
| ANY_VALUE | Workaround available outside of aggregation and GROUP BY.
      Use a subquery with theORDER BYorLIMITclauses, as shown in the following example:SELECT * FROM ( (expression) UNION ALL SELECT NULL, … -- as many columns as you have ) AS rows ORDER BY 1 NULLS LAST LIMIT 1; | 
| ARRAY_CONCAT_AGG | You can use ARRAY_AGGandUNNESTas shown in the
      following example:
WITH albums AS
(
  SELECT ARRAY['Song A', NULL, 'Song B'] AS songs
  UNION ALL
  SELECT NULL
  UNION ALL
  SELECT ARRAY[]::TEXT[]
)
SELECT ARRAY_AGG(song) FROM albums, UNNEST(songs) song;
       | 
| ARRAY_FIRST | Use the array subscript operator, as shown in the
      following example: SELECT array_expression[1]; NULLfor empty arrays. | 
| ARRAY_INCLUDES | Use the equality operator with the ANYfunction, as shown in the following example:SELECT search_value = ANY(array_to_search); | 
| ARRAY_INCLUDES_ALL | Use the array contains operator, as shown in the
      following example: SELECT array_to_search @> search_values; | 
| ARRAY_INCLUDES_ANY | Use the array overlap operator, as shown in the
      following example: SELECT array_to_search && search_values; | 
| ARRAY_IS_DISTINCT | Use a subquery to count distinct values and
      compare them to the original array length, as shown in the following
      example: SELECT ARRAY_LENGTH(value, 1) = ( SELECT COUNT(DISTINCT e) FROM UNNEST(value) AS e); | 
| ARRAY_LAST | Use the array subscript operator, as shown in the
      following example 
SELECT (value)[ARRAY_LENGTH(value, 1)];
      NULLfor empty arrays. | 
| ARRAY_MAX | Use a subquery with UNNESTand theMAXfunction, as shown in the following example:
SELECT MAX(e) FROM UNNEST(value) AS e;
       | 
| ARRAY_MIN | Use a subquery with UNNESTand theMINfunction, as shown in the following example:
SELECT MIN(e) FROM UNNEST(value) AS e;
       | 
| ARRAY_REVERSE | No recommendation available. | 
| ASINH | Use the formula of the function explicitly, as
      shown in the following example: SELECT LN(x + SQRT(x*x - 1)); | 
| ATANH | Use the formula of the function explicitly, as
      shown in the following example: SELECT 0.5 * LN((1 + x) / (1 - x)); | 
| BIT_COUNT | No recommendation available. | 
| BIT_XOR | |
| BYTE_LENGTH | |
| CODE_POINTS_TO_BYTES | |
| CODE_POINTS_TO_STRING | |
| COSH | Use the formula of the function explicitly, as
      shown in the following example: 
SELECT (EXP(x) + EXP(-x)) / 2;
       | 
| ERROR | No recommendation available. | 
| FROM_BASE32 | |
| FROM_BASE64 | |
| FROM_HEX | |
| GENERATE_ARRAY | |
| GENERATE_DATE_ARRAY | |
| NET.HOST | Use a regular expression and the substringfunction, as shown in the following example:
/* Use modified regular expression from
  https://tools.ietf.org/html/rfc3986#appendix-A. */
SELECT Substring('http://www.google.com/test' FROM
  '^(?:[^:/?#]+:)?(?://)?([^/?#]*)?[^?#]*(?:\\?[^#]*)?(?:#.*)?') | 
| NET.IP_FROM_STRING | No recommendation available. | 
| NET.IP_NET_MASK | |
| NET.IP_TO_STRING | |
| NET.IP_TRUNC | |
| NET.IPV4_FROM_INT64 | |
| NET.IPV4_TO_INT64 | |
| NET.PUBLIC_SUFFIX | |
| NET.REG_DOMAIN | |
| NET.SAFE_IP_FROM_STRING | |
| NORMALIZE | |
| NORMALIZE_AND_CASEFOLD | |
| REGEXP_EXTRACT_ALL | |
| SAFE.ADD | We recommend that you protect against an overflow explicitly leveraging
      the NUMERICdata type.WITH numbers AS ( SELECT 1::int8 AS a, 9223372036854775807::int8 AS b UNION ALL SELECT 1, 2 ) SELECT CASE WHEN a::numeric + b::numeric > 9223372036854775807 THEN NULL WHEN a + b < -9223372036854775808 THEN NULL ELSE a + b END AS result FROM numbers; | 
| SAFE.CAST | No recommendation available. | 
| SAFE.CONVERT_BYTES_TO_STRING | |
| SAFE.DIVIDE | We recommend that you protect against an overflow explicitly leveraging
      the NUMERICdata type during a division operation.WITH numbers AS ( SELECT 1::int8 AS a, 9223372036854775807::int8 AS b UNION ALL SELECT 10, 2 ) SELECT CASE WHEN b = 0 THEN NULL WHEN a::numeric / b::numeric > 9223372036854775807 THEN NULL WHEN a::numeric / b::numeric < -9223372036854775808 THEN NULL ELSE a / b END AS result FROM numbers; | 
| SAFE.MULTIPLY | We recommend that you protect against an overflow explicitly leveraging
      the NUMERICdata type during a multiplication operation.WITH numbers AS ( SELECT 1::int8 AS a, 9223372036854775807::int8 AS b UNION ALL SELECT 1, 2 ) SELECT CASE WHEN a::numeric * b::numeric > 9223372036854775807 THEN NULL WHEN a::numeric * b::numeric < -9223372036854775808 THEN NULL ELSE a * b END AS result FROM numbers; | 
| SAFE.NEGATE | We recommend that you protect against an overflow explicitly leveraging
      the NUMERICdata type during a negation operation.WITH numbers AS ( SELECT 9223372036854775807 AS a UNION ALL SELECT -9223372036854775808 ) SELECT CASE WHEN a <= -9223372036854775808 THEN NULL WHEN a >= 9223372036854775809 THEN NULL ELSE -a END AS result FROM numbers; | 
| SAFE.SUBTRACT | We recommend that you protect against an overflow explicitly leveraging
      the NUMERICdata type during a subtraction operation.WITH numbers AS ( SELECT 1::int8 AS a, 9223372036854775807::int8 AS b UNION ALL SELECT 1, 2 ) SELECT CASE WHEN a::numeric - b::numeric > 9223372036854775807 THEN NULL WHEN a::numeric - b::numeric < -9223372036854775808 THEN NULL ELSE a - b END AS result FROM numbers; | 
| SAFE.TO_JSON | No recommendation available. | 
| SINH | Use the formula of the function explicitly, as
      shown in the following example: SELECT (EXP(x) - EXP(-x)) / 2; | 
| SPLIT | Use the regexp_split_to_arrayfunction, as shown in the following example:WITH letters AS ( SELECT '' as letter_group UNION ALL SELECT 'a' as letter_group UNION ALL SELECT 'b c d' as letter_group ) SELECT regexp_split_to_array(letter_group, ' ') as example FROM letters; | 
| STDDEV | Use the formula of the function explicitly
      (unbiased standard deviation), as shown in the following example: WITH numbers AS ( SELECT 1 AS x UNION ALL SELECT 2 UNION ALL SELECT 3 ), mean AS ( SELECT AVG(x)::float8 AS mean FROM numbers ) SELECT SQRT(SUM(POWER(numbers.x - mean.mean, 2)) / (COUNT(x) - 1)) AS stddev FROM numbers CROSS JOIN mean | 
| STDDEV_SAMP | Use the formula of the function explicitly
      (unbiased standard deviation), as shown in the following example: 
WITH numbers AS
(
  SELECT 1 AS x
  UNION ALL
  SELECT 2
  UNION ALL
  SELECT 3
),
mean AS (
  SELECT AVG(x)::float8 AS mean
  FROM numbers
)
SELECT SQRT(SUM(POWER(numbers.x - mean.mean, 2)) / (COUNT(x) - 1))
  AS stddev
FROM numbers
CROSS JOIN mean
       | 
| TANH | Use the formula of the function explicitly. SELECT (EXP(x) - EXP(-x)) / (EXP(x) + EXP(-x)); | 
| TIMESTAMP_MICROS | Use the to_timestampfunction and
      truncate the microseconds part of the input (precision loss), as shown
      in the following example:SELECT to_timestamp(1230219000123456 / 1000000); | 
| TIMESTAMP_MILLIS | Use the to_timestampfunction and
      truncate the milliseconds part of the input (precision loss), as shown
      in the following example:SELECT to_timestamp(1230219000123 / 1000); | 
| TO_BASE32 | No recommendation available. | 
| TO_BASE64 | |
| TO_CODE_POINTS | |
| TO_HEX | |
| VAR_SAMP | Use the formula of the function explicitly
      (unbiased variance), as shown in the following: -- Use formula directly (unbiased) WITH numbers AS ( SELECT 1 AS x UNION ALL SELECT 2 UNION ALL SELECT 3 ), mean AS ( SELECT Avg(x)::float8 AS mean FROM numbers ) SELECT Sum(Power(numbers.x - mean.mean, 2)) / (Count(x) - 1) AS variance FROM numbers CROSS JOIN mean | 
| VARIANCE | Use the formula of the function explicitly
      (unbiased variance), as shown in the following example: -- Use formula directly (unbiased VARIANCE like VAR_SAMP) WITH numbers AS ( SELECT 1 AS x UNION ALL SELECT 2 UNION ALL SELECT 3 ), mean AS ( SELECT AVG(x)::float8 AS mean FROM numbers ) SELECT SUM(POWER(numbers.x - mean.mean, 2)) / (COUNT(x) - 1) AS variance FROM numbers CROSS JOIN mean | 
What's next
- Learn more about Spanner's PostgreSQL language support.