This section explains new features related to SQL statements.
3.2.1 ALTER OPERATOR
The following attributes can now be altered by the ALTER OPEARTOR statement. Previously, these could only be specified in the CREATE OPERATOR statement. However, once a flag is set, it cannot be changed. [2b5154b]
COMMUTATOR
NEGATOR
HASHES
MERGES
Example 36Attribute modification by ALTER OPERATOR statement
postgres=# CREATE OR REPLACE FUNCTION alter_op_test_fn_bool_real(boolean, real)
RETURNS boolean AS $$ SELECT NULL::BOOLEAN; $$ LANGUAGE SQL IMMUTABLE ;
CREATE FUNCTION
postgres=# CREATE OPERATOR === (
LEFTARG = boolean,
RIGHTARG = real,
PROCEDURE = alter_op_test_fn_bool_real) ;
CREATE OPERATOR
postgres=# ALTER OPERATOR === (boolean, real) SET (MERGES = false) ;
ALTER OPERATOR
postgres=# ALTER OPERATOR === (boolean, real) SET (HASHES = false) ;
ALTER OPERATOR
3.2.2 ALTER SYSTEM
A parameter containing module names (e.g., foo.bar) used by extensions, etc., can now be specified in the ALTER SYSTEM statement. In the previous version, only parameters for which a SET statement had been executed in the past could be set with the ALTER SYSTEM statement. [2d870b4]
PDF page 45
Example 37ALTER SYSTEM statement to change parameters for extensions
postgres=# ALTER SYSTEM SET foo.bar = 100 ;
ALTER SYSTEM
postgres=# SELECT pg_reload_conf() ;
pg_reload_conf
----------------
t
(1 row)
postgres=# SHOW foo.bar ;
foo.bar
---------
100
(1 row)
3.2.3 ALTER TABLE
The following enhancements have been implemented to the ALTER TABLE statement.
Change to default access method
To change to the access method specified in the parameter default_table_access_method, it is now possible to specify DEFAULT for the access method name. [d61a6ca]
Syntax 3 - ALTER TABLE SET ACCESS METHOD statement
ALTERTABLEtable_nameSETACCESSMETHODDEFAULT
Example 38Change access method
postgres=> ALTER TABLE data1 SET ACCESS METHOD DEFAULT ;
ALTER TABLE
Change Generate Column
The formulas for generated columns (GENERATE clause) can now be changed. If the formula for the column is changed, the existing data in the table will be recalculated. [5d06e99]
postgres=> CREATE TABLE data1(c1 INT, c2 INT GENERATED ALWAYS AS
(c1 * 2) STORED) ;
CREATE TABLE
postgres=> INSERT INTO data1(c1) VALUES (100) ;
INSERT 0 1
postgres=> SELECT * FROM data1 ;
c1 | c2
-----+-----
100 | 200
(1 row)
postgres=> ALTER TABLE data1 ALTER COLUMN c2 SET EXPRESSION AS (c1 * 200) ;
ALTER TABLE
postgres=> SELECT * FROM data1 ;
c1 | c2
-----+-------
100 | 20000
(1 row)
Statistics Settings
It is now possible to use DEFAULT when setting the volume of statistics stored in a single column to the parameter default_statistics_target. In previous versions, -1 was specified. When using the default value, the attstattarget column in the pg_attribute catalog will be set to the NULL value. Previously, the NOT NULL constraint was specified for this column. [4f62250, 5567996]
postgres=> ALTER TABLE data1 ALTER COLUMN c1 SET STATISTICS 110 ;
ALTER TABLE
postgres=> SELECT attname, attstattarget FROM pg_attribute WHERE attrelid=
(SELECT oid FROM pg_class WHERE relname='data1') AND attname='c1' ;
attname | attstattarget
---------+---------------
c1 | 110
(1 row)
postgres=> ALTER TABLE data1 ALTER COLUMN c1 SET STATISTICS DEFAULT ;
ALTER TABLE
postgres=> SELECT attname,attstattarget FROM pg_attribute WHERE attrelid=
(SELECT oid FROM pg_class WHERE relname='data1') AND attname='c1' ;
attname | attstattarget
---------+---------------
c1 | null
(1 row)
3.2.4 CLUSTER
The syntax for enclosing options in parentheses (()) is now permitted when the table name is omitted. This enhancement supports the same syntax as the REINDEX and VACUUM statements. [cdaedfc]
Example 41CLUSTER with VERBOSE clause
postgres=> CLUSTER (VERBOSE) ;
INFO: clustering "public.data1" using index scan on "data1_pkey1"
INFO: "public.data1": found 0 removable, 1000000 nonremovable row versions in
5406 pages
DETAIL: 0 dead row versions cannot be removed yet.
CPU: user: 0.22 s, system: 0.00 s, elapsed: 0.25 s.
CLUSTER
3.2.5 COPY
The following new features have been implemented in the COPY statement.
PDF page 48
FORCE_NULL option
An asterisk (*) can now be specified in the FORCE_NULL and FORCE_NOT_NULL options of the COPY statement. [f6d4c9c]
Example 42Specifying the FORCE_NULL * option
postgres=> COPY data1 FROM stdin WITH (FORMAT csv, FORCE_NULL *) ;
Enter data to be copied followed by a newline.
End with a backslash and a period on a line by itself, or an EOF signal.
>> 100,data1
>> 200,""
>> 300,data3
>> \.
COPY 3
postgres=> SELECT * FROM data1 ;
c1 | c2
-----+-------
100 | data1
200 | null
300 | data3
(3 rows)
ON_ERROR option
The ON_ERROR option has been added to the COPY FROM statement. This option changes the behavior when an error occurs. The following values can be specified for this option. [9e2d870, b725b7e, a6d0fa5]
Value
Description
stop
Stop processing when an error occurs.
ignore
Ignore the occurrence of data conversion errors and continue the processing.
Table 25Available value for the ON_ERROR option
Even if this option is set to ‘ignore’, the COPY statement will terminate abnormally if a constraint violation (primary key constraint or CHECK constraint) occurs.
PDF page 49
Example 43Specifying the ON_ERROR option
postgres=> CREATE TABLE data1(c1 INT, c2 VARCHAR(10)) ;
CREATE TABLE
postgres=> COPY data1 FROM STDIN WITH (FORMAT csv, ON_ERROR ignore) ;
Enter data to be copied followed by a newline.
End with a backslash and a period on a line by itself, or an EOF signal.
>> 100,data1
>> 200,data2
>> ABC,data3
>> 400,data4
>> \.
NOTICE: 1 row were skipped due to data type incompatibility
COPY 3
postgres=> SELECT * FROM data1 ;
c1 | c2
-----+--------
100 | data1
200 | data2
400 | data4
(3 rows)
LOG_VERBOSITY option
The LOG_VERBOSITY option has been added to the COPY statement to change the level of log output. The LOG_VERBOSITY option controls the output of errors generated by a COPY FROM statement with the ON_ERROR option set to ‘ignore’. The possible values are as follows. [f5a2278]
Value
Description
Note
default
Output standard log level.
Default
verbose
Output verbose log level.
Table 26Available value for the LOG_VERBOSITY option
PDF page 50
Example 44Specifying the LOG_VERBOSITY option
postgres=> COPY data1 FROM STDIN WITH (FORMAT csv, ON_ERROR ignore,
LOG_VERBOSITY default) ;
Enter data to be copied followed by a newline.
End with a backslash and a period on a line by itself, or an EOF signal.
>> 100,data1
>> 200,data2
>> ABC,data3
>> 400,data4
>> \.
NOTICE: 1 row was skipped due to data type incompatibility
COPY 3
postgres=> TRUNCATE TABLE data1 ;
TRUNCATE TABLE
postgres=> COPY data1 FROM STDIN WITH (FORMAT csv, ON_ERROR ignore,
LOG_VERBOSITY verbose) ;
Enter data to be copied followed by a newline.
End with a backslash and a period on a line by itself, or an EOF signal.
>> 100,data1
>> 200,data2
>> ABC,data3
>> 400,data4
>> \.
NOTICE: skipping row due to data type incompatibility at line 3 for column c1:
"ABC"
NOTICE: 1 row was skipped due to data type incompatibility
COPY 3
3.2.6 CREATE TABLE
The access method can now be specified for the partitioned table, using the CREATE TABLE USING or ALTER TABLE SET ACCESS METHOD statements. The specified value will be used as the default value for the access method for the partitions created under the partitioned table. The existing partitions will not be modified. [374c7a2]
PDF page 51
Syntax 6 - CREATE TABLE PARTITION BY USING statement
postgres=> CREATE TABLE part1 (c1 INT PRIMARY KEY, c2 VARCHAR(10))
PARTITION BY RANGE(c1) USING heap ;
CREATE TABLE
postgres=> ALTER TABLE part1 SET ACCESS METHOD DEFAULT ;
ALTER TABLE
3.2.7 EXPLAIN
The following new features have been added to the EXPLAIN statement.
Enhancements to JSON Format
Local I/O Read Time" and “Local I/O Write Time” are now output in JSON format. To enable this output, the parameter track_io_timing must be set to ‘on’. [295c36c]
The MEMORY option has been added to the EXPLAIN statement to output the memory settings when creating an execution plan. The EXPLAIN statement with the MEMORY clause outputs information about the amount of memory allocated (allocated) and the amount of memory actually used (used). [5de890e]
Example 47EXPLAIN statement with MEMORY clause
postgres=> EXPLAIN (MEMORY) SELECT COUNT(*) FROM data1 ;
QUERY PLAN
---------------------------------------------------------------------
Aggregate (cost=17906.00..17906.01 rows=1 width=8)
-> Seq Scan on data1 (cost=0.00..15406.00 rows=1000000 width=0)
Planning:
Memory: used=9kB allocated=16kB
(4 rows)
SERIALIZE option
The SERIALIZE clause has been added that outputs information on the amount of data and time output to the client by the query execution. This option is used in conjunction with the ANALYZE clause. The following values can be added to the option. [0628670]
Value
Description
Note
none
Do not output serialization information
Default
textbinary
Output in text format Output in binary format
Default for SERIALIZE only
Table 27Available value for the SERIALIZE option
PDF page 53
Example 48EXPLAIN statement with SERIALIZE clause
postgres=> EXPLAIN (ANALYZE, SERIALIZE BINARY) SELECT * FROM data1 ;
QUERY PLAN
-----------------------------------------------------------------------
Seq Scan on data1 (cost=0.00..15406.00 rows=1000000 width=10) (actual
time=0.005..36.797 rows=1000000 loops=1)
Planning Time: 0.056 ms
Serialization: time=84.192 ms output=18555kB format=binary
Execution Time: 154.041 ms
(4 rows)
3.2.8 MERGE
The following new features have been added to the MERGE statement.
BY SOURCE clause
The condition specification ``WHEN NOT MATCHED BY SOURCE’’ has been added to the MERGE statement. This condition operates on tuples that do not exist in the source table but exist in the target table. The UPDATE, DELETE, and DO NOTHING clauses can be specified as the action for the target tuple. [0294df2]
PDF page 54
Example 49Specifying the BY SOURCE clause
postgres=> SELECT * FROM src1 ;
c1 | c2
-----+---------
100 | source1
200 | source2
(2 rows)
postgres=> SELECT * FROM tgt1 ;
c1 | c2
-----+---------
100 | target1
300 | target3
(2 rows)
postgres=> MERGE INTO tgt1 AS t USING src1 AS s ON s.c1 = t.c1
WHEN NOT MATCHED THEN INSERT VALUES (s.c1, s.c2)
WHEN NOT MATCHED BY SOURCE THEN UPDATE SET c2='not matched' ;
MERGE 2
postgres=> SELECT * FROM tgt1 ;
c1 | c2
-----+-------------
100 | target1
200 | source2
300 | not matched
(3 rows)
The traditional WHEN NOT MATCHED clause is treated as a statement with the BY TARGET
clause omitted.
RETURNING clause
The MERGE statements can now specify the RETURNING clause to return the data of updated tuples, and a special function merge_action has been added to determine the type (INSERT/UPDATE/DELETE) of the tuple returned by the RETURNING clause. The output of the RETURNING clause is available as a source relation like any other DML. [c649fa2]
PDF page 55
Example 50MERGE statement with the RETURNING clause
postgres=> CREATE TABLE src1(c1 INT PRIMARY KEY, c2 VARCHAR(10)) ;
CREATE TABLE
postgres=> INSERT INTO src1 VALUES (100, 'data1'),(300, 'data3') ;
INSERT 0 2
postgres=> CREATE TABLE tgt1(c1 INT PRIMARY KEY, c2 VARCHAR(10)) ;
CREATE TABLE
postgres=> INSERT INTO tgt1 VALUES (100, 'data1'),(200, 'data2') ;
INSERT 0 2
postgres=> MERGE INTO tgt1 t
USING src1 s ON s.c1 = t.c1
WHEN MATCHED THEN
UPDATE SET c2 = 'updated'
WHEN NOT MATCHED THEN
INSERT (c1, c2) VALUES (s.c1, s.c2)
RETURNING merge_action(), t.* ;
merge_action | c1 | c2
--------------+-----+---------
UPDATE | 100 | updated
INSERT | 300 | data3
(2 rows)
MERGE 2
MERGE statement for updatable views
The updatable views can now be specified as targets in the MERGE statement. [5f2e179]
PDF page 56
Example 51MERGE statement for updatable views
postgres=> CREATE TABLE src1(c1 INT PRIMARY KEY, c2 VARCHAR(10)) ;
CREATE TABLE
postgres=> INSERT INTO src1 VALUES (generate_series(1, 10), 'data1') ;
INSERT 0 10
postgres=> CREATE TABLE tgt1(c1 INT PRIMARY KEY, c2 VARCHAR(10)) ;
CREATE TABLE
postgres=> CREATE VIEW view1 AS SELECT * FROM tgt1 ;
CREATE VIEW
postgres=> MERGE INTO view1 AS v1 USING src1 AS s1
ON v1.c1 = s1.c1
WHEN NOT MATCHED THEN
INSERT (c1, c2) VALUES (s1.c1, s1.c2) ;
MERGE 10
3.2.9 PL/pgSQL
The following enhancements have been implemented in PL/pgSQL.
Addition of data type attributes
The array declaration can now be specified for the %TYPE and %ROWTYPE attributes of variables. [5e8674d]
Example 52%TYPE attribute and %ROWTYPE attribute
postgres=> CREATE OR REPLACE FUNCTION func1() RETURNS TEXT AS $$
DECLARE
val1 pg_catalog.pg_class%ROWTYPE[];
val2 pg_node_tree;
val3 val2%TYPE[];
BEGIN
…
END; $$ LANGUAGE plpgsql;
CREATE FUNCTION
PDF page 57
INTERNAL Sub-Transaction
The BeginInternalSubTransaction API can now be run in parallel mode. In the example below, PostgreSQL 16 executes a PL/pgSQL function with PARALLEL SAFE specified in parallel mode, and an error occurs. [0075d78]
Example 53Execution error occurs in PostgreSQL 16
postgres=> CREATE FUNCTION zero_divide() RETURNS INT AS $$
DECLARE v INT := 0;
BEGIN
RETURN 10 / v;
END;
$$ LANGUAGE plpgsql PARALLEL SAFE ;
CREATE FUNCTION
postgres=> CREATE FUNCTION error_trap_test() RETURNS TEXT AS $$
BEGIN
PERFORM zero_divide();
RETURN 'no error detected!';
EXCEPTION WHEN division_by_zero THEN
RETURN 'division_by_zero detected';
END;
$$ LANGUAGE plpgsql PARALLEL SAFE ;
CREATE FUNCTION
postgres=> SET debug_parallel_query = on ;
SET
postgres=> SELECT error_trap_test() ;
ERROR: cannot start subtransactions during a parallel operation
CONTEXT: PL/pgSQL function error_trap_test() line 2 during statement block
entry
3.2.10 Data Types
The following enhancements have been implemented for PostgreSQL 17 data types.
INTERVAL
The +/-Infinity is now supported for the INTERVAL type. [519fc1b]
The AT LOCAL clause can now be specified to convert timestamps with time zones to local time zones. [97957fd]
Example 55Specify the AT LOCAL clause
postgres=> SHOW timezone ;
TimeZone
------------
Asia/Tokyo
(1 row)
postgres=> SELECT CAST('1978-07-07 19:38 America/New_York'
AS TIMESTAMP WITH TIME ZONE) AT LOCAL ;
timezone
---------------------
1978-07-08 08:38:00
(1 row)
RANGE
The expressions containing constant range values can now be directly compared for the range operators ‘<@’ and ‘@>’. [075df6b]
PDF page 59
Example 56Behavior of PostgreSQL 17
postgres=> EXPLAIN (VERBOSE, COSTS OFF)
SELECT CURRENT_DATE <@ DATERANGE 'empty' ;
QUERY PLAN
-----------------
Result
Output: false
(2 rows)
The timestamp_tz method cannot be executed on the jsonb_path_query function because the time
zone is not IMMUTABLE.
PDF page 62
Example 60Timestamp with Time zone
postgres=> SELECT
jsonb_path_query('"2024-09-27 12:34:56"', '$.timestamp_tz()') ;
ERROR: cannot convert value from timestamp to timestamptz without time zone
usage
HINT: Use *_tz() function for time zone support.
postgres=> SELECT
jsonb_path_query_tz('"2024-09-27 12:34:56"', '$.timestamp_tz()') ;
jsonb_path_query_tz
-----------------------------
"2024-09-27T03:34:56+00:00"
JSON Query
The functions have been added to query within JSON data. The JSON_EXISTS function returns true if the item was generated using the PASSING clause. The JSON_QUERY and JSON_VALUE functions return the result of applying the PASSING clause from the specified JSON data. [6185c97]
postgres=> SELECT * FROM JSON_TABLE(jsonb '123', '$'
COLUMNS (item int PATH '$', foo int)) bar ;
item | foo
------+-----
123 |
(1 row)
postgres=> SELECT * FROM JSON_TABLE(jsonb '"a"', '$'
COLUMNS (a int PATH 'strict $.a' DEFAULT 1 ON EMPTY DEFAULT 2 ON ERROR)) jt ;
a
---
2
(1 row)
Jsonb_populate_record_valid function
The jsonb_populate_record_valid function has been added. This function returns true if the jsonb_populate_record function does not generate an error for the specified JSON object, or false if it does. [1edb3b4]
Example 63Execute jsonb_populate_record_valid function
postgres=> CREATE TYPE jsb_char2 AS (a char(2)) ;
CREATE TYPE
postgres=> SELECT jsonb_populate_record_valid(NULL::jsb_char2,
'{"a": "aaa"}') ;
jsonb_populate_record_valid
-----------------------------
f
(1 row)
PDF page 65
3.2.12 Functions
The following functions have been added/enhanced.
Pg_promote
The error message is now output if sending a signal to the postmaster process fails. [f593c55]
The function pg_basetype has been added. This function returns the base type for a given domain. [b154d8a]
Syntax 12 - Pg_basetype function
regtypepg_basetype(regtype)
Example 64Execute pg_basetype function
postgres=> CREATE DOMAIN mytext AS text ;
CREATE DOMAIN
postgres=> CREATE DOMAIN mytext_child AS mytext ;
CREATE DOMAIN
postgres=> SELECT pg_basetype('mytext'::regtype) ;
pg_basetype
-------------
text
(1 row)
postgres=> SELECT pg_basetype('mytext_child'::regtype) ;
pg_basetype
-------------
text
(1 row)
PDF page 66
Random
The functions have been added to generate random numbers that have “minimum value ≤ random ≤ maximum value” given a minimum and maximum value. [e634132]
postgres=> SELECT TO_BIN(100) bin, TO_OCT(100) oct ;
bin | oct
---------+-----
1100100 | 144
(1 row)
To_regtypemod
The to_regtypemod function has been added. This function retrieves the typemod information of a datatype from a datatype string. This information can be used with the format_type function, etc. [1218ca9]
The to_timestamp function can now specify TZ and OF in the format string. In previous versions, it was only available in the to_char function. [8ba6fdf]
Example 68Execute to_timestamp function with TZ/OF
The basic functions related to UNICODE versions have been added. The unicode_version function outputs the UNICODE version used by the database cluster, and the icu_unicode_version function outputs the UNICODE version used by the ICU library. The unicode_assigned function verifies whether the specified string is assigned to UNICODE. [a02b37f]
postgres=> SELECT unicode_version() ;
-[ RECORD 1 ]---+-----
unicode_version | 15.1
postgres=> SELECT icu_unicode_version() ;
-[ RECORD 1 ]-------+-----
icu_unicode_version | 10.0
postgres=> SELECT unicode_assigned('ABC') ;
-[ RECORD 1 ]----+--
unicode_assigned | t
The unicode_assigned function can be executed only when the database encoding is UTF-8. The
following error will occur if it is executed in a database with encoding set to EUC_JP.
Example 70Execute on EUC_JP encoded database
postgres=> SELECT unicode_assigned('ABC') ;
ERROR: Unicode categorization can only be performed if server encoding is UTF8
XMLText
The XMLText function of the XML standard (SQL/XML X038) has been added. This function converts text information into XML text nodes. The –with-libxml option is required at PostgreSQL build time to execute this function. [526fe0d]
The newly added pg_column_toast_chunk_id function returns the chunk ID of TOAST data. [d1162cf]
Syntax 19 - Pg_column_toast_chunk_id functions
oidpg_column_toast_chunk_id(any)
PDF page 70
Example 73Execute pg_column_toast_chunk_id function
postgres=> CREATE TABLE data1 (col1 TEXT, col2 TEXT STORAGE EXTERNAL) ;
CREATE TABLE
postgres=> INSERT INTO data1 VALUES ('A', repeat('B', 8192)) ;
INSERT 0 1
postgres=> SELECT pg_column_toast_chunk_id(col2) FROM data1 ;
-[ RECORD 1 ]------------+------
pg_column_toast_chunk_id | 16412
Pg_stat_reset_shared
The behavior of the pg_stat_reset_shared function has changed. If the parameter to the pg_stat_reset_shared function is omitted or specified as NULL, all statistics counters are reset. Also, the pg_stat_reset_shared function parameter can now contain the string ‘slru’ to reset the pg_stat_slru view. [23c8c0c, 2e8a0ed]
If the parameter of the pg_stat_reset_slru function is omitted, all SLRU statistics counters are reset. [e5cca62]
Syntax 21 - Pg_stat_reset_slru functions
voidpg_stat_reset_slru(targettextDEFAULTNULL)
The element name specified as a parameter to this function has been changed. Entering a string other than the changed name will reset the “other” item. [bcdfa5f]
Faster execution plans are now chosen in the following cases.
GROUP BY optimization
If the GROUP BY clause with multiple columns is not related to the sort order, the sorting process can now be reduced by swapping the columns. This behavior is enabled by setting the parameter enable_group_by_reordering to ‘on’ (default ‘on’). [0452b46]
Example 74Execution Plan on PostgreSQL 17
postgres=> EXPLAIN (COSTS OFF) SELECT COUNT(*) FROM data1 GROUP BY col2, col1 ;
QUERY PLAN
-------------------------------------------------
GroupAggregate
Group Key: col1, col2
-> Index Only Scan using idx1_data1 on data1
(3 rows)
PDF page 72
Example 75Execution Plan on PostgreSQL 16
postgres=> EXPLAIN (COSTS OFF) SELECT COUNT(*) FROM data1 GROUP BY col2, col1 ;
QUERY PLAN
-------------------------------
GroupAggregate
Group Key: col2, col1
-> Sort
Sort Key: col2, col1
-> Seq Scan on data1
(5 rows)
UNION optimization
The Merge Append plan can now be used for lookups that contain a UNION clause in the subquery. Previously, Sort plan was required. [66c0185]
Example 76Execution Plan on PostgreSQL 17
postgres=> EXPLAIN (COSTS OFF) SELECT COUNT(*) FROM
(SELECT c1 FROM merge1 UNION SELECT c1 FROM merge2) ;
QUERY PLAN
---------------------------------------------------------------
Aggregate
-> Unique
-> Merge Append
Sort Key: merge1.c1
-> Index Only Scan using merge1_pkey on merge1
-> Index Only Scan using merge2_pkey on merge2
(6 rows)
PDF page 73
Example 77Execution Plan on PostgreSQL 16
postgres=> EXPLAIN (COSTS OFF) SELECT COUNT(*) FROM
(SELECT c1 FROM merge1 UNION SELECT c1 FROM merge2) ;
QUERY PLAN
--------------------------------------------
Aggregate
-> Unique
-> Sort
Sort Key: merge1.c1
-> Append
-> Seq Scan on merge1
-> Seq Scan on merge2
(7 rows)
Better IS [NOT] NULL Handling
The optimizations have been implemented to reduce unnecessary evaluation of IS NULL and IS NOT NULL clauses. [b262ad4]
Example 78Execution Plan on PostgreSQL 17
postgres=> \d data1
Table "public.data1"
Column | Type | Collation | Nullable | Default
--------+-----------------------+-----------+----------+---------
c1 | integer | | not null |
c2 | character varying(10) | | |
Indexes:
"data1_pkey" PRIMARY KEY, btree (c1)
postgres=> EXPLAIN SELECT COUNT(*) FROM data1 WHERE c1 IS NOT NULL ;
QUERY PLAN
---------------------------------------------------------------------
Aggregate (cost=17906.00..17906.01 rows=1 width=8)
-> Seq Scan on data1 (cost=0.00..15406.00 rows=1000000 width=0)
(2 rows)
PDF page 74
Example 79Execution Plan on PostgreSQL 16
postgres=> \d data1
Table "public.data1"
Column | Type | Collation | Nullable | Default
--------+-----------------------+-----------+----------+---------
c1 | integer | | not null |
c2 | character varying(10) | | |
Indexes:
"data1_pkey" PRIMARY KEY, btree (c1)
postgres=> EXPLAIN SELECT COUNT(*) FROM data1 WHERE c1 IS NOT NULL ;
QUERY PLAN
---------------------------------------------------------------------
Aggregate (cost=17906.00..17906.01 rows=1 width=8)
-> Seq Scan on data1 (cost=0.00..15406.00 rows=1000000 width=0)
Filter: (c1 IS NOT NULL)
(3 rows)
Better Parallel DISTINCT Processing
The Gather Merge plan is now available for parallel execution of SELECT statements with DISTINCT clauses. [7e0ade0]
Example 80Execution Plan on PostgreSQL 17
postgres=> EXPLAIN (COSTS OFF) SELECT DISTINCT c2 FROM data1 ;
QUERY PLAN
----------------------------------------------------
Unique
-> Gather Merge
Workers Planned: 2
-> Sort
Sort Key: c2
-> HashAggregate
Group Key: c2
-> Parallel Seq Scan on data1
(8 rows)
PDF page 75
Example 81Execution Plan on PostgreSQL 16
postgres=> EXPLAIN (COSTS OFF) SELECT DISTINCT c2 FROM data1 ;
QUERY PLAN
----------------------------------------------------
Unique
-> Sort
Sort Key: c2
-> Gather
Workers Planned: 2
-> HashAggregate
Group Key: c2
-> Parallel Seq Scan on data1
(8 rows)
GiST Index optimization
The Incremental Sort plan is now available for GiST and SP-GiST indexes. [625d5b3]
Example 82Execution Plan on PostgreSQL 17
postgres=> CREATE INDEX idx1_gist1 ON gist1 USING GIST(c1) ;
CREATE INDEX
postgres=> EXPLAIN (COSTS OFF) SELECT c1, c2, c1<->POINT(5,5) dist FROM gist1
ORDER BY dist, c2 LIMIT 1 ;
QUERY PLAN
--------------------------------------------------
Limit
-> Incremental Sort
Sort Key: ((c1 <-> '(5,5)'::point)), c2
Presorted Key: ((c1 <-> '(5,5)'::point))
-> Index Scan using idx1_data1 on data1
Order By: (c1 <-> '(5,5)'::point)
(6 rows)
PDF page 76
Example 83Execution Plan on PostgreSQL 16
postgres=> EXPLAIN (COSTS OFF) SELECT c1, c2, c1<->POINT(5,5) dist FROM data1
ORDER BY dist, c2 LIMIT 1 ;
QUERY PLAN
-------------------------------------------------
Limit
-> Sort
Sort Key: ((c1 <-> '(5,5)'::point)), c2
-> Seq Scan on data1
(4 rows)