More Than a Query Language: SQL in the 21st Century

@MarkusWinand • @ModernSQL

http://www.almaden.ibm.com/cs/people/chamberlin/sequel-1974.pdf More Than a Query Language: SQL in the 21st Century

@MarkusWinand • @ModernSQL

http://www.almaden.ibm.com/cs/people/chamberlin/sequel-1974.pdf Safe Harbour Statement … Instead of a Safe Harbour Statement … Instead of a Safe Harbour Statement … Take this Safe the Planet Statement: I’m traveling a lot for business and always aim for the most environmental friendly way to do so.

If I cannot avoid flying, I offset its climate impact at a transparent climate protection company. (currently atmosfair.de)

Picture: https://upload.wikimedia.org/wikipedia/commons/2/2c/North_America_from_low_orbiting_satellite_Suomi_NPP.jpg

1974 1992 SQL-92 — Tied to the Relational Idea SQL-92 — Tied to the Relational Idea

Relational Data Model ‣ “Atomic” types (domain)

SQL-92 — Tied to the Relational Idea

Relational Data Model ‣ “Atomic” types (domain)

Atom image: https://commons.wikimedia.org/wiki/File:Stylised_atom_with_three_Bohr_model_orbits_and_stylised_nucleus.png SQL-92 — Tied to the Relational Idea

Relational Data Model ‣ “Atomic” types (domain)

A B C

Atom image: https://commons.wikimedia.org/wiki/File:Stylised_atom_with_three_Bohr_model_orbits_and_stylised_nucleus.png SQL-92 — Tied to the Relational Idea

Relational Data Model ‣ “Atomic” types (domain)

A B C SQL-92 — Tied to the Relational Idea

Relational Data Model ‣ “Atomic” types (domain) ‣ Schema independent of processing purposes ‣ “Normalization”

A B C SQL-92 — Tied to the Relational Idea

Relational Data Model ‣ “Atomic” types (domain) ‣ Schema independent of processing purposes ‣ “Normalization”

A B C C D B E SQL-92 — Tied to the Relational Idea

Relational Data Model Relational Operations ‣ “Atomic” types (domain) ‣ Transform data for ‣ Schema independent of each particular processing purposes processing purposes ‣ “Normalization” ‣ JOIN, UNION, nesting, …

A B C C D B E A B C D E SQL-92 — Tied to the Relational Idea

Relational Data Model Relational Operations ‣ “Atomic” types (domain) ‣ Transform data for ‣ Schema independent of each particular processing purposes processing purposes ‣ “Normalization” ‣ JOIN, UNION, nesting, …

A B C D E A B C C D B E A B E SQL-92 — Tied to the Relational Idea

Relational Data Model Relational Operations ‣ “Atomic” types (domain) ‣ Transform data for ‣ Schema independent of each particular processing purposes processing purposes ‣ “Normalization” ‣ JOIN, UNION, nesting, …

A B C D E A B C C D B E C D E

A B E SQL-92 — Tied to the Relational Idea

Relational Data Model Relational Operations ‣ “Atomic” types (domain) ‣ Transform data for ‣ Schema independent of each particular processing purposes processing purposes ‣ “Normalization” ‣ JOIN, UNION, nesting, …

A B C D E A B C C D B E

A B E

C D E 1992 1999 https://www.wiscorp.com/DBMS_-_GreatNews-TheRelationalModelIsDead_-_paper_-_sam.pdf SQL:1999 — Escaping the Relational Cage

To say that these SQL:1999 extensions are mere “extended interpretations” of the relational data model is like saying that an intercontinental ballistic missile is merely an “extended interpretation” of a spear.

https://www.wiscorp.com/DBMS_-_GreatNews-TheRelationalModelIsDead_-_paper_-_sam.pdf SQL:1999 — Escaping the Relational Cage

To say that these SQL:1999 extensions are mere “extended interpretations” of the relational data model is like saying that an intercontinental ballistic missile is merely an “extended interpretation” of a spear.

With SQL/99 you can get the best of both worlds and of course, you can get the worst of both worlds. It’s up to the database practitioners to do the right thing. https://www.wiscorp.com/DBMS_-_GreatNews-TheRelationalModelIsDead_-_paper_-_sam.pdf SQL:1999 — Escaping the Relational Cage SQL:1999 — Escaping the Relational Cage

Relational Model?

SQL:1999 — Escaping the Relational Cage

Relational Model?

I was as confused as anyone else

Date on Database: Writings 2000-2006 Chris Date SQL:1999 — Escaping the Relational Cage

Relational Model? ? I was as confused as anyone else

Date on Database: Writings 2000-2006 Chris Date SQL:1999 — Escaping the Relational Cage

Relational Model?

I was as confused as anyone else ? By the early 1990s, however, I’d seen the light

Date on Database: Writings 2000-2006 Chris Date SQL:1999 — Escaping the Relational Cage

Relational Model?

I was as confused as anyone else ? By the early 1990s, however, I’d seen the light Domains Can Contain Anything! Date on Database: Writings 2000-2006 Chris Date SQL:1999 — Escaping the Relational Cage

Relational Model? ‣ Introduced rich types

I was as confused as anyone else ? By the early 1990s, however, I’d seen the light Domains Can Contain Anything! Date on Database: Writings 2000-2006 Chris Date SQL:1999 — Escaping the Relational Cage

Relational Model? A ‣ Introduced rich types

I was as confused as anyone else ? By the early 1990s, however, I’d seen the light Domains Can Contain Anything! Date on Database: Writings 2000-2006 Chris Date SQL:1999 — Escaping the Relational Cage

Relational Model? A B ‣ Introduced rich types [ , ] ‣ arrays [ ] [] I was as confused as anyone else ? By the early 1990s, however, I’d seen the light Domains Can Contain Anything! Date on Database: Writings 2000-2006 Chris Date SQL:1999 — Escaping the Relational Cage

Relational Model? A B C ‣ Introduced rich types [ , ] C D

‣ arrays C D ‣ Nested tables (multiset) [ ] [] C D I was as confused as anyone else ? By the early 1990s, however, I’d seen the light Domains Can Contain Anything! Date on Database: Writings 2000-2006 Chris Date SQL:1999 — Escaping the Relational Cage

Relational Model? A B C D ‣ Introduced rich types C D {x: , [ , ] y: } ‣ arrays C D {x: , ‣ Nested tables (multiset) [ ] y: } C D ‣ composite types (objects) {x: , [] y: } I was as confused as anyone else ? By the early 1990s, however, I’d seen the light Domains Can Contain Anything! Date on Database: Writings 2000-2006 Chris Date SQL:1999 — Escaping the Relational Cage

Relational Model? Non-Relational Operations ‣ Introduced rich types ‣ Introduced recursive ‣ arrays queries that process ‣ Nested tables (multiset) their own output ‣ composite types (objects) ‣ Transitive closure

I was as confused as anyone else ? By the early 1990s, however, I’d seen the light Domains Can Contain Anything! Date on Database: Writings 2000-2006 Chris Date SQL:1999 — Recursion CREATE TABLE t ( id INTEGER, parent INTEGER, ) SQL:1999 — Recursion CREATE TABLE t ( id INTEGER, parent INTEGER, ) SQL:1999 — Recursion CREATE TABLE t ( id INTEGER, parent INTEGER, ) SQL:1999 — Recursion SQL:1999 — Recursion

SQL:1999 — Recursion

SELECT t.id, t.parent FROM t WHERE t.id = ?

SQL:1999 — Recursion

SELECT t.id, t.parent FROM t WHERE t.id = ? UNION ALL SELECT t.id, t.parent FROM t WHERE t.parent = ? SQL:1999 — Recursion

SELECT t.id, t.parent FROM t WHERE t.id = ? UNION ALL SELECT t.id, t.parent FROM t WHERE t.parent = ? SQL:1999 — Recursion

SELECT t.id, t.parent FROM t WHERE t.id = ? UNION ALL SELECT t.id, t.parent FROM t WHERE t.parent = ? SQL:1999 — Recursion

WITH RECURSIVE prev (id, parent) AS ( SELECT t.id, t.parent FROM t WHERE t.id = ? UNION ALL

SELECT t.id, t.parent FROM t JOIN prev ON t.parent = prev.id ) SELECT * FROM prev SQL:1999 — Recursion

WITH RECURSIVE prev (id, parent) AS ( SELECT t.id, t.parent FROM t WHERE t.id = ? UNION ALL

SELECT t.id, t.parent FROM t JOIN prev ON t.parent = prev.id ) SELECT * FROM prev SQL:1999 — Recursion

WITH RECURSIVE prev (id, parent) AS ( SELECT t.id, t.parent FROM t WHERE t.id = ? UNION ALL

SELECT t.id, t.parent FROM t JOIN prev ON t.parent = prev.id ) SELECT * FROM prev SQL:1999 — Recursion

1999 2001 2003 2005 2007 2009 2011 2013 2015 2017 5.1 10.2 MariaDB 8.0 MySQL 8.4 PostgreSQL

1.0 3.8.3 SQLite 7.0 DB2 LUW 11gR2 Oracle 2005 SQL Server

LATERAL SQL:1999 — LATERAL

Select-list sub-queries must be scalar[0]: (return no more than one domain value)

SELECT … , (SELECT column_1 FROM t1 WHERE t1.x = t2.y ) AS c FROM t2 … SQL:1999 — LATERAL

Select-list sub-queries must be scalar[0]: (return no more than one domain value)

SELECT … , (SELECT column_1 , column_2 FROM t1 ✗ WHERE t1.x = t2.y ) AS c More than FROM t2 one column? … ⇒Syntax error SQL:1999 — LATERAL

Select-list sub-queries must be scalar[0]: (return no more than one domain value)

SELECT … More than one row? , (SELECT column_1 , column_2 ⇒Runtime error! FROM t1 ✗ WHERE t1.x = t2.y } ) AS c More than FROM t2 one column? … ⇒Syntax error SQL:1999 — LATERAL

Lateral derived tables lift both limitations and can be correlated:

SELECT … , ldt.* FROM t2 CROSS JOIN LATERAL (SELECT column_1, column_2 FROM t1 WHERE t1.x = t2.y ) AS ldt … SQL:1999 — LATERAL

Lateral derived tables lift both limitations and can be correlated:

SELECT … “Derived table” means

, ldt.* it’s in the FROM t2 FROM/JOIN clause CROSS JOIN LATERAL (SELECT column_1, column_2 FROM t1 WHERE t1.x = t2.y ) AS ldt … SQL:1999 — LATERAL

Lateral derived tables lift both limitations and can be correlated:

SELECT … “Derived table” means

, ldt.* it’s in the FROM t2 FROM/JOIN clause CROSS JOIN LATERAL (SELECT column_1, column_2 FROM t1 WHERE t1.x = t2.y ) AS ldt … Still “correlated” SQL:1999 — LATERAL

Lateral derived tables lift both limitations and can be correlated:

SELECT … “Derived table” means

, ldt.* it’s in the FROM t2 FROM/JOIN clause CROSS JOIN LATERAL (SELECT column_1, column_2 FROM t1 WHERE Regular t1.x = t2.y semantics ) AS ldt … Still “correlated” SQL:1999 — LATERAL

SQL:1999 — LATERAL

‣ Top-N per group

inside a lateral derived table FETCH FIRST (or LIMIT, TOP) applies per row left tables. SQL:1999 — LATERAL

‣ Top-N per group FROM t

inside a lateral derived table JOIN LATERAL (SELECT … FETCH FIRST (or LIMIT, TOP) FROM … WHERE t.c=… applies per row from left tables. … LIMIT 10 ) derived_table SQL:1999 — LATERAL Add proper index

for Top-N query ‣ Top-N per group FROM t

inside a lateral derived table JOIN LATERAL (SELECT … FETCH FIRST (or LIMIT, TOP) FROM … WHERE t.c=… applies per row from left tables. ORDER BY … LIMIT 10 ) derived_table

https://use-the-index-luke.com/sql/partial-results/top-n-queries SQL:1999 — LATERAL Add proper index

for Top-N query ‣ Top-N per group FROM t

inside a lateral derived table JOIN LATERAL (SELECT … FETCH FIRST (or LIMIT, TOP) FROM … WHERE t.c=… applies per row from left tables. ORDER BY … LIMIT 10 Also useful to find most recent ‣ ) derived_table news from several subscribed topics (“multi-source top-N”). https://use-the-index-luke.com/sql/partial-results/top-n-queries SQL:1999 — LATERAL Add proper index

for Top-N query ‣ Top-N per group FROM t

inside a lateral derived table JOIN LATERAL (SELECT … FETCH FIRST (or LIMIT, TOP) FROM … WHERE t.c=… applies per row from left tables. ORDER BY … LIMIT 10 Also useful to find most recent ‣ ) derived_table news from several subscribed topics (“multi-source top-N”). https://use-the-index-luke.com/sql/partial-results/top-n-queries

‣ Table function arguments FROM t (TABLE often implies LATERAL) JOIN TABLE (your_func(t.c)) SQL:1999 — LATERAL

1999 2001 2003 2005 2007 2009 2011 2013 2015 2017 5.1 MariaDB 8.0.14 MySQL 9.3 PostgreSQL

1.0 SQLite 9.1 DB2 LUW 11gR1[0] 12cR1 Oracle 2005[1] SQL Server [0]Undocumented. Requires setting trace event 22829. [1]LATERAL is not supported as of SQL Server 2016 but [CROSS|OUTER] APPLY can be used for the same effect. 1999 2003 http://www.acm.org:80/sigmod/record/issues/0206/standard.pdf (via Wayback machine) SQL:2003 — Schemaless & Analytical

Schemaless ‣ Introduced XML ‣ Non-uniform documents in a single column SQL:2003 — Schemaless & Analytical

Schemaless ‣ Introduced XML ‣ Non-uniform documents in a single column

Later: ‣ JSON added with SQL:2016 ‣ Proprietary JSON support: ‣ 2012: PostgreSQL ‣ 2014: Oracle ‣ 2015: MySQL ‣ 2016: SQL Server SQL:2003 — Schemaless & Analytical

Schemaless Analytical ‣ Introduced XML ‣ Introduced ‣ Non-uniform window functions documents in ‣ Accessing other rows a single column of the current result

Later: ‣ JSON added with SQL:2016 ‣ Proprietary JSON support: ‣ 2012: PostgreSQL ‣ 2014: Oracle ‣ 2015: MySQL ‣ 2016: SQL Server SQL:2003 — Schemaless & Analytical

Schemaless Analytical ‣ Introduced XML ‣ Introduced ‣ Non-uniform window functions documents in ‣ Accessing other rows a single column of the current result

Later: Later: ‣ JSON added with SQL:2016 ‣ Extended in SQL:2011 ‣ Proprietary JSON support: ‣ Popular among “New ” ‣ 2012: PostgreSQL ‣ 2013: BigQuery, Hive ‣ 2014: Oracle ‣ 2014: Impala ‣ 2015: MySQL ‣ 2015: Spark SQL ‣ 2016: SQL Server ‣ 2016: NuoDB, MemSQL, Cockroach DB, VoltDB SQL:2003 — Analytical

SELECT id, value id value 1 +10 2 +20 3 -10 4 +50 5 -30 6 -20 FROM t SQL:2003 — Analytical

SELECT id, value id value bal 1 +10 2 +20 3 -10 4 +50 5 -30 6 -20 FROM t SQL:2003 — Analytical

SELECT id, value id value bal 1 +10 +10 2 +20 3 -10 4 +50 5 -30 6 -20 FROM t SQL:2003 — Analytical

SELECT id, value id value bal 1 +10 +10 2 +20 +30 3 -10 +20 4 +50 +70 5 -30 +40 6 -20 +20 FROM t SQL:2003 — Analytical

SELECT id, value id value bal , SUM(value) 1 +10 +10 OVER ( 2 +20 +30 3 -10 +20 4 +50 +70 5 -30 +40 ) bal 6 -20 +20 FROM t SQL:2003 — Analytical

SELECT id, value id value bal , SUM(value) 1 +10 +10 OVER ( 2 +20 +30 ORDER BY id 3 -10 +20 4 +50 +70 5 -30 +40 ) bal 6 -20 +20 FROM t SQL:2003 — Analytical

SELECT id, value id value bal , SUM(value) 1 +10 +10 OVER ( 2 +20 +30 ORDER BY id 3 -10 +20 4 +50 +70 5 -30 +40 ) bal 6 -20 +20 FROM t SQL:2003 — Analytical

SELECT id, value id value bal , SUM(value) 1 +10 +10 OVER ( 2 +20 +30 ORDER BY id 3 -10 +20 ROWS BETWEEN 4 +50 +70 UNBOUNDED PRECEDING 5 -30 +40 ) bal 6 -20 +20 FROM t SQL:2003 — Analytical

SELECT id, value id value bal , SUM(value) 1 +10 +10 OVER ( 2 +20 +30 ORDER BY id 3 -10 +20 ROWS BETWEEN 4 +50 +70 UNBOUNDED PRECEDING 5 -30 +40 ) bal 6 -20 +20 FROM t SQL:2003 — Analytical

SELECT id, value id value bal , SUM(value) 1 +10 +10 OVER ( 2 +20 +30 ORDER BY id 3 -10 +20 ROWS BETWEEN 4 +50 +70 UNBOUNDED PRECEDING 5 -30 +40 AND CURRENT ROW ) bal 6 -20 +20 FROM t SQL:2003 — Analytical

SELECT id, value id value bal , SUM(value) 1 +10 +10 OVER ( 2 +20 +30 ORDER BY id 3 -10 +20 ROWS BETWEEN 4 +50 +70 UNBOUNDED PRECEDING 5 -30 +40 AND CURRENT ROW ) bal 6 -20 +20 FROM t SQL:2003 — Analytical

SELECT id, value id value bal , SUM(value) 1 +10 +10 OVER ( 2 +20 +30 ORDER BY id 3 -10 +20 ROWS BETWEEN 4 +50 +70 UNBOUNDED PRECEDING 5 -30 +40 AND CURRENT ROW ) bal 6 -20 +20 FROM t SQL:2003 — Analytical

SELECT id, value id value bal , SUM(value) 1 +10 +10 OVER ( 2 +20 +30 ORDER BY id 3 -10 +20 ROWS BETWEEN 4 +50 +70 UNBOUNDED PRECEDING 5 -30 +40 AND CURRENT ROW ) bal 6 -20 +20 FROM t SQL:2003 — Analytical

SELECT id, value id value bal , SUM(value) 1 +10 +10 OVER ( 2 +20 +30 ORDER BY id 3 -10 +20 ROWS BETWEEN 4 +50 +70 UNBOUNDED PRECEDING 5 -30 +40 AND CURRENT ROW ) bal 6 -20 +20 FROM t SQL:2003 — Analytical

SELECT id, value id value bal , SUM(value) 1 +10 +10 OVER ( 2 +20 +30 ORDER BY id 3 -10 +20 ROWS BETWEEN 4 +50 +70 UNBOUNDED PRECEDING 5 -30 +40 AND CURRENT ROW ) bal 6 -20 +20 FROM t SQL:2003 — Analytical

SELECT id, value id value bal , SUM(value) 1 +10 +10 OVER ( 2 +20 +30 ORDER BY id 3 -10 +20 ROWS BETWEEN 4 +50 +70 UNBOUNDED PRECEDING 5 -30 +40 AND CURRENT ROW ) bal 6 -20 +20 FROM t SQL:2003 — Analytical

SELECT id, value id value bal , SUM(value) 1 +10 +10 OVER ( 2 +20 +30 ORDER BY id 3 -10 +20 ROWS BETWEEN 4 +50 +70 UNBOUNDED PRECEDING 5 -30 +40 AND CURRENT ROW ) bal 6 -20 +20 FROM t SQL:2003 — Analytical

1999 2001 2003 2005 2007 2009 2011 2013 2015 2017 5.1 10.2 MariaDB 8.0 MySQL 8.4 PostgreSQL

1.0 3.25.0 SQLite 7.0 DB2 LUW 8i Oracle 2005[0] 2012 SQL Server [0]Without framing OVER SQL:2011 groups option SQL:2011 — OVER GROUPS option

ORDER BY x between 1 preceding and 1 following

SQL:2011 — OVER GROUPS option rows, range ORDER BY x between 1 preceding and 1 following

SQL:2011 — OVER GROUPS option rows, range ORDER BY x between 1 preceding and 1 following x CURRENT ROW 1 3 3.5 3.5 4 SQL:2011 — OVER GROUPS option rows, range ORDER BY x between 1 preceding and 1 following x CURRENT ROW 1 3 rows count(*) 3.5 3.5 4 SQL:2011 — OVER GROUPS option rows, range ORDER BY x between 1 preceding and 1 following x CURRENT ROW 1 3 rows count(*) range 3.5 x between current_row.x - 1 3.5 and current_row.x + 1 4 SQL:2011 — OVER GROUPS option rows, New in ORDER BY x range SQL:2011 between 1 preceding groups and 1 following x CURRENT ROW 1 3 rows count(*) range 3.5 x between current_row.x - 1 3.5 and current_row.x + 1 4 SQL:2011 — OVER GROUPS option rows, New in ORDER BY x range SQL:2011 between 1 preceding groups and 1 following x CURRENT ROW 1 3 rows count(*) groups range 3.5 count(distinct x) x between current_row.x - 1 3.5 and current_row.x + 1 4 SQL:2011 — OVER GROUPS option

1999 2001 2003 2005 2007 2009 2011 2013 2015 2017 5.1 MariaDB MySQL 11 PostgreSQL

3.28.0 SQLite DB2 LUW Oracle SQL Server OVER SQL:2003 frame exclusion SQL:2003 — frame exclusion

OVER (ORDER BY … BETWEEN … exclude [ no others | current row | group | ties ] )

SQL:2003 — frame exclusion

OVER (ORDER BY default … BETWEEN … exclude [ no others | current row no others | group | ties ] )

SQL:2003 — frame exclusion

OVER (ORDER BY default … BETWEEN … exclude [ no others | current row no others | group | ties ] ) x 1 2 2 2 3 SQL:2003 — frame exclusion

OVER (ORDER BY default … BETWEEN … exclude [ no others | current row current row | group | ties ] ) x 1 2 current row 2 2 3 SQL:2003 — frame exclusion

OVER (ORDER BY default … BETWEEN … exclude [ no others | current row | group | ties ] group ) x 1 2 group current row 2 x = current_row 2 3 SQL:2003 — frame exclusion

OVER (ORDER BY default … BETWEEN … exclude [ no others | current row | group | ties ] ties ) x 1 2 group current row 2 ties x = current_row 2 3 SQL:2003 — frame exclusion

1999 2001 2003 2005 2007 2009 2011 2013 2015 2017 5.1 MariaDB MySQL 11 PostgreSQL

3.28.0 SQLite DB2 LUW Oracle SQL Server Inverse Distribution Functions (percentiles) SQL:2003 — Analytical (Median)

SQL:2003 — Analytical (Median)

SELECT d1.val FROM data d1 JOIN data d2 ON (d1.val < d2.val OR (d1.val=d2.val AND d1.id

SELECT d1.val Number rows FROM data d1 JOIN data d2 ON (d1.val < d2.val OR (d1.val=d2.val AND d1.id

SELECT d1.val Number rows FROM data d1 JOIN data d2 ON (d1.val < d2.val OR (d1.val=d2.val AND d1.id

SELECT d1.val Number rows FROM data d1 JOIN data d2 ON (d1.val < d2.val OR (d1.val=d2.val AND d1.id

SELECT d1.val Number rows FROM data d1 JOIN data d2 ON (d1.val < d2.val OR (d1.val=d2.val AND d1.id

SELECT d1.val Number rows FROM data d1 All employees must JOIN data d2 ON (d1.val < d2.val wash hands OR (d1.val=d2.val AND d1.id

SELECT PERCENTILE_DISC(0.5) WITHIN GROUP (ORDER BY val) FROM data SQL:2003 — Analytical (Median) Median SELECT PERCENTILE_DISC(0.5) WITHIN GROUP (ORDER BY val) FROM data SQL:2003 — Analytical (Median) Median SELECT PERCENTILE_DISC(0.5) WITHIN GROUP (ORDER BY val) FROM data Which value? SQL:2003 — Analytical (Median)

SELECT PERCENTILE_DISC(0.5) WITHIN GROUP (ORDER BY val) FROM data SQL:2003 — Analytical (Median)

SELECT PERCENTILE_DISC(0.5) WITHIN GROUP (ORDER BY val) FROM data

Two variants: ‣for discrete values (categories) ‣for continuous values (linear interpolation) SQL:2003 — Analytical (Median)

SELECT PERCENTILE_DISC(0.5) WITHIN GROUP (ORDER BY val) FROM data

Two variants: 0 0.25 0.5 0.75 1 1 ‣for discrete values 2 (categories) ‣for continuous values 3 (linear interpolation) 4 SQL:2003 — Analytical (Median)

SELECT PERCENTILE_DISC(0.5) WITHIN GROUP (ORDER BY val) FROM data

Two variants: 0 0.25 0.5 0.75 1 1 PERCENTILE_DISC ‣for discrete values 2 (categories) ‣for continuous values 3 (linear interpolation) 4 SQL:2003 — Analytical (Median)

SELECT PERCENTILE_DISC(0.5) WITHIN GROUP (ORDER BY val) FROM data

Two variants: 0 0.25 0.5 0.75 1 1 PERCENTILE_DISC ‣for discrete values 2 (categories) ‣for continuous values 3 (linear interpolation) 4 SQL:2003 — Analytical (Median)

SELECT PERCENTILE_DISC(0.5) WITHIN GROUP (ORDER BY val) FROM data

Two variants: 0 0.25 0.5 0.75 1 1 PERCENTILE_DISCPERCENTILE_DISC(0.5) ‣for discrete values 2 (categories) ‣for continuous values 3 (linear interpolation) 4 SQL:2003 — Analytical (Median)

SELECT PERCENTILE_DISC(0.5) WITHIN GROUP (ORDER BY val) FROM data

Two variants: 0 0.25 0.5 0.75 1 1 PERCENTILE_DISCPERCENTILE_DISC(0.5) ‣for discrete values 2 PERCENTILE_CONT (categories) ‣for continuous values 3 (linear interpolation) 4 SQL:2003 — Analytical (Median)

SELECT PERCENTILE_DISC(0.5) WITHIN GROUP (ORDER BY val) FROM data

Two variants: 0 0.25 0.5 0.75 1 1 PERCENTILE_DISCPERCENTILE_DISC(0.5) ‣for discrete values 2 PERCENTILE_CONTPERCENTILE_CONT(0.5) (categories) ‣for continuous values 3 (linear interpolation) 4 SQL:2003 — PERCENTILE_DISC

1999 2001 2003 2005 2007 2009 2011 2013 2015 2017 5.1 10.3.7[0] MariaDB MySQL 9.4 PostgreSQL

1.0 SQLite DB2 LUW 9iR1 Oracle 2012[0] SQL Server [0]Only as window function (OVER required). 2003 2006 XMLTABLE SQL:2006 — XMLTABLE SQL:2006 — XMLTABLE

foo

bar SQL:2006 — XMLTABLE

foo id c1 42 foo bar 43 bar SQL:2006 — XMLTABLE

foo bar

id c1 42 foo 43 bar SQL:2006 — XMLTABLE

SELECT * foo FROM XMLTABLE ( '/d/e' bar PASSING ? COLUMNS id INT PATH '@id' id c1 , c1 VARCHAR(…) PATH 'c1' ) r 42 foo 43 bar SQL:2006 — XMLTABLE

XQuery (XPath) SELECT * ‣ Each match is foo FROM XMLTABLE turned into a row ( '/d/e' bar PASSING ? COLUMNS id INT PATH '@id' id c1 , c1 VARCHAR(…) PATH 'c1' ) r 42 foo 43 bar In preparation for JSON_TABLE & co, SQL:2006 — XMLTABLE PG 12 introduced SQL/JSON Path XQuery (XPath) SELECT * ‣ Each match is foo FROM XMLTABLE turned into a row ( '/d/e' bar PASSING ? COLUMNS id INT PATH '@id' id c1 , c1 VARCHAR(…) PATH 'c1' ) r 42 foo 43 bar In preparation for JSON_TABLE & co, SQL:2006 — XMLTABLE PG 12 introduced SQL/JSON Path XQuery (XPath) SELECT * ‣ Each match is foo FROM XMLTABLE turned into a row ( '/d/e' bar PASSING ? COLUMNS id INT PATH '@id' id c1 , c1 VARCHAR(…) PATH 'c1' ) r 42 foo 43 bar In preparation for JSON_TABLE & co, SQL:2006 — XMLTABLE PG 12 introduced SQL/JSON Path XQuery (XPath) SELECT * ‣ Each match is foo FROM XMLTABLE turned into a row ( '/d/e' Bind bar PASSING ? Parameter COLUMNS id INT PATH '@id' id c1 , c1 VARCHAR(…) PATH 'c1' ) r 42 foo 43 bar SQL:2006 — XMLTABLE

SELECT * foo FROM XMLTABLE ( '/d/e' bar PASSING ? COLUMNS id INT PATH '@id' id c1 , c1 VARCHAR(…) PATH 'c1' ) r 42 foo 43 bar SQL:2006 — XMLTABLE

INSERT INTO target_table SELECT * foo FROM XMLTABLE ( '/d/e' bar PASSING ? COLUMNS id INT PATH '@id' id c1 , c1 VARCHAR(…) PATH 'c1' ) r 42 foo 43 bar SQL:2006 — XMLTABLE

1999 2001 2003 2005 2007 2009 2011 2013 2015 2017 5.1 MariaDB MySQL 10[0] PostgreSQL

1.0 SQLite 9.7 DB2 LUW 11gR1 Oracle SQL Server [0]No XQuery (only XPath). No default namespace declaration. https://webstore.iec.ch/publication/59685 A lot has happened since SQL-92

https://webstore.iec.ch/publication/59685 A lot has happened since SQL-92

SQL has evolved beyond the relational idea

https://webstore.iec.ch/publication/59685 A lot has happened since SQL-92

SQL has evolved beyond the relational idea

If you use SQL for CRUD operations only, you are doing it wrong A lot has happened since SQL-92

SQL has evolved beyond the relational idea Training: https://winand.at/ If you use SQL for https://modern-sql.com CRUD operations only, @ModernSQL by @MarkusWinand you are doing it wrong A lot has happened since SQL-92

SQL has evolved beyond the relational idea Training: https://winand.at/ If you use SQL for https://modern-sql.com CRUD operations only, @ModernSQL by @MarkusWinand you are doing it wrong Feedback: https://2019.pgconf.eu/f