Skip to content

4 SQL Statements

SQL syntax, data types, functions, optimizer behavior, and worked examples.
GASource pages: PDF 35-54Original PDF on GitHub

PDF page 35

This section explains new features related to SQL statements.

3.2.1 ALTER NO DEPENDS ON

ALTER statements for functions, indexes, materialized views, and triggers can now remove dependency settings for extensions. Syntax

ALTER object_name NO DEPENDS ON EXTENSION extension_name
Example 27 ALTER NO DEPENDS ON statement
postgres=> ALTER FUNCTION func1 DEPENDS ON EXTENSION cube ;
ALTER FUNCTION
postgres=> ALTER FUNCTION func1 NO DEPENDS ON EXTENSION cube ;
ALTER FUNCTION

3.2.2 ALTER STATISTICS SET STATISTICS

Specify the statistics collection target when executing ANALYZE. The value that can be set is from 0 to 10,000. If you specify 10,000 or more, it is assumed to be 10,000. Specify -1 to reset to the default value. Syntax

ALTER STATISTICS statistic_name SET STATISTICS new_target

The specified value can be checked in the stxstattarget column of the pg_statistic_ext catalog or with the command “\d” in the psql utility.

PDF page 36

Example 28 ALTER STATISTICS SET STATISTICS statement
postgres=> CREATE TABLE data1(c1 INT, c2 INT, c3 VARCHAR(10)) ;
CREATE TABLE
postgres=> CREATE STATISTICS stat1 ON c1, c2 FROM data1 ;
CREATE STATISTICS
postgres=> ALTER STATISTICS stat1 SET STATISTICS 10000 ;
ALTER STATISTICS
postgres=> SELECT stxname, stxstattarget FROM pg_statistic_ext
WHERE stxname='stat1' ;
 stxname | stxstattarget
---------+---------------
 stat1   |         10000
(1 row)
postgres=> \d data1
                      Table "public.data1"
 Column |         Type          | Collation | Nullable | Default
--------+--------------------+-----------+----------+---------
 c1     | integer               |             |          |
 c2     | integer               |             |          |
 c3     | character varying(10) |          |          |
Statistics objects:
    "public"."stat1" (ndistinct, dependencies, mcv) ON c1, c2 FROM
data1; STATISTICS 10000

3.2.3 ALTER TABLE

The following enhancements have been added to the ALTER TABLE statement.

ALTER TABLE ALTER COLUMN DROP EXPRESSION

By executing the ALTER TABLE ALTER COLUMN DROP EXPRESSION statement, it is now possible to drop an auto-created definition from a column specified with the GENERATED ALWAYS clause. Simply drop the calculation information and the column values will remain. IF EXISTS clause can be specified.

PDF page 37

Syntax

ALTER TABLE  table_name ALTER COLUMN  column_name DROP EXPRESSION
[IF EXISTS]
Example 29 ALTER TABLE DROP EXPRESSION statement
postgres=> CREATE TABLE gen1(c1 INT, c2 INT, c3 INT
GENERATED ALWAYS AS (c1 + c2) STORED) ;
CREATE TABLE
postgres=> \d gen1
                              Table "public.gen1"
 Column |  Type   | Collation | Nullable |               Default
--------+---------+-----------+----------+--------------------------
c1     | integer |           |          |
 c2     | integer |           |          |
 c3     | integer |           |          | generated always as (c1 + c2)
stored
postgres=> INSERT INTO gen1(c1, c2) VALUES (100, 200) ;
INSERT 0 1
postgres=> ALTER TABLE gen1 ALTER COLUMN c3
DROP EXPRESSION IF EXISTS ;
ALTER TABLE
postgres=> \d gen1
                Table "public.gen1"
 Column |  Type   | Collation | Nullable | Default
--------+---------+-----------+----------+---------
 c1     | integer |           |          |
 c2     | integer |           |          |
 c3     | integer |           |          |
postgres=> SELECT * FROM gen1 ;
 c1  | c2  | c3
-----+-----+-----
 100 | 200 | 300
(1 row)

PDF page 38

ALTER TABLE ALTER COLUMN SET STORAGE

Alters to a column’s STORAGE attribute are now propagated to the index. This feature has been backported to the latest versions of PostgreSQL 12 and PostgreSQL 11.

Example 30 ALTER TABLE SET STORAGE statement
postgres=> CREATE TABLE attr1(c1 CHAR(10)) ;
CREATE TABLE
postgres=> \d+ attr1
                                      Table "public.attr1"
 Column |     Type      | Collation | Nullable | Default | Storage  | …
--------+-------------+-----------+----------+---------+----------+ …
 c1     | character(10) |            |            |           | extended | …
Access method: heap
postgres=> CREATE INDEX idx1_attr1 ON attr1(c1) ;
CREATE INDEX
postgres=> ALTER TABLE attr1 ALTER COLUMN c1 SET STORAGE EXTERNAL ;
ALTER TABLE
postgres=> \d+ idx1_attr1
                      Index "public.idx1_attr1"
 Column |     Type      | Key? | Definition | Storage  | Stats target
--------+-------------+------+------------+----------+--------------
 c1     | character(10) | yes | c1           | external |
btree, for table "public.attr1"

3.2.4 ALTER TYPE

In the ALTER TYPE statement, attributes can be changed using the SET clause. Syntax

ALTER TYPE type_name SET (attribute = value)

The attributes that can be changed are as follows. Part of the attributes that can be specified in the CREATE TYPE statement.

PDF page 39

Attribute nameDescription
RECEIVEThe function that converts external representation into an internal representation.
SENDThe function to convert from the internal representation to binary.
TYPMOD_INModifier support function.
TYPMOD_OUTModifier support function.
ANALYZEStatistics collection function.
STORAGEThe function that determines how to store variable-length data.
Table 20 Attributes that can be changed
Example 31 ALTER TYPE SET statement
postgres=# ALTER TYPE box SET (SEND = myboxsend) ;
ALTER TYPE

3.2.5 ALTER VIEW

Column names in views can now be changed. Execute an ALTER VIEW RENAME COLUMN statement. Syntax

ALTER VIEW view_name RENAME COLUMN old_name TO new_name

PDF page 40

Example 32 Rename view column name
postgres=> CREATE VIEW customer_name AS SELECT c_customer_id,
c_first_name || ',' || c_last_name c_full_name FROM customer ;
CREATE VIEW
postgres=> \d customer_name
                  View "public.customer_name"
    Column     |     Type      | Collation | Nullable | Default
---------------+---------------+-----------+----------+---------
 c_customer_id | character(16) |           |          |
 c_full_name   | text          |           |          |
postgres=> ALTER VIEW customer_name RENAME COLUMN c_full_name TO full_name ;
ALTER VIEW
postgres=> \d customer_name
                  View "public.customer_name"
    Column     |     Type      | Collation | Nullable | Default
---------------+---------------+-----------+----------+---------
 c_customer_id | character(16) |           |          |
 full_name     | text          |           |          |

3.2.6 CREATE DATABASE

The LOCALE clause can now be specified as an option in the CREATE DATABASE statement. It cannot be specified at the same time as LC_CTYPE clause or LC_COLLATE clause. Syntax

CREATE DATABASE database_name [[WITH] LOCALE [=] 'locale_name']

PDF page 41

Example 33 Specify LOCALE clause
postgres=# CREATE DATABASE localedb1 WITH LOCALE='ja_JP.eucjp' ENCODING='eucjp'
TEMPLATE=template0 ;
CREATE DATABASE
postgres=# \l localedb1
                                List of databases
   Name    |  Owner   | Encoding |   Collate   |    Ctype    | Access privileges
-----------+----------+----------+-------------+-------------+-----------------
 localedb1 | postgres | EUC_JP   | ja_JP.eucjp | ja_JP.eucjp |
(1 row)

3.2.7 CREATE INDEX

In-page deduplication has been added to the B-Tree index. This feature is enabled by default. To disable this feature, set the deduplicate_items attribute to ‘off’.

Example 34 B-Tree index deduplication
postgres=> CREATE TABLE data1(c1 INT, c2 VARCHAR(10), c3 VARCHAR(10)) ;
CREATE TABLE
postgres=> INSERT INTO data1 VALUES (generate_series(1, 1000000),
'data1', 'data1') ;
INSERT 0 1000000
postgres=> CREATE INDEX idx1_dup ON data1(c2)
WITH (deduplicate_items = off) ;
CREATE INDEX
postgres=> CREATE INDEX idx2_dedup ON data1(c3)
WITH (deduplicate_items = on) ;
CREATE INDEX
postgres=> \d+ idx1_dup
                           Index "public.idx1_dup"
 Column |         Type          | Key? | Definition | Storage  | Stats target
--------+-----------------------+------+------------+----------+--
 c2     | character varying(10) | yes  | c2         | extended |
btree, for table "public.data1"
Options: deduplicate_items=off

PDF page 42

Example 35 Effect of index deduplication
postgres=> SELECT pg_size_pretty(pg_relation_size('idx1_dup')) ;
 pg_size_pretty
----------------
 21 MB
(1 row)
postgres=> SELECT pg_size_pretty(pg_relation_size('idx2_dedup')) ;
 pg_size_pretty
----------------
 6792 kB
(1 row)

3.2.8 CREATE TABLE

Some attributes4 can be specified in the CREATE TABLE/ALTER TABLE statement.

Example 36 Specifying additional attributes for tables
postgres=> CREATE TABLE data1(c1 NUMERIC, c2 VARCHAR(10)) ;
CREATE TABLE
postgres=> ALTER TABLE data1 SET (toast.vacuum_index_cleanup = off) ;
ALTER TABLE
postgres=> ALTER TABLE data1 SET (autovacuum_vacuum_insert_threshold = 10000) ;
ALTER TABLE
postgres=> ALTER TABLE data1 SET (autovacuum_vacuum_insert_scale_factor =
0.1) ;
ALTER TABLE

3.2.9 CREATE TABLESPACE

Maintenance_io_concurrency5 can now be specified for tablespace attributes.

4 See 3.3.1 Added parameters 5 See 3.3.1 Added parameters

PDF page 43

Example 37 Specify maintenance_io_concurrency attribute
postgres=# CREATE TABLESPACE ts1 LOCATION '/home/postgres/ts1'
WITH (maintenance_io_concurrency = 20) ;
CREATE TABLESPACE
postgres=# ALTER TABLESPACE ts1 SET (maintenance_io_concurrency = 30) ;
ALTER TABLESPACE
postgres=# \db+ ts1
                                                List of tablespaces
 Name |  Owner   |      Location      | Access privileges |             Options
|  Size   | Description
------+----------+--------------------+-------------------+--------------
-------------------+---------+-------------
 ts1  | postgres | /home/postgres/ts1 |                   |
{maintenance_io_concurrency=30} | 0 bytes |
(1 row)

3.2.10 DROP DATABASE FORCE

The database can now be forcibly dropped even if there are connected session exists. The connected session is forcibly closed. The database to which the session issuing the DROP DATABASE statement is connected cannot be dropped. Syntax

DROP DATABASE database_name [[WITH] (FORCE)]
Example 38 DROP DATABASE statement
postgres=# DROP DATABASE demodb WITH (FORCE) ;
DROP DATABASE
postgres=# DROP DATABASE postgres WITH (FORCE) ;
ERROR:  cannot drop the currently open database

3.2.11 EXPLAIN ANALYZE

The following extensions have been implemented in the EXPLAIN ANALYZE statement.

PDF page 44

The output of cache information

By specifying BUFFERS, the status of the shared buffer when creating an execution plan is now output.

Example 39 EXPLAIN (ANALYZE, BUFFERS) statement
postgres=> EXPLAIN (ANALYZE, BUFFERS) SELECT * FROM data1 WHERE c1
= 10000 ;
                                   QUERY PLAN
-----------------------------------------------------------------
Index Scan using idx1_data1 on data1  (cost=0.42..8.44 rows=1
width=16) (actual time=0.029..0.030 rows=1 loops=1)
   Index Cond: (c1 = 10000)
   Buffers: shared hit=1 read=3
 Planning:
   Buffers: shared hit=17 read=1
 Planning Time: 1.380 ms
 Execution Time: 0.044 ms
(7 rows)
If ANALYZE clause is not specified, the planner's buffer usage is output.
Example 40 EXPLAIN (BUFFERS) statement
postgres=> EXPLAIN (BUFFERS) SELECT * FROM data1 WHERE c1=100 ;
                                QUERY PLAN
-----------------------------------------------------------------
 Index Scan using idx1_data1 on data1  (cost=0.43..19.68 rows=4
width=16)
   Index Cond: (c1 = 100)
 Planning:
   Buffers: shared hit=93 read=3
(4 rows)
Output sort information

The output of the EXPLAIN ANALYZE statement when executing a parallel query has changed. JIT function information is now output for each worker. Also, the output of sort information has changed.

PDF page 45

Example 41 Output of the EXPLAIN ANALYZE statement (JIT)
postgres=> EXPLAIN (ANALYZE, VERBOSE) SELECT * FROM data1 WHERE c1 < 10000 ;
                                                             QUERY PLAN
-----------------------------------------------------------------------------
 Gather
(cost=0.00..107056.40
rows=9188
width=12)
(actual
time=12.326..410.535 rows=9999 loops=1)
   Output: c1, c2
   Workers Planned: 2
   Workers Launched: 2
   ->  Parallel Seq Scan on public.data1  (cost=0.00..106137.60 rows=3828
width=12) (actual time=228.802..360.446 rows=3333 loops=3)
         Output: c1, c2
         Filter: (data1.c1 < '10000'::numeric)
         Rows Removed by Filter: 3330000
         Worker 0:  actual time=330.989..330.989 rows=0 loops=1
           JIT:
             Functions: 2
             Options: Inlining false, Optimization false, Expressions true,
Deforming true
             Timing: Generation 2.370 ms, Inlining 0.000 ms, Optimization 1.562
ms, Emission 25.957 ms, Total 29.889 ms
         Worker 1:  actual time=343.317..343.317 rows=0 loops=1
           JIT:
             Functions: 2
             Options: Inlining false, Optimization false, Expressions true,
Deforming true
             Timing: Generation 1.655 ms, Inlining 0.000 ms, Optimization 1.265
ms, Emission 18.974 ms, Total 21.894 ms
…

PDF page 46

Example 42 Output of the EXPLAIN ANALYZE statement (Sort)
postgres=> EXPLAIN (ANALYZE, VERBOSE) SELECT * FROM data1 ORDER BY c1 ;
                                                                  QUERY PLAN
----------------------------------------------------------------------------
Gather Merge  (cost=752323.83..1726047.95 rows=8345624 width=12) (actual
time=1471.139..4003.430 rows=10000000 loops=1)
   Output: c1, c2
   Workers Planned: 2
   Workers Launched: 2
   ->  Sort  (cost=751323.81..761755.84 rows=4172812 width=12) (actual
time=1328.103..1849.320 rows=3333333 loops=3)
         Output: c1, c2
         Sort Key: data1.c1
         Sort Method: external merge  Disk: 74864kB
         Worker 0:  actual time=1466.829..1989.939 rows=3335746 loops=1
           Sort Method: external merge  Disk: 75096kB
         Worker 1:  actual time=1360.627..1885.937 rows=3338879 loops=1
           Sort Method: external merge  Disk: 75168kB
         ->  Parallel Seq Scan on public.data1  (cost=0.00..149837.12
rows=4172812 width=12) (actual time=60.222..279.624 rows=3333333 loops=3)
               Output: c1, c2
               Worker 0:  actual time=58.974..280.404 rows=3335746 loops=1
               Worker 1:  actual time=58.948..277.968 rows=3338879 loops=1
 Planning Time: 0.056 ms
 Execution Time: 4261.051 ms
(18 rows)
The output of WAL information

If the WAL option is specified in the EXPLAIN ANALYZE statement, the generated WAL information will be output.

PDF page 47

Example 43 EXPLAIN (ANALYZE, WAL) statement
postgres=> EXPLAIN (ANALYZE, WAL) DELETE FROM data1 WHERE c1 = 20000 ;
                                                QUERY PLAN
--------------------------------------------------------------------
Delete on data1  (cost=0.00..17906.00 rows=1 width=6) (actual
time=56.817..56.818 rows=0 loops=1)
   WAL: records=2 fpi=1 bytes=8237
   ->  Seq Scan on data1  (cost=0.00..17906.00 rows=1 width=6) (actual
time=1.269..56.800 rows=1 loops=1)
         Filter: (c1 = 20001)
         Rows Removed by Filter: 999998
         WAL: records=1 fpi=1 bytes=8183
 Planning Time: 0.085 ms
 Execution Time: 56.843 ms
(8 rows)

3.2.12 INSERT

Specifying a value using the INSERT OVERRIDING USER VALUE statement for an IDENTITY column with the GENERATED ALWAYS clause specified is now allowed. However, the value specified for the IDENTITY column is ignored.

Example 44 INSERT OVERRIDING USER VALUE statement
postgres=> CREATE TABLE gen1(c1 INT GENERATED ALWAYS AS IDENTITY,
c2 VARCHAR(10)) ;
CREATE TABLE
postgres=> INSERT INTO gen1 OVERRIDING USER VALUE VALUES
(100, 'data1') ;
INSERT 0 1
postgres=> SELECT * FROM gen1 ;
 c1 |  c2
----+-------
  1 | data1
(1 row)

PDF page 48

3.2.13 JSON

The following JSON related features have been added.

Allow Unicode escape

Unicode escapes are now allowed for character literals and identifiers, even if the server’s character encoding is not UTF-8.

Datetime method of jsonpath

The datetime method that converts a character string to a date/time type has been added.

Example 45 datetime method
postgres=> SELECT
jsonb_path_query('"27-09-2020"', '$.datetime("dd-mm-yyyy")') ;
 jsonb_path_query
------------------
 "2020-09-27"
(1 row)

3.2.14 MAX/MIN pg_lsn

MAX / MIN function can be used for pg_lsn data type.

Example 46 MAX(pg_lsn) function
postgres=# SELECT MIN(flush_lsn), MAX(flush_lsn)
FROM pg_stat_replication ;
    min    |    max
-----------+-----------
 0/94FD1E0 | 0/94FD1E0
(1 row)

3.2.15 ROW

ROW can now be directly extracted fields in some data types from the expressions.

PDF page 49

Example 47 Extracting values from ROW expressions
postgres=> SELECT (ROW(2, 3.1)).f1, (ROW(4, 5.1)).f2 ;
f1 | f2
----+-----
  2 | 5.1
(1 row)

3.2.16 SELECT FETCH FIRST WITH TIES

The WITH TIES clause can be specified in the SELECT FETCH FIRST statement. It outputs the same number of tuples as the number of tuples specified in the ROWS clause.

Example 48 SELECT FETCH FIRST WITH TIES statement
postgres=> SELECT * FROM data1 WHERE c1 < 4 ORDER BY c1
FETCH FIRST 2 ROWS ONLY ;
 c1 |  c2
----+-------
  1 | data1
  1 | data1
(2 rows)
postgres=> SELECT * FROM data1 WHERE c1 < 4 ORDER BY c1
FETCH FIRST 2 ROWS WITH TIES ;
 c1 |  c2
----+-------
  1 | data1
  1 | data1
  1 | data1
(3 rows)

3.2.17 VACUUM PARALLEL

VACUUM statements for indexes can now be parallelized. Parallelism works by default, and the degree of parallelism is determined by the number of indexes created on the table. To specify the degree of parallelism, specify a value between 0 and 1024 in the PARALLEL clause. This feature does not work with automatic VACUUM.

PDF page 50

Example 49 PARALLEL specification of VACUUM statement
postgres=> VACUUM (PARALLEL 4, VERBOSE) data1 ;
INFO:  vacuuming "public.data1"
INFO:  launched 2 parallel vacuum workers for index vacuuming (planned: 2)
INFO:  scanned index "idx3_data1" to remove 1000 row versions by parallel
vacuum worker
DETAIL:  CPU: user: 0.39 s, system: 0.11 s, elapsed: 12.45 s
INFO:  scanned index "idx2_data1" to remove 1000 row versions by parallel
vacuum worker
DETAIL:  CPU: user: 0.39 s, system: 0.12 s, elapsed: 12.56 s
INFO:  scanned index "idx1_data1" to remove 1000 row versions
DETAIL:  CPU: user: 0.47 s, system: 0.17 s, elapsed: 53.26 s
INFO:  "data1": removed 1000 row versions in 1000 pages
…

3.2.18 Operator <->

Distance operators (<->) can now be used between several data types. GiST index and SP-GiST index can be used in box_ops operator class.

Example 50 Operator &lt;-&gt;
postgres=> CREATE TABLE point_tbl(f1 point) ;
CREATE TABLE
postgres=> CREATE TABLE box_tbl(f1 box) ;
CREATE TABLE
postgres=> SELECT p.f1, b.f1, p.f1 <-> b.f1 AS dist_pb,
b.f1 <-> p.f1 AS dist_bp FROM point_tbl p, box_tbl b ;
f1 | f1 | dist_pb | dist_bp
----+----+---------+---------
(0 rows)

3.2.19 Functions

The following functions have been added/enhanced.

PDF page 51

Gen_random_uuid

The gen_random_uuid function has been provided to generate a random UUID.

Example 51 gen_random_uuid function
postgres=> SELECT gen_random_uuid() ;
           gen_random_uuid
--------------------------------------
 3759712a-e7f6-4348-b62c-85535c831e7e
(1 row)
Date format

FF1 to FF6 format patterns defined in SQL Standard 2016 have been added to the date format.

Example 52 Date format
postgres=> SELECT TO_CHAR(current_timestamp, 'HH:MI:SS.FF6') ;
     to_char
-----------------
 01:39:56.077670
(1 row)
PatternOutput valueNote
FF11/10th of a second.
FF21/100 second.
FF3Millisecond .
FF41/10 ms.
FF51/100 ms.
FF6Microsecond .
Table 21 Added format pattern

The pattern SSSSS is now an alias for SSSS.

Min_scale, trim_scale

The min_scale function obtains the minimum number of digits from the NUMERIC type, excluding trailing zeros after the decimal point. The trim_scale function gets the value of the NUMERIC type excluding trailing zeros after the decimal point.

PDF page 52

Example 53 min_scale, trim_scale function
postgres=> SELECT trim_scale(1.234000::numeric),
min_scale(1.234000::numeric) ;
 trim_scale | min_scale
------------+-----------
      1.234 |         3
(1 row)
Jsonb_set_lax

The jsonb_set_lax function is the same as the jsonb_set function except that a null_value_treatment parameter (text type) has been added. The following values can be specified for this parameter.

ValueDescriptionNote
use_json_nullraise_exceptiondelete_keyreturn_targetReturn NULL. Raise exception. Delete key. Return target.default value
Table 22 null_value_treatment parameter value
Example 54 jsonb_set_lax function
postgres=> SELECT
jsonb_set_lax('[{"f1":1,"f2":null},2,null,3]',
'{0,f1}', null) ;
             jsonb_set_lax
----------------------------------------
 [{"f1": null, "f2": null}, 2, null, 3]
(1 row)
Gcd, lcm

The lcm function, which returns the least common multiple, and the gcd function, which returns the greatest common divisor, have been added. Specify two numbers of the same type in the parameter. If both parameters are 0, these functions return 0.

PDF page 53

Example 55 gcd, lcm function
postgres=> SELECT gcd(1071, 462), lcm(1071, 46) ;
 gcd |  lcm
-----+-------
  21 | 49266
(1 row)
Get_bit / set_bit

The offset parameter of the get_bit / set_bit function that specifies the bytea type has been changed from integer type to bigint type.

Unicode normalization

The normalize function that performs UNICODE normalization and the ‘is_normalized’ function that checks whether it is normalized has been implemented. You can also use the ‘is normalized’ syntax to check the specified text. Syntax

text is [not] [form] normalized

In the [form] part, NFC (default), NFD, NFKC, or NFKD can be specified.

Example 56 is normalized syntax
postgres=> SELECT '㋿' IS NFKD NORMALIZED ;
 is_normalized
---------------
 f
(1 row)
postgres=> SELECT normalize('㋿', NFKD) ;
 normalize
-----------
 令和
(1 row)

PDF page 54

Pg_stat_reset_slru

This function resets the counter value in the pg_stat_slru catalog. If NULL is specified for the parameter of this function, all counters will be reset. By specifying the counter name (’name’ column of pg_stat_slru catalog) to this function, the specified counter is reset.

Example 57 is normalized syntax
postgres=# SELECT pg_stat_reset_slru('subtrans') ;
pg_stat_reset_slru
--------------------
(1 row)