RelationalDesign: dml.sql

File dml.sql, 18.6 KB (added by 231175, 12 hours ago)
Line 
1-- =============================================================================
2-- DML — synthetic dataset, generate_series only (no PL/pgSQL loops)
3-- Approx. row counts:
4-- language 3
5-- account 500,000
6-- "user" 450,000
7-- expert 50,000
8-- course 2,000
9-- course_version 6,000
10-- course_translate 6,000
11-- course_content 36,000
12-- course_content_translate 108,000
13-- course_lecture 288,000
14-- course_lecture_translate 864,000
15-- enrollment 800,000
16-- payment 800,000
17-- review ~160,000
18-- user_course_progress ~960,000
19-- verification_token 150,000
20-- tag 500
21-- tag_translate 1,500
22-- user_tag 900,000
23-- course_tag 6,000
24-- meeting_email_reminder 250,000
25-- user_favorite_course 900,000
26-- -------------------------------------
27-- TOTAL ~7,200,000 rows
28--
29-- All IDs are explicit and deterministic so cross-table references can be
30-- computed arithmetically; identity sequences are re-synced at the end.
31-- Run after 01_ddl_v2.sql.
32-- =============================================================================
33
34SET synchronous_commit = off;
35
36TRUNCATE TABLE
37 user_favorite_course, meeting_email_reminder, course_tag, user_tag,
38 tag_translate, tag, verification_token, user_course_progress, review,
39 payment, enrollment, course_lecture_translate, course_lecture,
40 course_content_translate, course_content, course_translate, course_version,
41 course, expert, "user", account, language
42 RESTART IDENTITY CASCADE;
43
44-- -----------------------------------------------------------------------------
45-- 22. language — 3 rows
46-- -----------------------------------------------------------------------------
47INSERT INTO language (id, value) VALUES
48 (1, 'mk'),
49 (2, 'en'),
50 (3, 'sq');
51
52-- -----------------------------------------------------------------------------
53-- 1. account — 500,000 rows
54-- 1 .. 450,000 -> "user"
55-- 450,001 .. 500,000 -> expert
56-- -----------------------------------------------------------------------------
57INSERT INTO account (id, email, password_hash, name)
58SELECT g,
59 'account' || g || '@shifter.mk',
60 md5('pwd::' || g),
61 'Account Holder ' || g
62FROM generate_series(1, 500000) AS g;
63
64-- -----------------------------------------------------------------------------
65-- 2. "user" — 450,000 rows
66-- -----------------------------------------------------------------------------
67INSERT INTO "user" (user_id, name, email, password_hash, login_provider,
68 is_verified, is_profile_complete, has_used_free_consultation,
69 work_position, company_size, points, account_id)
70SELECT g,
71 'Account Holder ' || g,
72 'account' || g || '@shifter.mk',
73 md5('pwd::' || g),
74 (ARRAY['local','google']::login_provider[])[1 + (g % 2)],
75 (g % 10) <> 0,
76 (g % 4) <> 0,
77 (g % 7) = 0,
78 (ARRAY['CEO','CTO','Product Manager','Team Lead','Software Engineer',
79 'Marketing Specialist','HR Manager','Accountant'])[1 + (g % 8)],
80 (ARRAY['freelance','micro','small','medium','mid_market','enterprise','other']
81 ::company_size[])[1 + (g % 7)],
82 (g * 37) % 5000,
83 g
84FROM generate_series(1, 450000) AS g;
85
86-- -----------------------------------------------------------------------------
87-- 3. expert — 50,000 rows
88-- -----------------------------------------------------------------------------
89INSERT INTO expert (expert_id, account_id)
90SELECT g, 450000 + g
91FROM generate_series(1, 50000) AS g;
92
93-- -----------------------------------------------------------------------------
94-- 8. course — 2,000 rows
95-- -----------------------------------------------------------------------------
96INSERT INTO course (course_id, color, difficulty, duration_minutes, image_url, price)
97SELECT g,
98 '#' || lpad(to_hex((g * 7919) % 16777216), 6, '0'),
99 (ARRAY['beginner','intermediate','advanced','expert']::difficulty[])[1 + (g % 4)],
100 60 + (g % 24) * 15,
101 'https://cdn.shifter.mk/courses/' || g || '/cover.webp',
102 (20 + (g % 40) * 5)::numeric(12,2)
103FROM generate_series(1, 2000) AS g;
104
105-- -----------------------------------------------------------------------------
106-- 7. course_version — 6,000 rows (3 per course; version 3 is the active one)
107-- course_version_id = (course_id - 1) * 3 + version_number
108-- -----------------------------------------------------------------------------
109INSERT INTO course_version (course_version_id, version_number, created_at, is_active, course_id)
110SELECT (c - 1) * 3 + v,
111 v,
112 DATE '2022-01-01' + ((c % 700) + v * 30),
113 (v = 3),
114 c
115FROM generate_series(1, 2000) AS c
116 CROSS JOIN generate_series(1, 3) AS v;
117
118-- -----------------------------------------------------------------------------
119-- 9. course_translate — 6,000 rows (2,000 courses x 3 languages)
120-- -----------------------------------------------------------------------------
121INSERT INTO course_translate (course_translate_id, description_short, description,
122 description_long, title_short, title,
123 what_will_be_learned, course_id, language_id)
124SELECT (c - 1) * 3 + l,
125 'Short description for course ' || c || ' (lang ' || l || ')',
126 'Description for course ' || c || ' (lang ' || l || '). '
127 || repeat('Content paragraph. ', 5),
128 'Long description for course ' || c || ' (lang ' || l || '). '
129 || repeat('Extended content paragraph. ', 15),
130 'Course ' || c,
131 'Course ' || c || ' — Full Title (lang ' || l || ')',
132 ARRAY['Outcome A of course ' || c,
133 'Outcome B of course ' || c,
134 'Outcome C of course ' || c],
135 c,
136 l
137FROM generate_series(1, 2000) AS c
138 CROSS JOIN generate_series(1, 3) AS l;
139
140-- -----------------------------------------------------------------------------
141-- 10. course_content — 36,000 rows (6 modules per version)
142-- course_content_id = (course_version_id - 1) * 6 + position
143-- -----------------------------------------------------------------------------
144INSERT INTO course_content (course_content_id, position, course_version_id)
145SELECT (cv - 1) * 6 + p, p, cv
146FROM generate_series(1, 6000) AS cv
147 CROSS JOIN generate_series(1, 6) AS p;
148
149-- -----------------------------------------------------------------------------
150-- 11. course_content_translate — 108,000 rows
151-- -----------------------------------------------------------------------------
152INSERT INTO course_content_translate (course_content_translate_id, title,
153 course_content_id, language_id)
154SELECT (cc - 1) * 3 + l,
155 'Module ' || (((cc - 1) % 6) + 1) || ' (lang ' || l || ')',
156 cc,
157 l
158FROM generate_series(1, 36000) AS cc
159 CROSS JOIN generate_series(1, 3) AS l;
160
161-- -----------------------------------------------------------------------------
162-- 12. course_lecture — 288,000 rows (8 lectures per module)
163-- course_lecture_id = (course_content_id - 1) * 8 + position
164-- -----------------------------------------------------------------------------
165INSERT INTO course_lecture (course_lecture_id, position, duration_minutes,
166 content_type, course_content_id)
167SELECT (cc - 1) * 8 + p,
168 p,
169 5 + ((cc + p) % 12) * 5,
170 (ARRAY['text','file','video','quiz']::content_type[])[1 + ((cc + p) % 4)],
171 cc
172FROM generate_series(1, 36000) AS cc
173 CROSS JOIN generate_series(1, 8) AS p;
174
175-- -----------------------------------------------------------------------------
176-- 13. course_lecture_translate — 864,000 rows
177-- -----------------------------------------------------------------------------
178INSERT INTO course_lecture_translate (course_lecture_translate_id, content_file_name,
179 content_text, description, title,
180 course_lecture_id, language_id)
181SELECT (cl.course_lecture_id - 1) * 3 + l,
182 CASE WHEN cl.content_type IN ('file','video')
183 THEN 'lecture_' || cl.course_lecture_id || '_' || l
184 || CASE WHEN cl.content_type = 'video' THEN '.mp4' ELSE '.pdf' END
185 END,
186 CASE WHEN cl.content_type IN ('text','quiz')
187 THEN 'Body text for lecture ' || cl.course_lecture_id
188 || ' (lang ' || l || '). ' || repeat('Lorem ipsum dolor sit amet. ', 8)
189 END,
190 'Description of lecture ' || cl.course_lecture_id || ' (lang ' || l || ')',
191 'Lecture ' || cl.position || ' (lang ' || l || ')',
192 cl.course_lecture_id,
193 l
194FROM course_lecture cl
195 CROSS JOIN generate_series(1, 3) AS l;
196
197-- -----------------------------------------------------------------------------
198-- 4. enrollment — 800,000 rows
199-- user_id = ((e - 1) % 450000) + 1
200-- k = (e - 1) / 450000 (0 or 1 -> 1st / 2nd enrollment)
201-- course_version_id = ((user_slot * 7919 + k * 3001) % 6000) + 1
202-- The +3001 offset guarantees the (user_id, course_version_id) pair is unique.
203-- -----------------------------------------------------------------------------
204INSERT INTO enrollment (enrollment_id, enrollment_status, activation_date,
205 completion_date, purchase_date, user_id, course_version_id)
206SELECT e,
207 CASE WHEN e % 10 < 2 THEN 'pending'
208 WHEN e % 10 < 5 THEN 'active'
209 ELSE 'completed' END::enrollment_status,
210 CASE WHEN e % 10 >= 2
211 THEN DATE '2023-01-01' + (e % 900) + (e % 5) END,
212 CASE WHEN e % 10 >= 5
213 THEN DATE '2023-01-01' + (e % 900) + (e % 5) + 14 + (e % 60) END,
214 DATE '2023-01-01' + (e % 900),
215 ((e - 1) % 450000) + 1,
216 (((((e - 1) % 450000)::bigint * 7919) + ((e - 1) / 450000) * 3001) % 6000) + 1
217FROM generate_series(1, 800000) AS e;
218
219-- -----------------------------------------------------------------------------
220-- 5. payment — 800,000 rows (payment_id mirrors enrollment_id)
221-- -----------------------------------------------------------------------------
222INSERT INTO payment (payment_id, amount, payment_date, payment_method,
223 payment_status, enrollment_id)
224SELECT e.enrollment_id,
225 c.price,
226 e.purchase_date,
227 (ARRAY['card','paypal','casys']::payment_method[])[1 + (e.enrollment_id % 3)],
228 CASE WHEN e.enrollment_status = 'pending' THEN 'pending'
229 WHEN e.enrollment_id % 50 = 0 THEN 'failed'
230 ELSE 'completed' END::payment_status,
231 e.enrollment_id
232FROM enrollment e
233 JOIN course_version cv ON cv.course_version_id = e.course_version_id
234 JOIN course c ON c.course_id = cv.course_id;
235
236-- -----------------------------------------------------------------------------
237-- 6. review — ~160,000 rows (completed enrollments with an even id)
238-- -----------------------------------------------------------------------------
239INSERT INTO review (review_id, rating, comment, review_date, enrollment_id)
240SELECT e.enrollment_id,
241 1 + (e.enrollment_id % 5),
242 'Review text for enrollment ' || e.enrollment_id || '. '
243 || (ARRAY['Very useful.','Solid material.','Could be deeper.',
244 'Great instructor.','Too basic for me.'])[1 + (e.enrollment_id % 5)],
245 e.completion_date + 3,
246 e.enrollment_id
247FROM enrollment e
248WHERE e.enrollment_status = 'completed'
249 AND e.enrollment_id % 2 = 0;
250
251-- -----------------------------------------------------------------------------
252-- 14. user_course_progress — ~960,000 rows
253-- First 3 lectures of each non-pending enrollment among ids 1..400,000.
254-- Lecture id of module 1 of a version: (course_version_id - 1) * 48 + p
255-- -----------------------------------------------------------------------------
256INSERT INTO user_course_progress (user_course_progress_id, is_completed,
257 completed_at, course_lecture_id, enrollment_id)
258SELECT (e.enrollment_id - 1) * 3 + p,
259 (p < 3),
260 CASE WHEN p < 3
261 THEN (e.activation_date + p)::timestamp + interval '9 hours' END,
262 (e.course_version_id - 1) * 48 + p,
263 e.enrollment_id
264FROM enrollment e
265 CROSS JOIN generate_series(1, 3) AS p
266WHERE e.enrollment_id <= 400000
267 AND e.enrollment_status <> 'pending';
268
269-- -----------------------------------------------------------------------------
270-- 15. verification_token — 150,000 rows
271-- -----------------------------------------------------------------------------
272INSERT INTO verification_token (verification_token_uuid, expires_at, created_at, user_id)
273SELECT gen_random_uuid(),
274 TIMESTAMP '2024-01-01 00:00:00' + (g % 500) * interval '1 day' + interval '24 hours',
275 TIMESTAMP '2024-01-01 00:00:00' + (g % 500) * interval '1 day',
276 g
277FROM generate_series(1, 450000) AS g
278WHERE g % 3 = 0;
279
280-- -----------------------------------------------------------------------------
281-- 16. tag — 500 rows
282-- -----------------------------------------------------------------------------
283INSERT INTO tag (tag_id, tag_type)
284SELECT g, (ARRAY['skill','topic']::tag_type[])[1 + (g % 2)]
285FROM generate_series(1, 500) AS g;
286
287-- -----------------------------------------------------------------------------
288-- 17. tag_translate — 1,500 rows
289-- -----------------------------------------------------------------------------
290INSERT INTO tag_translate (tag_translate_id, value, tag_id, language_id)
291SELECT (t - 1) * 3 + l,
292 'tag_' || t || '_lang_' || l,
293 t,
294 l
295FROM generate_series(1, 500) AS t
296 CROSS JOIN generate_series(1, 3) AS l;
297
298-- -----------------------------------------------------------------------------
299-- 18. user_tag — 900,000 rows (2 distinct tags per user)
300-- -----------------------------------------------------------------------------
301INSERT INTO user_tag (tag_id, user_id)
302SELECT ((u * 7 + k * 13) % 500) + 1, u
303FROM generate_series(1, 450000) AS u
304 CROSS JOIN generate_series(1, 2) AS k;
305
306-- -----------------------------------------------------------------------------
307-- 19. course_tag — 6,000 rows (3 distinct tags per course)
308-- -----------------------------------------------------------------------------
309INSERT INTO course_tag (tag_id, course_id)
310SELECT ((c * 11 + k * 17) % 500) + 1, c
311FROM generate_series(1, 2000) AS c
312 CROSS JOIN generate_series(1, 3) AS k;
313
314-- -----------------------------------------------------------------------------
315-- 20. meeting_email_reminder — 250,000 rows
316-- -----------------------------------------------------------------------------
317INSERT INTO meeting_email_reminder (id, meeting_at, scheduled_at, sent, meeting_link, user_id)
318SELECT g,
319 TIMESTAMP '2025-01-01 08:00:00' + (g % 400) * interval '1 day' + (g % 9) * interval '1 hour',
320 TIMESTAMP '2025-01-01 08:00:00' + (g % 400) * interval '1 day' + (g % 9) * interval '1 hour'
321 - interval '1 day',
322 (g % 3) <> 0,
323 'https://meet.shifter.mk/' || md5('meeting::' || g),
324 ((g * 7) % 450000) + 1
325FROM generate_series(1, 250000) AS g;
326
327-- -----------------------------------------------------------------------------
328-- 21. user_favorite_course — 900,000 rows (2 distinct courses per user)
329-- -----------------------------------------------------------------------------
330INSERT INTO user_favorite_course (user_id, course_id)
331SELECT u, ((u * 13 + k * 331) % 2000) + 1
332FROM generate_series(1, 450000) AS u
333 CROSS JOIN generate_series(1, 2) AS k;
334
335-- -----------------------------------------------------------------------------
336-- Re-sync identity sequences after explicit-ID inserts
337-- -----------------------------------------------------------------------------
338SELECT setval(pg_get_serial_sequence('language', 'id'), (SELECT MAX(id) FROM language));
339SELECT setval(pg_get_serial_sequence('account', 'id'), (SELECT MAX(id) FROM account));
340SELECT setval(pg_get_serial_sequence('"user"', 'user_id'), (SELECT MAX(user_id) FROM "user"));
341SELECT setval(pg_get_serial_sequence('expert', 'expert_id'), (SELECT MAX(expert_id) FROM expert));
342SELECT setval(pg_get_serial_sequence('course', 'course_id'), (SELECT MAX(course_id) FROM course));
343SELECT setval(pg_get_serial_sequence('course_version', 'course_version_id'), (SELECT MAX(course_version_id) FROM course_version));
344SELECT setval(pg_get_serial_sequence('course_translate', 'course_translate_id'), (SELECT MAX(course_translate_id) FROM course_translate));
345SELECT setval(pg_get_serial_sequence('course_content', 'course_content_id'), (SELECT MAX(course_content_id) FROM course_content));
346SELECT setval(pg_get_serial_sequence('course_content_translate', 'course_content_translate_id'), (SELECT MAX(course_content_translate_id) FROM course_content_translate));
347SELECT setval(pg_get_serial_sequence('course_lecture', 'course_lecture_id'), (SELECT MAX(course_lecture_id) FROM course_lecture));
348SELECT setval(pg_get_serial_sequence('course_lecture_translate', 'course_lecture_translate_id'), (SELECT MAX(course_lecture_translate_id) FROM course_lecture_translate));
349SELECT setval(pg_get_serial_sequence('enrollment', 'enrollment_id'), (SELECT MAX(enrollment_id) FROM enrollment));
350SELECT setval(pg_get_serial_sequence('payment', 'payment_id'), (SELECT MAX(payment_id) FROM payment));
351SELECT setval(pg_get_serial_sequence('review', 'review_id'), (SELECT MAX(review_id) FROM review));
352SELECT setval(pg_get_serial_sequence('user_course_progress', 'user_course_progress_id'), (SELECT MAX(user_course_progress_id) FROM user_course_progress));
353SELECT setval(pg_get_serial_sequence('tag', 'tag_id'), (SELECT MAX(tag_id) FROM tag));
354SELECT setval(pg_get_serial_sequence('tag_translate', 'tag_translate_id'), (SELECT MAX(tag_translate_id) FROM tag_translate));
355SELECT setval(pg_get_serial_sequence('meeting_email_reminder', 'id'), (SELECT MAX(id) FROM meeting_email_reminder));
356
357VACUUM ANALYZE;