Skip to content

Instantly share code, notes, and snippets.

@aarsilv
Last active June 9, 2020 18:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save aarsilv/b44e6d46a2227d5acaebc7c06e24f6cf to your computer and use it in GitHub Desktop.
Save aarsilv/b44e6d46a2227d5acaebc7c06e24f6cf to your computer and use it in GitHub Desktop.
Example of MySQL 8 Windowing and Common Table Expression Functionality
CREATE DATABASE demo;
USE demo;
#Let's get some data
CREATE TABLE video_games (
video_game_id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
title varchar(200) NOT NULL,
developer varchar(200) NOT NULL,
platform varchar(100) NOT NULL,
release_date date NOT NULL,
sales int NOT NULL
);
#Source: https://en.wikipedia.org/wiki/List_of_best-selling_video_games
INSERT INTO video_games (title, developer, platform, release_date, sales)
VALUES
('Grand Theft Auto V', 'Rockstar North', 'Multi-platform', '2013-09-17', 95000000),
('Mario Kart Wii', 'Nintendo EAD', 'Wii', '2008-04-10', 37100000),
('Minecraft', 'Mojang', 'Multi-platform', '2011-11-18', 144000000),
('New Super Mario Bros.', 'Nintendo EAD', 'Nintendo DS', '2006-05-15', 30800000),
('New Super Mario Bros. Wii', 'Nintendo EAD', 'Wii', '2009-11-11', 30200000),
('PlayerUnknown\'s Battlegrounds', 'PUBG Corporation', 'Multi-platform', '2017-12-20', 40000000),
('Pokémon Red and Blue', 'Game Freak', 'Game Boy', '1996-02-27', 31380000),
('Super Mario Bros.', 'Nintendo', 'NES', '1985-09-13', 40240000),
('Tetris', 'Elektronorgtechnica', 'Multi-platform', '1984-06-06', 170000000),
('The Elder Scrolls V: Skyrim', 'Bethesda Game Studios', 'Multi-platform', '2011-11-11', 30000000),
('Wii Sports', 'Nintendo EAD', 'Wii', '2006-11-19', 82850000),
('Wii Sports Resort', 'Nintendo EAD', 'Wii', '2009-06-25', 33080000);
#Check data
SELECT * FROM video_games ORDER BY sales DESC;
#Ranking (inline)
SELECT
title,
sales,
rank() OVER (ORDER BY sales DESC) AS sales_rank,
ntile(4) OVER (ORDER BY sales DESC) AS sales_quartile
FROM video_games
ORDER BY title;
#Ranking (defined window)
SELECT
title,
sales,
rank() OVER sales_window AS sales_rank,
ntile(4) OVER sales_window AS sales_quartile
FROM video_games
WINDOW sales_window AS (ORDER BY sales DESC)
ORDER BY title;
#multiple windows (inline)
SELECT platform,
rank() OVER (PARTITION BY platform ORDER BY sales DESC) AS platform_sales_rank,
title,
sales,
rank() OVER (ORDER BY release_date) AS relase_order
FROM video_games
ORDER BY platform, platform_sales_rank;
#multiple windows (defined)
SELECT platform,
rank() OVER platform_sales_window AS platform_sales_rank,
title,
sales,
rank() OVER release_window AS relase_order
FROM video_games
WINDOW platform_sales_window AS (PARTITION BY platform ORDER BY sales DESC)
, release_window AS (ORDER BY release_date)
ORDER BY platform, platform_sales_rank;
#ranking functions
SELECT platform,
row_number() OVER platform_window AS the_row_number,
rank() OVER platform_window AS the_rank,
dense_rank() OVER platform_window AS the_dense_rank,
percent_rank() OVER platform_window AS the_percent_rank,
cume_dist() OVER platform_window AS the_cum_dist
FROM video_games
WINDOW platform_window AS (ORDER BY platform)
ORDER BY platform;
#lagging and leading window functions
SELECT platform,
title,
release_date,
lag(title) OVER platform_release_window AS previous_platform_release,
lead(title) OVER platform_release_window AS next_platform_release
FROM video_games
WINDOW platform_release_window AS (PARTITION BY platform ORDER BY release_date)
ORDER BY release_date;
#counting window functions
SELECT platform,
title,
release_date,
first_value(title) OVER platform_release_window AS first_platform_release,
nth_value(title, 2) OVER platform_release_window AS second_platform_release_thus_far,
last_value(title) OVER platform_release_window AS last_released_for_platform_thus_far
FROM video_games
WINDOW platform_release_window AS (PARTITION BY platform ORDER BY release_date)
ORDER BY release_date;
#Grouping
SELECT
if(month(release_date) <= 6, '1H', '2H') AS year_half,
platform,
count(*) as num_titles,
sum(sales) AS total_sales
FROM video_games
GROUP BY year_half, platform
ORDER BY year_half, platform;
#With rollup and grouping
SELECT
if(grouping(platform), 'All Platforms', platform) AS platform,
if(grouping(if(month(release_date) <= 6, '1H', '2H')), 'Both Halves', if(month(release_date) <= 6, '1H', '2H')) AS half,
count(*) as num_titles,
sum(sales) AS total_sales
FROM video_games
GROUP BY if(month(release_date) <= 6, '1H', '2H'), platform
WITH ROLLUP
ORDER BY half, platform;
#days between top selling games
SELECT title,
release_date,
lag(release_date) OVER release_window AS previous_release,
datediff(release_date, lag(release_date) OVER release_window) AS days_since_previous
FROM video_games
WINDOW release_window AS (ORDER BY release_date ASC);
#Common table expressions
WITH released_after_previous AS (
SELECT title,
release_date,
datediff(lead(release_date) OVER release_window, release_date) AS days_until_next_release
FROM video_games
WINDOW release_window AS (ORDER BY release_date ASC)
)
SELECT * FROM released_after_previous;
#Multiple common table expressions
WITH released_after_previous AS (
SELECT title,
release_date,
datediff(lead(release_date) OVER release_window, release_date) AS days_until_next_release
FROM video_games
WINDOW release_window AS (ORDER BY release_date ASC)
), days_to_beat_games AS (
SELECT 'Aaron' AS name, 1 AS days_to_beat_a_game
UNION ALL SELECT 'Kim', 365
UNION ALL SELECT 'Hark', 200
)
SELECT d.name AS player, r.title AS game
FROM released_after_previous r
JOIN days_to_beat_games d
ON r.days_until_next_release >= d.days_to_beat_a_game
ORDER BY r.release_date;
#simple recursive common table expression
WITH RECURSIVE cte (n) AS
(
SELECT 1
UNION ALL
SELECT n + 1 FROM cte WHERE n < 5
)
SELECT n FROM cte;
#recursive common table expressions
CREATE TABLE members (
member_id int NOT NULL PRIMARY KEY,
name varchar(100),
parent_member_id int NULL REFERENCES members(member_id)
);
#managed hierarchy via id convention
INSERT INTO members (member_id, name, parent_member_id)
VALUES (1, 'TJ', null),
(11, 'Jonathan F', 1),
(12, 'Ben S', 1),
(13, 'Wendy', 1),
(131, 'Mellisa', 13),
(14, 'Greta', 1),
(141, 'Zach', 14),
(111, 'Aaron', 11),
(112, 'Ben F', 11),
(113, 'Neel', 11),
(1111, 'Kim', 111),
(11111, 'Hayley', 1111),
(1112, 'Ben O', 111),
(1113, 'Jonathan C', 111),
(1114, 'Mathias', 111),
(1121, 'Brendan', 112),
(11211, 'Chas', 1121),
(1122, 'Yun-Sun', 112),
(1123, 'Hope', 112),
(1124, 'Chuck', 112),
(1125, 'Alex', 112);
#how we'd get hierarchy
SELECT m1.name,
m2.name AS boss,
(length(cast(m1.member_id AS char(5))) - 2) AS tier
FROM members m1
LEFT JOIN members m2
ON m1.parent_member_id = m2.member_id
WHERE cast(m1.member_id AS char(5)) LIKE '112%'
ORDER BY m1.member_id;
#make more realistic data
TRUNCATE TABLE members;
INSERT INTO members (member_id, name, parent_member_id)
VALUES (1, 'TJ', null),
(2, 'Jonathan F', 2),
(3, 'Ben S', 2),
(19, 'Wendy', 2),
(20, 'Mellisa', 20),
(14, 'Greta', 1),
(21, 'Zach', 14),
(4, 'Aaron', 2),
(5, 'Ben F', 2),
(6, 'Neel', 2),
(7, 'Kim', 4),
(8, 'Hayley', 7),
(9, 'Ben O', 4),
(10, 'Jonathan C', 4),
(11, 'Mathias', 4),
(12, 'Brendan', 5),
(13, 'Chas', 12),
(15, 'Yun-Sun', 5),
(16, 'Hope', 5),
(17, 'Chuck', 5),
(18, 'Alex', 5);
#yikes how to get hierarchy here?
SELECT * FROM members;
#recursion!
WITH RECURSIVE bosses (member_id, name, parent_member_id, boss, tier) AS (
#initialization
SELECT m.member_id, m.name, m.parent_member_id, b.name AS boss, 1 AS tier
FROM members m
JOIN members b
ON m.parent_member_id = b.member_id
WHERE m.name = 'Ben F'
UNION ALL
#recursive execution
SELECT m.member_id , m.name, m.parent_member_id, b.name AS boss, tier + 1 AS tier
FROM members m
JOIN bosses b
ON m.parent_member_id = b.member_id
)
SELECT name, boss, tier FROM bosses
ORDER BY tier;
DROP DATABASE demo;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment