SQL has gone out of fashion lately—partly due to the NoSQL movement, but mostly because SQL is often still used like 20 years ago. As a matter of fact, the SQL standard continued to evolve during the past decades resulting in the current release of 2016. In this session, we will go through the most important additions since the widely known SQL-92. We will cover common table expressions and window functions in detail and have a very short look at the temporal features of SQL:2011 and row pattern matching from SQL:2016.
Links:
http://modern-sql.com/
http://winand.at/
http://sql-performance-explained.com/
Report
Share
Report
Share
1 of 174
Download to read offline
More Related Content
Modern SQL in Open Source and Commercial Databases
4. Select-list sub-queries must be scalar[0]:
LATERAL Before SQL:1999
SELECT …
, (SELECT column_1
FROM t1
WHERE t1.x = t2.y
) AS c
FROM t2
…
(an atomic quantity that can hold only one value at a time[1])
[0] Neglecting row values and other workarounds here; [1] https://en.wikipedia.org/wiki/Scalar
5. Select-list sub-queries must be scalar[0]:
LATERAL Before SQL:1999
SELECT …
, (SELECT column_1
FROM t1
WHERE t1.x = t2.y
) AS c
FROM t2
…
(an atomic quantity that can hold only one value at a time[1])
[0] Neglecting row values and other workarounds here; [1] https://en.wikipedia.org/wiki/Scalar
✗, column_2
More than
one column?
Syntax error
}
More than
one row?
Runtime error!
6. Lateral derived tables lift both limitations and can be correlated:
LATERAL Since SQL:1999
SELECT …
, ldt.*
FROM t2
LEFT JOIN LATERAL (SELECT column_1, column_2
FROM t1
WHERE t1.x = t2.y
) AS ldt
ON (true)
…
7. Lateral derived tables lift both limitations and can be correlated:
LATERAL Since SQL:1999
SELECT …
, ldt.*
FROM t2
LEFT JOIN LATERAL (SELECT column_1, column_2
FROM t1
WHERE t1.x = t2.y
) AS ldt
ON (true)
…
“Derived table” means
it’s in the
FROM/JOIN clause
Still
“correlated”
Regular join
semantics
8. FROM t
JOIN LATERAL (SELECT …
FROM …
WHERE t.c=…
ORDER BY …
LIMIT 10
) derived_table
‣ Top-N per group
inside a lateral derived table
FETCH FIRST (or LIMIT, TOP)
applies per row from left tables.
‣ Also useful to find most recent
news from several subscribed
topics (“multi-source top-N”).
Use-CasesLATERAL
Add proper index
for Top-N query
http://use-the-index-luke.com/sql/partial-results/top-n-queries
9. FROM t
JOIN LATERAL (SELECT …
FROM …
WHERE t.c=…
ORDER BY …
LIMIT 10
) derived_table
‣ Top-N per group
inside a lateral derived table
FETCH FIRST (or LIMIT, TOP)
applies per row from left tables.
‣ Also useful to find most recent
news from several subscribed
topics (“multi-source top-N”).
‣ Table function arguments
(TABLE often implies LATERAL)
Use-CasesLATERAL
FROM t
JOIN TABLE (your_func(t.c))
10. LATERAL is the "for each" loop of SQL
LATERAL plays well with outer and cross joins
LATERAL is great for Top-N subqueries
LATERAL can join table functions (unnest!)
LATERAL In a Nutshell
13. Only one GROUP BY operation at a time:
GROUPING SETS Before SQL:1999
SELECT year
, month
, sum(revenue)
FROM tbl
GROUP BY year, month
Monthly revenue Yearly revenue
SELECT year
, sum(revenue)
FROM tbl
GROUP BY year
17. GROUPING SETS are multiple GROUP BYs in one go
() (empty brackets) build a group over all rows
GROUPING (function) disambiguates the meaning of NULL
(was the grouped data NULL or is this column not currently grouped?)
Permutations can be created using ROLLUP and CUBE
(ROLLUP(a,b,c) = GROUPING SETS ((a,b,c), (a,b),(a),())
GROUPING SETS In a Nutshell
20. WITH (non-recursive) The Problem
Nested queries are hard to read:
SELECT …
FROM (SELECT …
FROM t1
JOIN (SELECT … FROM …
) a ON (…)
) b
JOIN (SELECT … FROM …
) c ON (…)
21. Understand
this first
WITH (non-recursive) The Problem
Nested queries are hard to read:
SELECT …
FROM (SELECT …
FROM t1
JOIN (SELECT … FROM …
) a ON (…)
) b
JOIN (SELECT … FROM …
) c ON (…)
22. Then this...
WITH (non-recursive) The Problem
Nested queries are hard to read:
SELECT …
FROM (SELECT …
FROM t1
JOIN (SELECT … FROM …
) a ON (…)
) b
JOIN (SELECT … FROM …
) c ON (…)
23. Then this...
WITH (non-recursive) The Problem
Nested queries are hard to read:
SELECT …
FROM (SELECT …
FROM t1
JOIN (SELECT … FROM …
) a ON (…)
) b
JOIN (SELECT … FROM …
) c ON (…)
24. Finally the first line makes sense
WITH (non-recursive) The Problem
Nested queries are hard to read:
SELECT …
FROM (SELECT …
FROM t1
JOIN (SELECT … FROM …
) a ON (…)
) b
JOIN (SELECT … FROM …
) c ON (…)
25. CTEs are statement-scoped views:
WITH
a (c1, c2, c3)
AS (SELECT c1, c2, c3 FROM …),
b (c4, …)
AS (SELECT c4, …
FROM t1
JOIN a
ON (…)
),
WITH (non-recursive) Since SQL:1999
26. CTEs are statement-scoped views:
WITH
a (c1, c2, c3)
AS (SELECT c1, c2, c3 FROM …),
b (c4, …)
AS (SELECT c4, …
FROM t1
JOIN a
ON (…)
),
Keyword
WITH (non-recursive) Since SQL:1999
27. CTEs are statement-scoped views:
WITH
a (c1, c2, c3)
AS (SELECT c1, c2, c3 FROM …),
b (c4, …)
AS (SELECT c4, …
FROM t1
JOIN a
ON (…)
),
Name of CTE and (here
optional) column names
WITH (non-recursive) Since SQL:1999
28. CTEs are statement-scoped views:
WITH
a (c1, c2, c3)
AS (SELECT c1, c2, c3 FROM …),
b (c4, …)
AS (SELECT c4, …
FROM t1
JOIN a
ON (…)
),
Definition
WITH (non-recursive) Since SQL:1999
29. CTEs are statement-scoped views:
WITH
a (c1, c2, c3)
AS (SELECT c1, c2, c3 FROM …),
b (c4, …)
AS (SELECT c4, …
FROM t1
JOIN a
ON (…)
),
Introduces
another CTE
Don't repeat
WITH
WITH (non-recursive) Since SQL:1999
30. CTEs are statement-scoped views:
WITH
a (c1, c2, c3)
AS (SELECT c1, c2, c3 FROM …),
b (c4, …)
AS (SELECT c4, …
FROM t1
JOIN a
ON (…)
),
May refer to
previous CTEs
WITH (non-recursive) Since SQL:1999
34. CTEs are statement-scoped views:
WITH
a (c1, c2, c3)
AS (SELECT c1, c2, c3 FROM …),
b (c4, …)
AS (SELECT c4, …
FROM t1
JOIN a
ON (…)
),
c (…)
AS (SELECT … FROM …)
SELECT …
FROM b JOIN c ON (…)
Read
top down
WITH (non-recursive) Since SQL:1999
35. ‣ Literate SQL
Organize SQL code to
improve maintainability
‣ Assign column names
to tables produced by values
or unnest.
‣ Overload tables (for testing)
with queries hide tables
of the same name.
Use-CasesWITH (non-recursive)
http://modern-sql.com/use-case/literate-sql
http://modern-sql.com/use-case/naming-unnamed-columns
http://modern-sql.com/use-case/unit-tests-on-transient-data
36. WITH are the "private methods" of SQL
WITH is a prefix to SELECT
WITH queries are only visible in the SELECT
they precede
WITH in detail:
http://modern-sql.com/feature/with
WITH (non-recursive) In a Nutshell
41. Views and derived tables support "predicate pushdown":
SELECT *
FROM (SELECT *
FROM news
) n
WHERE topic=1;
PostgreSQL “issues”WITH (non-recursive)
42. Views and derived tables support "predicate pushdown":
SELECT *
FROM (SELECT *
FROM news
) n
WHERE topic=1;
Bitmap Heap Scan
on news (rows=6370)
->Bitmap Index Scan
on idx (rows=6370)
Cond: topic=1
PostgreSQL “issues”WITH (non-recursive)
43. PostgreSQL 9.1+ allows DML within WITH:
WITH deleted_rows AS (
DELETE FROM source_tbl
RETURNING *
)
INSERT INTO destination_tbl
SELECT * FROM deleted_rows;
PostgreSQL ExtensionWITH (non-recursive)
51. Recursive common table expressions may refer to
themselves in the second leg of a UNION [ALL]:
WITH RECURSIVE cte (n)
AS (SELECT 1
UNION ALL
SELECT n+1
FROM cte
WHERE n < 3)
SELECT * FROM cte
Since SQL:1999WITH RECURSIVE
52. Recursive common table expressions may refer to
themselves in the second leg of a UNION [ALL]:
WITH RECURSIVE cte (n)
AS (SELECT 1
UNION ALL
SELECT n+1
FROM cte
WHERE n < 3)
SELECT * FROM cte
Keyword
Since SQL:1999WITH RECURSIVE
53. Recursive common table expressions may refer to
themselves in the second leg of a UNION [ALL]:
WITH RECURSIVE cte (n)
AS (SELECT 1
UNION ALL
SELECT n+1
FROM cte
WHERE n < 3)
SELECT * FROM cte
Column list
mandatory here
Since SQL:1999WITH RECURSIVE
54. Recursive common table expressions may refer to
themselves in the second leg of a UNION [ALL]:
WITH RECURSIVE cte (n)
AS (SELECT 1
UNION ALL
SELECT n+1
FROM cte
WHERE n < 3)
SELECT * FROM cte
Executed first
Since SQL:1999WITH RECURSIVE
55. Recursive common table expressions may refer to
themselves in the second leg of a UNION [ALL]:
WITH RECURSIVE cte (n)
AS (SELECT 1
UNION ALL
SELECT n+1
FROM cte
WHERE n < 3)
SELECT * FROM cte
Result
sent there
Since SQL:1999WITH RECURSIVE
56. Recursive common table expressions may refer to
themselves in the second leg of a UNION [ALL]:
WITH RECURSIVE cte (n)
AS (SELECT 1
UNION ALL
SELECT n+1
FROM cte
WHERE n < 3)
SELECT * FROM cte
Result
visible
twice
Since SQL:1999WITH RECURSIVE
57. Recursive common table expressions may refer to
themselves in the second leg of a UNION [ALL]:
WITH RECURSIVE cte (n)
AS (SELECT 1
UNION ALL
SELECT n+1
FROM cte
WHERE n < 3)
SELECT * FROM cte
n
---
1
2
3
(3 rows)
Once it becomes
part of
the final
result
Since SQL:1999WITH RECURSIVE
58. Recursive common table expressions may refer to
themselves in the second leg of a UNION [ALL]:
WITH RECURSIVE cte (n)
AS (SELECT 1
UNION ALL
SELECT n+1
FROM cte
WHERE n < 3)
SELECT * FROM cte
n
---
1
2
3
(3 rows)
Since SQL:1999WITH RECURSIVE
59. Recursive common table expressions may refer to
themselves in the second leg of a UNION [ALL]:
WITH RECURSIVE cte (n)
AS (SELECT 1
UNION ALL
SELECT n+1
FROM cte
WHERE n < 3)
SELECT * FROM cte
n
---
1
2
3
(3 rows)
Second
leg of
UNION
is
executed
Since SQL:1999WITH RECURSIVE
60. Recursive common table expressions may refer to
themselves in the second leg of a UNION [ALL]:
WITH RECURSIVE cte (n)
AS (SELECT 1
UNION ALL
SELECT n+1
FROM cte
WHERE n < 3)
SELECT * FROM cte
n
---
1
2
3
(3 rows)
Result
sent there
again
Since SQL:1999WITH RECURSIVE
61. Recursive common table expressions may refer to
themselves in the second leg of a UNION [ALL]:
WITH RECURSIVE cte (n)
AS (SELECT 1
UNION ALL
SELECT n+1
FROM cte
WHERE n < 3)
SELECT * FROM cte
n
---
1
2
3
(3 rows)
Since SQL:1999WITH RECURSIVE
62. Recursive common table expressions may refer to
themselves in the second leg of a UNION [ALL]:
WITH RECURSIVE cte (n)
AS (SELECT 1
UNION ALL
SELECT n+1
FROM cte
WHERE n < 3)
SELECT * FROM cte
n
---
1
2
3
(3 rows)
It's a
loop!
Since SQL:1999WITH RECURSIVE
63. Recursive common table expressions may refer to
themselves in the second leg of a UNION [ALL]:
WITH RECURSIVE cte (n)
AS (SELECT 1
UNION ALL
SELECT n+1
FROM cte
WHERE n < 3)
SELECT * FROM cte
n
---
1
2
3
(3 rows)
It's a
loop!
Since SQL:1999WITH RECURSIVE
64. Recursive common table expressions may refer to
themselves in the second leg of a UNION [ALL]:
WITH RECURSIVE cte (n)
AS (SELECT 1
UNION ALL
SELECT n+1
FROM cte
WHERE n < 3)
SELECT * FROM cte
n
---
1
2
3
(3 rows)
It's a
loop!
Since SQL:1999WITH RECURSIVE
65. Recursive common table expressions may refer to
themselves in the second leg of a UNION [ALL]:
WITH RECURSIVE cte (n)
AS (SELECT 1
UNION ALL
SELECT n+1
FROM cte
WHERE n < 3)
SELECT * FROM cte
n
---
1
2
3
(3 rows)
n=3
doesn't
match
Since SQL:1999WITH RECURSIVE
66. Recursive common table expressions may refer to
themselves in the second leg of a UNION [ALL]:
WITH RECURSIVE cte (n)
AS (SELECT 1
UNION ALL
SELECT n+1
FROM cte
WHERE n < 3)
SELECT * FROM cte
n
---
1
2
3
(3 rows)
n=3
doesn't
match
Loop
terminates
Since SQL:1999WITH RECURSIVE
67. Use Cases
‣ Row generators
To fill gaps (e.g., in time series),
generate test data.
‣ Processing graphs
Shortest route from person A to B
in LinkedIn/Facebook/Twitter/…
‣ Finding distinct values
with n*log(N)† time complexity.
[…many more…]
As shown on previous slide
http://aprogrammerwrites.eu/?p=1391
“[…] for certain classes of graphs, solutions utilizing
relational database technology […] can offer
performance superior to that of the dedicated graph
databases.” event.cwi.nl/grades2013/07-welc.pdf
http://wiki.postgresql.org/wiki/Loose_indexscan
† n … # distinct values, N … # of table rows. Suitable index required
WITH RECURSIVE
68. WITH RECURSIVE is the “while” of SQL
WITH RECURSIVE "supports" infinite loops
Except PostgreSQL, databases generally don't require
the RECURSIVE keyword.
DB2, SQL Server & Oracle don’t even know the
keyword RECURSIVE, but allow recursive CTEs anyway.
In a NutshellWITH RECURSIVE
76. OVER (PARTITION BY) The Problem
Two distinct concepts could not be used independently:
‣ Merge rows with the same key properties
‣ GROUP BY to specify key properties
‣ DISTINCT to use full row as key
‣ Aggregate data from related rows
‣ Requires GROUP BY to segregate the rows
‣ COUNT, SUM, AVG, MIN, MAX to aggregate grouped rows
78. SELECT c1
, SUM(c2) tot
FROM t
GROUP BY c1
OVER (PARTITION BY) The Problem
Yes⇠Mergerows⇢No
No ⇠ Aggregate ⇢ Yes
SELECT c1
, c2
FROM t
SELECT DISTINCT
c1
, c2
FROM t
SELECT c1
, c2
FROM t
JOIN ( ) ta
ON (t.c1=ta.c1)
SELECT c1
, SUM(c2) tot
FROM t
GROUP BY c1
, tot
79. SELECT c1
, SUM(c2) tot
FROM t
GROUP BY c1
OVER (PARTITION BY) The Problem
Yes⇠Mergerows⇢No
No ⇠ Aggregate ⇢ Yes
SELECT c1
, c2
FROM t
SELECT DISTINCT
c1
, c2
FROM t
SELECT c1
, c2
FROM t
JOIN ( ) ta
ON (t.c1=ta.c1)
SELECT c1
, SUM(c2) tot
FROM t
GROUP BY c1
, tot
89. acnt id value balance
1 1 +10 +10
22 2 +20 +30
22 3 -10 +20
333 4 +50 +70
333 5 -30 +40
333 6 -20 +20
OVER (ORDER BY) The Problem
SELECT id,
value,
FROM transactions t
90. acnt id value balance
1 1 +10 +10
22 2 +20 +30
22 3 -10 +20
333 4 +50 +70
333 5 -30 +40
333 6 -20 +20
OVER (ORDER BY) The Problem
SELECT id,
value,
(SELECT SUM(value)
FROM transactions t2
WHERE t2.id <= t.id)
FROM transactions t
91. acnt id value balance
1 1 +10 +10
22 2 +20 +30
22 3 -10 +20
333 4 +50 +70
333 5 -30 +40
333 6 -20 +20
OVER (ORDER BY) The Problem
SELECT id,
value,
(SELECT SUM(value)
FROM transactions t2
WHERE t2.id <= t.id)
FROM transactions t
Range segregation (<=)
not possible with
GROUP BY or
PARTITION BY
92. OVER (ORDER BY) Since SQL:2003
SELECT id,
value,
FROM transactions t
SUM(value)
OVER (
)
acnt id value balance
1 1 +10 +10
22 2 +20 +30
22 3 -10 +20
333 4 +50 +70
333 5 -30 +40
333 6 -20 +20
ORDER BY id
93. OVER (ORDER BY) Since SQL:2003
SELECT id,
value,
FROM transactions t
SUM(value)
OVER (
)
acnt id value balance
1 1 +10 +10
22 2 +20 +30
22 3 -10 +20
333 4 +50 +70
333 5 -30 +40
333 6 -20 +20
ORDER BY id
ROWS BETWEEN
UNBOUNDED PRECEDING
94. OVER (ORDER BY) Since SQL:2003
SELECT id,
value,
FROM transactions t
SUM(value)
OVER (
)
acnt id value balance
1 1 +10 +10
22 2 +20 +30
22 3 -10 +20
333 4 +50 +70
333 5 -30 +40
333 6 -20 +20
ORDER BY id
ROWS BETWEEN
UNBOUNDED PRECEDING
AND CURRENT ROW
95. OVER (ORDER BY) Since SQL:2003
SELECT id,
value,
FROM transactions t
SUM(value)
OVER (
)
acnt id value balance
1 1 +10 +10
22 2 +20 +30
22 3 -10 +20
333 4 +50 +70
333 5 -30 +40
333 6 -20 +20
ORDER BY id
ROWS BETWEEN
UNBOUNDED PRECEDING
AND CURRENT ROW
96. OVER (ORDER BY) Since SQL:2003
SELECT id,
value,
FROM transactions t
SUM(value)
OVER (
)
acnt id value balance
1 1 +10 +10
22 2 +20 +30
22 3 -10 +20
333 4 +50 +70
333 5 -30 +40
333 6 -20 +20
ORDER BY id
ROWS BETWEEN
UNBOUNDED PRECEDING
AND CURRENT ROW
97. OVER (ORDER BY) Since SQL:2003
SELECT id,
value,
FROM transactions t
SUM(value)
OVER (
)
acnt id value balance
1 1 +10 +10
22 2 +20 +30
22 3 -10 +20
333 4 +50 +70
333 5 -30 +40
333 6 -20 +20
ORDER BY id
ROWS BETWEEN
UNBOUNDED PRECEDING
AND CURRENT ROW
98. OVER (ORDER BY) Since SQL:2003
SELECT id,
value,
FROM transactions t
SUM(value)
OVER (
)
acnt id value balance
1 1 +10 +10
22 2 +20 +30
22 3 -10 +20
333 4 +50 +70
333 5 -30 +40
333 6 -20 +20
ORDER BY id
ROWS BETWEEN
UNBOUNDED PRECEDING
AND CURRENT ROW
99. OVER (ORDER BY) Since SQL:2003
SELECT id,
value,
FROM transactions t
SUM(value)
OVER (
)
acnt id value balance
1 1 +10 +10
22 2 +20 +30
22 3 -10 +20
333 4 +50 +70
333 5 -30 +40
333 6 -20 +20
ORDER BY id
ROWS BETWEEN
UNBOUNDED PRECEDING
AND CURRENT ROW
100. OVER (ORDER BY) Since SQL:2003
SELECT id,
value,
FROM transactions t
SUM(value)
OVER (
)
acnt id value balance
1 1 +10 +10
22 2 +20 +20
22 3 -10 +10
333 4 +50 +50
333 5 -30 +20
333 6 -20 . 0
ORDER BY id
ROWS BETWEEN
UNBOUNDED PRECEDING
AND CURRENT ROW
PARTITION BY acnt
101. OVER (ORDER BY) Since SQL:2003
With OVER (ORDER BY n) a new type of functions make sense:
n ROW_NUMBER RANK DENSE_RANK PERCENT_RANK CUME_DIST
1 1 1 1 0 0.25
2 2 2 2 0.33… 0.75
3 3 2 2 0.33… 0.75
4 4 4 3 1 1
102. ‣ Aggregates without GROUP BY
‣ Running totals,
moving averages
‣ Ranking
‣ Top-N per Group
‣ Avoiding self-joins
[… many more …]
Use Cases
SELECT *
FROM (SELECT ROW_NUMBER()
OVER(PARTITION BY … ORDER BY …) rn
, t.*
FROM t) numbered_t
WHERE rn <= 3
AVG(…) OVER(ORDER BY …
ROWS BETWEEN 3 PRECEDING
AND 3 FOLLOWING) moving_avg
OVER(SQL:2003)
103. OVER may follow any aggregate function
OVER defines which rows are visible at each row
OVER() makes all rows visible at every row
OVER(PARTITION BY …) segregates like GROUP BY
OVER(ORDER BY … BETWEEN) segregates using <, >
In a NutshellOVER(SQL:2003)
136. OVER (LEAD, LAG, …) Since SQL:2011
1999
2001
2003
2005
2007
2009
2011
2013
2015
5.1[0]
MariaDB
MySQL
8.4[1]
PostgreSQL
SQLite
9.5[2]
11.1 DB2 LUW
8i[2]
11gR2 Oracle
2012[2]
SQL Server
[0]
Not yet available in MariaDB 10.2.2 (alpha). MDEV-8091
[1]
No IGNORE NULLS and FROM LAST as of PostgreSQL 9.6
[2]
No NTH_VALUE
140. ID Data start_ts end_ts
1 X 10:00:00
UPDATE ... SET DATA = 'Y' ...
ID Data start_ts end_ts
1 X 10:00:00 11:00:00
1 Y 11:00:00
DELETE ... WHERE ID = 1
INSERT ... (ID, DATA) VALUES (1, 'X')
Temporal Tables Since SQL:2011
141. ID Data start_ts end_ts
1 X 10:00:00
UPDATE ... SET DATA = 'Y' ...
ID Data start_ts end_ts
1 X 10:00:00 11:00:00
1 Y 11:00:00
DELETE ... WHERE ID = 1
ID Data start_ts end_ts
1 X 10:00:00 11:00:00
1 Y 11:00:00 12:00:00
Temporal Tables Since SQL:2011
142. Although multiple versions exist, only the “current”
one is visible per default.
After 12:00:00, SELECT * FROM t doesn’t return
anything anymore.
ID Data start_ts end_ts
1 X 10:00:00 11:00:00
1 Y 11:00:00 12:00:00
Temporal Tables Since SQL:2011
143. ID Data start_ts end_ts
1 X 10:00:00 11:00:00
1 Y 11:00:00 12:00:00
With FOR … AS OF you can query anything you like:
SELECT *
FROM t FOR SYSTEM_TIME AS OF
TIMESTAMP '2015-04-02 10:30:00'
ID Data start_ts end_ts
1 X 10:00:00 11:00:00
Temporal Tables Since SQL:2011
144. It isn’t possible to define constraints to avoid overlapping periods.
Workarounds are possible, but no fun: CREATE TRIGGER
id begin end
1 8:00 9:00
1 9:00 11:00
1 10:00 12:00
Temporal Tables The Problem
145. SQL:2011 provides means to cope with temporal tables:
PRIMARY KEY (id, period WITHOUT OVERLAPS)
Temporal Tables Since SQL:2011
Temporal support in SQL:2011 goes way further.
Please read this paper to get the idea:
Temporal features in SQL:2011
http://cs.ulb.ac.be/public/_media/teaching/infoh415/tempfeaturessql2011.pdf
146. Temporal Tables Since SQL:2011
1999
2001
2003
2005
2007
2009
2011
2013
2015
5.1 MariaDB
MySQL
PostgreSQL
SQLite
10.1 DB2 LUW
10gR1[0]
12cR1[1]
Oracle
2016[2]
SQL Server
[0]
Limited system versioning via Flashback
[1]
Limited application versioning added (e.g. no WITHOUT OVERLAPS)
[2]
Only system versioning
167. Since SQL:2016
grp val
1 B
1 A
1 C
2 X
SELECT grp
, LIST_AGG(val, ', ')
WITHIN GROUP (ORDER BY val)
FROM t
GROUP BY grp
LIST_AGG
168. Since SQL:2016
grp val
1 B
1 A
1 C
2 X
grp val
1 A, B, C
2 X
SELECT grp
, LIST_AGG(val, ', ')
WITHIN GROUP (ORDER BY val)
FROM t
GROUP BY grp
LIST_AGG
169. Since SQL:2016
grp val
1 B
1 A
1 C
2 X
grp val
1 A, B, C
2 X
SELECT grp
, LIST_AGG(val, ', ')
WITHIN GROUP (ORDER BY val)
FROM t
GROUP BY grp
LIST_AGG(val, ', ' ON OVERFLOW TRUNCATE '...' WITH COUNT) ➔ 'A, B, ...(1)'
LIST_AGG(val, ', ' ON OVERFLOW ERROR)
Default
LIST_AGG
LIST_AGG(val, ', ' ON OVERFLOW TRUNCATE '...' WITHOUT COUNT) ➔ 'A, B, ...'
Default