Dialect parity between GoogleSQL and PostgreSQL

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
Use TEXT columns with checked constraints instead. Unlike ENUMS , the sort order of a TEXT column 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.
Use a JOIN or a subquery to filter for the MAX or MIN value for the aggregation. The following example requires filtering MAX or MIN in 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);
No recommendation available.
No recommendation available.
NUMERIC column as a primary key, secondary index, or foreign key
We recommend using an index over a TEXT generated 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 BYTEA data type .
No recommendation available.
We recommend that you spell out all columns in the SELECT statement.
We recommend that you spell out all columns in the SELECT statement.
The following columns in the SPANNER_SYS statistics tables:
No recommendation available.
We recommend that you apply a custom function F , which converts a row to TEXT or BYTEA . You can then use spanner.farm_fingerprint to sample your data.

In the following example, we use CONCAT as our function F :
-- 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 */
Use the equality operator with the ANY function, as shown in the following example:
SELECT value = any(array[...])

GoogleSQL dialect function differences

GoogleSQL function
PostgreSQL dialect recommendation
Use the formula of the function explicitly, as shown in the following example:
SELECT LN(x + SQRT(x*x - 1));
No recommendation available.
Workaround available outside of aggregation and GROUP BY . Use a subquery with the ORDER BY or LIMIT clauses, 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;
You can use ARRAY_AGG and UNNEST as 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;
Use the array subscript operator, as shown in the following example:
SELECT array_expression[1];
Note that this will return NULL for empty arrays.
Use the equality operator with the ANY function, as shown in the following example:
SELECT search_value = ANY(array_to_search);
Use the array contains operator, as shown in the following example:
SELECT array_to_search @> search_values;
Use the array overlap operator, as shown in the following example:
SELECT array_to_search && search_values;
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);
Use the array subscript operator, as shown in the following example
SELECT (value)[ARRAY_LENGTH(value, 1)];
This returns NULL for empty arrays.
Use a subquery with UNNEST and the MAX function, as shown in the following example:
SELECT MAX(e) FROM UNNEST(value) AS e;
Use a subquery with UNNEST and the MIN function, as shown in the following example:
SELECT MIN(e) FROM UNNEST(value) AS e;
No recommendation available.
Use the formula of the function explicitly, as shown in the following example:
SELECT LN(x + SQRT(x*x - 1));
Use the formula of the function explicitly, as shown in the following example:
SELECT 0.5 * LN((1 + x) / (1 - x));
No recommendation available.
No recommendation available.
No recommendation available.
Use the formula of the function explicitly, as shown in the following example:
SELECT (EXP(x) + EXP(-x)) / 2;
No recommendation available.
Use a regular expression and the substring function, 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
  '^(?:[^:/?#]+:)?(?://)?([^/?#]*)?[^?#]*(?:\\?[^#]*)?(?:#.*)?')
No recommendation available.
We recommend that you protect against an overflow explicitly leveraging the NUMERIC data 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;
No recommendation available.
We recommend that you protect against an overflow explicitly leveraging the NUMERIC data 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;
We recommend that you protect against an overflow explicitly leveraging the NUMERIC data 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;
We recommend that you protect against an overflow explicitly leveraging the NUMERIC data 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;
We recommend that you protect against an overflow explicitly leveraging the NUMERIC data 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;
No recommendation available.
Use the formula of the function explicitly, as shown in the following example:
SELECT (EXP(x) - EXP(-x)) / 2;
Use the regexp_split_to_array function, 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;
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
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
Use the formula of the function explicitly.
SELECT (EXP(x) - EXP(-x)) / (EXP(x) + EXP(-x));
Use the to_timestamp function and truncate the microseconds part of the input (precision loss), as shown in the following example:
SELECT to_timestamp(1230219000123456 / 1000000);
Use the to_timestamp function and truncate the milliseconds part of the input (precision loss), as shown in the following example:
SELECT to_timestamp(1230219000123 / 1000);
No recommendation available.
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
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

Design a Mobile Site
View Site in Mobile | Classic
Share by: