freshmart.slt 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957
  1. # Copyright Materialize, Inc. and contributors. All rights reserved.
  2. #
  3. # Use of this software is governed by the Business Source License
  4. # included in the LICENSE file at the root of this repository.
  5. #
  6. # As of the Change Date specified in that file, in accordance with
  7. # the Business Source License, use of this software will be governed
  8. # by the Apache License, Version 2.0.
  9. statement ok
  10. CREATE TABLE materialized_view_refresh_log (
  11. view_name TEXT,
  12. last_refresh TIMESTAMP DEFAULT now(),
  13. refresh_duration DOUBLE PRECISION DEFAULT 0
  14. );
  15. statement ok
  16. CREATE TABLE products (
  17. product_id INTEGER NOT NULL,
  18. product_name VARCHAR(255) NOT NULL,
  19. base_price NUMERIC(10, 2) NOT NULL,
  20. category_id INTEGER NOT NULL,
  21. supplier_id INTEGER NOT NULL,
  22. available BOOLEAN NOT NULL,
  23. last_update_time TIMESTAMP WITH TIME ZONE DEFAULT NOW()
  24. );
  25. statement ok
  26. CREATE TABLE categories (
  27. category_id INTEGER NOT NULL,
  28. category_name VARCHAR(255) NOT NULL,
  29. parent_id INT
  30. );
  31. statement ok
  32. CREATE TABLE suppliers (
  33. supplier_id INTEGER NOT NULL,
  34. supplier_name VARCHAR(255) NOT NULL
  35. );
  36. statement ok
  37. CREATE TABLE sales (
  38. sale_id INTEGER NOT NULL,
  39. product_id INTEGER NOT NULL,
  40. sale_price NUMERIC(10, 2) NOT NULL,
  41. sale_date TIMESTAMP NOT NULL,
  42. price NUMERIC(10, 2) NOT NULL
  43. );
  44. statement ok
  45. CREATE TABLE inventory (
  46. inventory_id INTEGER NOT NULL,
  47. product_id INTEGER NOT NULL,
  48. stock INTEGER NOT NULL,
  49. warehouse_id INTEGER NOT NULL,
  50. restock_date TIMESTAMP NOT NULL
  51. );
  52. statement ok
  53. CREATE TABLE promotions (
  54. promotion_id INTEGER NOT NULL,
  55. product_id INTEGER NOT NULL,
  56. promotion_discount NUMERIC(10, 2) NOT NULL,
  57. start_date TIMESTAMP NOT NULL,
  58. end_date TIMESTAMP NOT NULL,
  59. active BOOLEAN NOT NULL,
  60. updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
  61. );
  62. statement ok
  63. CREATE TABLE shopping_cart (
  64. product_id INT NOT NULL,
  65. product_name TEXT NOT NULL,
  66. category_id INT NOT NULL,
  67. price NUMERIC(10, 2) NOT NULL,
  68. ts TIMESTAMP NOT NULL DEFAULT NOW()
  69. );
  70. statement ok
  71. CREATE INDEX idx_products_product_name ON products(product_name);
  72. statement ok
  73. CREATE INDEX idx_sales_product_id ON sales(product_id);
  74. statement ok
  75. CREATE INDEX idx_sales_sale_date ON sales(sale_date);
  76. statement ok
  77. CREATE INDEX idx_sales_product_id_sale_date ON sales(product_id, sale_date);
  78. statement ok
  79. CREATE INDEX idx_promotions_product_id ON promotions(product_id);
  80. statement ok
  81. CREATE INDEX idx_promotions_active ON promotions(active);
  82. statement ok
  83. CREATE INDEX idx_promotions_product_id_active ON promotions(product_id, active);
  84. statement ok
  85. CREATE INDEX idx_inventory_product_id ON inventory(product_id);
  86. query T multiline
  87. EXPLAIN OPTIMIZED PLAN WITH (humanized expressions) AS VERBOSE TEXT FOR
  88. WITH recent_prices AS (
  89. SELECT grp.product_id, AVG(price) AS avg_price
  90. FROM (SELECT DISTINCT product_id FROM sales) grp,
  91. LATERAL (
  92. SELECT product_id, price
  93. FROM sales
  94. WHERE sales.product_id = grp.product_id
  95. ORDER BY sale_date DESC LIMIT 10
  96. ) sub
  97. GROUP BY grp.product_id
  98. ),
  99. promotion_effect AS (
  100. SELECT
  101. p.product_id,
  102. MIN(pr.promotion_discount) AS promotion_discount
  103. FROM promotions pr
  104. JOIN products p ON pr.product_id = p.product_id
  105. WHERE pr.active = TRUE
  106. GROUP BY p.product_id
  107. ),
  108. popularity_score AS (
  109. SELECT
  110. s.product_id,
  111. RANK() OVER (PARTITION BY p.category_id ORDER BY COUNT(s.sale_id) DESC) AS popularity_rank,
  112. COUNT(s.sale_id) AS sale_count
  113. FROM sales s
  114. JOIN products p ON s.product_id = p.product_id
  115. GROUP BY s.product_id, p.category_id
  116. ),
  117. inventory_status AS (
  118. SELECT
  119. i.product_id,
  120. SUM(i.stock) AS total_stock,
  121. RANK() OVER (ORDER BY SUM(i.stock) DESC) AS stock_rank
  122. FROM inventory i
  123. GROUP BY i.product_id
  124. ),
  125. high_demand_products AS (
  126. SELECT
  127. p.product_id,
  128. AVG(s.sale_price) AS avg_sale_price,
  129. COUNT(s.sale_id) AS total_sales
  130. FROM products p
  131. JOIN sales s ON p.product_id = s.product_id
  132. GROUP BY p.product_id
  133. HAVING COUNT(s.sale_id) > (SELECT AVG(total_sales) FROM (SELECT COUNT(*) AS total_sales FROM sales GROUP BY product_id) subquery)
  134. ),
  135. dynamic_pricing AS (
  136. SELECT
  137. p.product_id,
  138. p.base_price,
  139. CASE
  140. WHEN pop.popularity_rank <= 3 THEN 1.2
  141. WHEN pop.popularity_rank BETWEEN 4 AND 10 THEN 1.1
  142. ELSE 0.9
  143. END AS popularity_adjustment,
  144. rp.avg_price,
  145. COALESCE(1.0 - (pe.promotion_discount / 100), 1) AS promotion_discount,
  146. CASE
  147. WHEN inv.stock_rank <= 3 THEN 1.1
  148. WHEN inv.stock_rank BETWEEN 4 AND 10 THEN 1.05
  149. ELSE 1
  150. END AS stock_adjustment,
  151. CASE
  152. WHEN p.base_price > rp.avg_price THEN 1 + (p.base_price - rp.avg_price) / rp.avg_price
  153. ELSE 1 - (rp.avg_price - p.base_price) / rp.avg_price
  154. END AS demand_multiplier,
  155. hd.avg_sale_price,
  156. CASE
  157. WHEN p.product_name ilike '%cheap%' THEN 0.8
  158. ELSE 1.0
  159. END AS additional_discount
  160. FROM products p
  161. LEFT JOIN recent_prices rp ON p.product_id = rp.product_id
  162. LEFT JOIN promotion_effect pe ON p.product_id = pe.product_id
  163. JOIN popularity_score pop ON p.product_id = pop.product_id
  164. LEFT JOIN inventory_status inv ON p.product_id = inv.product_id
  165. LEFT JOIN high_demand_products hd ON p.product_id = hd.product_id
  166. )
  167. SELECT
  168. dp.product_id,
  169. dp.base_price * dp.popularity_adjustment * dp.promotion_discount * dp.stock_adjustment * dp.demand_multiplier * dp.additional_discount AS adjusted_price,
  170. p.last_update_time
  171. FROM dynamic_pricing dp
  172. JOIN products p ON dp.product_id = p.product_id;
  173. ----
  174. Explained Query:
  175. With
  176. cte l0 =
  177. Project (#0{product_id}, #3)
  178. Map ((#1{sum_price} / bigint_to_numeric(case when (#2{count} = 0) then null else #2{count} end)))
  179. Reduce group_by=[#0{product_id}] aggregates=[sum(#1{price}), count(*)]
  180. Project (#0{product_id}, #2{price})
  181. TopK group_by=[#0{product_id}] order_by=[#1{sale_date} desc nulls_first] limit=10
  182. Project (#1{product_id}, #3{sale_date}, #4{price})
  183. ReadIndex on=sales idx_sales_product_id=[*** full scan ***]
  184. cte l1 =
  185. Project (#0{product_id})
  186. ReadIndex on=products idx_products_product_name=[*** full scan ***]
  187. cte l2 =
  188. ArrangeBy keys=[[#0{product_id}]]
  189. Get l1
  190. cte l3 =
  191. Reduce group_by=[#0{product_id}] aggregates=[min(#1{promotion_discount})]
  192. Project (#1{product_id}, #2{promotion_discount})
  193. Join on=(#1{product_id} = #8{product_id}) type=differential
  194. ArrangeBy keys=[[#1{product_id}]]
  195. ReadIndex on=materialize.public.promotions idx_promotions_active=[lookup value=(true)]
  196. Get l2
  197. cte l4 =
  198. Distinct project=[#0{product_id}]
  199. Get l1
  200. cte l5 =
  201. ArrangeBy keys=[[#1{product_id}]]
  202. ReadIndex on=sales idx_sales_product_id=[differential join]
  203. cte l6 =
  204. Project (#0{product_id}..=#2{base_price}, #10..=#12)
  205. Map (case when (#5) IS NULL then null else #4 end, case when (#8) IS NULL then null else #7{min_promotion_discount} end)
  206. Join on=(#0{product_id} = #3{product_id} = #6{product_id} = #9{product_id}) type=delta
  207. ArrangeBy keys=[[#0{product_id}]]
  208. Project (#0{product_id}..=#2{base_price})
  209. ReadIndex on=products idx_products_product_name=[*** full scan ***]
  210. ArrangeBy keys=[[#0{product_id}]]
  211. Union
  212. Map (true)
  213. Get l0
  214. Map (null, null)
  215. Threshold
  216. Union
  217. Negate
  218. Project (#0{product_id})
  219. Get l0
  220. Get l4
  221. ArrangeBy keys=[[#0{product_id}]]
  222. Union
  223. Map (true)
  224. Get l3
  225. Map (null, null)
  226. Threshold
  227. Union
  228. Negate
  229. Project (#0{product_id})
  230. Get l3
  231. Get l4
  232. ArrangeBy keys=[[#0{product_id}]]
  233. Project (#2, #3)
  234. Filter (#2) IS NOT NULL
  235. Map (record_get[0](record_get[1](#1)), record_get[0](#1))
  236. FlatMap unnest_list(#0{rank})
  237. Project (#1{rank})
  238. Reduce group_by=[#1{category_id}] aggregates=[rank[order_by=[#0{product_id} desc nulls_first]](row(list[row(#0{product_id}, #1{category_id}, #2{count})], #2{count}))]
  239. Reduce group_by=[#0{product_id}, #1{category_id}] aggregates=[count(*)]
  240. Project (#1{product_id}, #6{category_id})
  241. Join on=(#1{product_id} = #5{product_id}) type=differential
  242. Get l5
  243. ArrangeBy keys=[[#0{product_id}]]
  244. Project (#0{product_id}, #3{category_id})
  245. ReadIndex on=products idx_products_product_name=[*** full scan ***]
  246. cte l7 =
  247. Project (#2, #3)
  248. Filter (#2) IS NOT NULL
  249. Map (record_get[0](record_get[1](#1)), record_get[0](#1))
  250. FlatMap unnest_list(#0{rank})
  251. Reduce aggregates=[rank[order_by=[#0{product_id} desc nulls_first]](row(list[row(#0{product_id}, #1{sum_stock})], #1{sum_stock}))]
  252. Reduce group_by=[#0{product_id}] aggregates=[sum(#1{stock})]
  253. Project (#1{product_id}, #2{stock})
  254. ReadIndex on=inventory idx_inventory_product_id=[*** full scan ***]
  255. cte l8 =
  256. Project (#0{product_id})
  257. Filter (bigint_to_numeric(#1{count}) > (#2{sum_count} / bigint_to_numeric(case when (#3{count} = 0) then null else #3{count} end)))
  258. CrossJoin type=differential
  259. ArrangeBy keys=[[]]
  260. Reduce group_by=[#0{product_id}] aggregates=[count(*)]
  261. Project (#0{product_id})
  262. Join on=(#0{product_id} = #2{product_id}) type=differential
  263. Get l2
  264. Get l5
  265. ArrangeBy keys=[[]]
  266. Reduce aggregates=[sum(#0{count}), count(*)]
  267. Project (#1{count})
  268. Reduce group_by=[#0{product_id}] aggregates=[count(*)]
  269. Project (#1{product_id})
  270. ReadIndex on=sales idx_sales_product_id=[*** full scan ***]
  271. cte l9 =
  272. Distinct project=[#0{product_id}]
  273. Project (#0{product_id})
  274. Get l6
  275. Return
  276. Project (#0{product_id}, #13, #11{last_update_time})
  277. Map (case when (#8) IS NULL then null else #7 end, (((((#2{base_price} * case when (#3{popularity_rank} <= 3) then 1.2 else case when ((#3{popularity_rank} <= 10) AND (#3{popularity_rank} >= 4)) then 1.1 else 0.9 end end) * coalesce((1 - (#5{promotion_discount} / 100)), 1)) * case when (#12{stock_rank} <= 3) then 1.1 else case when ((#12 <= 10) AND (#12 >= 4)) then 1.05 else 1 end end) * case when (#2{base_price} > #4{avg_price}) then (1 + ((#2{base_price} - #4{avg_price}) / #4{avg_price})) else (1 - ((#4{avg_price} - #2{base_price}) / #4{avg_price})) end) * case when ilike["%cheap%"](varchar_to_text(#1{product_name})) then 0.8 else 1 end))
  278. Join on=(#0{product_id} = #6{product_id} = #9{product_id} = #10{product_id}) type=delta
  279. ArrangeBy keys=[[#0{product_id}]]
  280. Get l6
  281. ArrangeBy keys=[[#0{product_id}]]
  282. Union
  283. Map (true)
  284. Get l7
  285. Map (null, null)
  286. Threshold
  287. Union
  288. Negate
  289. Project (#0)
  290. Get l7
  291. Get l9
  292. ArrangeBy keys=[[#0{product_id}]]
  293. Union
  294. Get l8
  295. Threshold
  296. Union
  297. Negate
  298. Get l8
  299. Get l9
  300. ArrangeBy keys=[[#0{product_id}]]
  301. Project (#0{product_id}, #6{last_update_time})
  302. ReadIndex on=products idx_products_product_name=[*** full scan ***]
  303. Used Indexes:
  304. - materialize.public.idx_products_product_name (*** full scan ***)
  305. - materialize.public.idx_sales_product_id (*** full scan ***, differential join)
  306. - materialize.public.idx_promotions_active (lookup)
  307. - materialize.public.idx_inventory_product_id (*** full scan ***)
  308. Target cluster: quickstart
  309. EOF
  310. statement ok
  311. CREATE VIEW dynamic_pricing AS
  312. WITH recent_prices AS (
  313. SELECT grp.product_id, AVG(price) AS avg_price
  314. FROM (SELECT DISTINCT product_id FROM sales) grp,
  315. LATERAL (
  316. SELECT product_id, price
  317. FROM sales
  318. WHERE sales.product_id = grp.product_id
  319. ORDER BY sale_date DESC LIMIT 10
  320. ) sub
  321. GROUP BY grp.product_id
  322. ),
  323. promotion_effect AS (
  324. SELECT
  325. p.product_id,
  326. MIN(pr.promotion_discount) AS promotion_discount
  327. FROM promotions pr
  328. JOIN products p ON pr.product_id = p.product_id
  329. WHERE pr.active = TRUE
  330. GROUP BY p.product_id
  331. ),
  332. popularity_score AS (
  333. SELECT
  334. s.product_id,
  335. RANK() OVER (PARTITION BY p.category_id ORDER BY COUNT(s.sale_id) DESC) AS popularity_rank,
  336. COUNT(s.sale_id) AS sale_count
  337. FROM sales s
  338. JOIN products p ON s.product_id = p.product_id
  339. GROUP BY s.product_id, p.category_id
  340. ),
  341. inventory_status AS (
  342. SELECT
  343. i.product_id,
  344. SUM(i.stock) AS total_stock,
  345. RANK() OVER (ORDER BY SUM(i.stock) DESC) AS stock_rank
  346. FROM inventory i
  347. GROUP BY i.product_id
  348. ),
  349. high_demand_products AS (
  350. SELECT
  351. p.product_id,
  352. AVG(s.sale_price) AS avg_sale_price,
  353. COUNT(s.sale_id) AS total_sales
  354. FROM products p
  355. JOIN sales s ON p.product_id = s.product_id
  356. GROUP BY p.product_id
  357. HAVING COUNT(s.sale_id) > (SELECT AVG(total_sales) FROM (SELECT COUNT(*) AS total_sales FROM sales GROUP BY product_id) subquery)
  358. ),
  359. dynamic_pricing AS (
  360. SELECT
  361. p.product_id,
  362. p.base_price,
  363. CASE
  364. WHEN pop.popularity_rank <= 3 THEN 1.2
  365. WHEN pop.popularity_rank BETWEEN 4 AND 10 THEN 1.1
  366. ELSE 0.9
  367. END AS popularity_adjustment,
  368. rp.avg_price,
  369. COALESCE(1.0 - (pe.promotion_discount / 100), 1) AS promotion_discount,
  370. CASE
  371. WHEN inv.stock_rank <= 3 THEN 1.1
  372. WHEN inv.stock_rank BETWEEN 4 AND 10 THEN 1.05
  373. ELSE 1
  374. END AS stock_adjustment,
  375. CASE
  376. WHEN p.base_price > rp.avg_price THEN 1 + (p.base_price - rp.avg_price) / rp.avg_price
  377. ELSE 1 - (rp.avg_price - p.base_price) / rp.avg_price
  378. END AS demand_multiplier,
  379. hd.avg_sale_price,
  380. CASE
  381. WHEN p.product_name ilike '%cheap%' THEN 0.8
  382. ELSE 1.0
  383. END AS additional_discount
  384. FROM products p
  385. LEFT JOIN recent_prices rp ON p.product_id = rp.product_id
  386. LEFT JOIN promotion_effect pe ON p.product_id = pe.product_id
  387. JOIN popularity_score pop ON p.product_id = pop.product_id
  388. LEFT JOIN inventory_status inv ON p.product_id = inv.product_id
  389. LEFT JOIN high_demand_products hd ON p.product_id = hd.product_id
  390. )
  391. SELECT
  392. dp.product_id,
  393. dp.base_price * dp.popularity_adjustment * dp.promotion_discount * dp.stock_adjustment * dp.demand_multiplier * dp.additional_discount AS adjusted_price,
  394. p.last_update_time
  395. FROM dynamic_pricing dp
  396. JOIN products p ON dp.product_id = p.product_id;
  397. statement ok
  398. CREATE INDEX IF NOT EXISTS dynamic_pricing_product_id_idx ON dynamic_pricing (product_id);
  399. statement ok
  400. CREATE MATERIALIZED VIEW mv_dynamic_pricing AS
  401. WITH recent_prices AS (
  402. SELECT grp.product_id, AVG(price) AS avg_price
  403. FROM (SELECT DISTINCT product_id FROM sales) grp,
  404. LATERAL (
  405. SELECT product_id, price
  406. FROM sales
  407. WHERE sales.product_id = grp.product_id
  408. ORDER BY sale_date DESC LIMIT 10
  409. ) sub
  410. GROUP BY grp.product_id
  411. ),
  412. promotion_effect AS (
  413. SELECT
  414. p.product_id,
  415. MIN(pr.promotion_discount) AS promotion_discount
  416. FROM promotions pr
  417. JOIN products p ON pr.product_id = p.product_id
  418. WHERE pr.active = TRUE
  419. GROUP BY p.product_id
  420. ),
  421. popularity_score AS (
  422. SELECT
  423. s.product_id,
  424. RANK() OVER (PARTITION BY p.category_id ORDER BY COUNT(s.sale_id) DESC) AS popularity_rank,
  425. COUNT(s.sale_id) AS sale_count
  426. FROM sales s
  427. JOIN products p ON s.product_id = p.product_id
  428. GROUP BY s.product_id, p.category_id
  429. ),
  430. inventory_status AS (
  431. SELECT
  432. i.product_id,
  433. SUM(i.stock) AS total_stock,
  434. RANK() OVER (ORDER BY SUM(i.stock) DESC) AS stock_rank
  435. FROM inventory i
  436. GROUP BY i.product_id
  437. ),
  438. high_demand_products AS (
  439. SELECT
  440. p.product_id,
  441. AVG(s.sale_price) AS avg_sale_price,
  442. COUNT(s.sale_id) AS total_sales
  443. FROM products p
  444. JOIN sales s ON p.product_id = s.product_id
  445. GROUP BY p.product_id
  446. HAVING COUNT(s.sale_id) > (SELECT AVG(total_sales) FROM (SELECT COUNT(*) AS total_sales FROM sales GROUP BY product_id) subquery)
  447. ),
  448. dynamic_pricing AS (
  449. SELECT
  450. p.product_id,
  451. p.base_price,
  452. CASE
  453. WHEN pop.popularity_rank <= 3 THEN 1.2
  454. WHEN pop.popularity_rank BETWEEN 4 AND 10 THEN 1.1
  455. ELSE 0.9
  456. END AS popularity_adjustment,
  457. rp.avg_price,
  458. COALESCE(1.0 - (pe.promotion_discount / 100), 1) AS promotion_discount,
  459. CASE
  460. WHEN inv.stock_rank <= 3 THEN 1.1
  461. WHEN inv.stock_rank BETWEEN 4 AND 10 THEN 1.05
  462. ELSE 1
  463. END AS stock_adjustment,
  464. CASE
  465. WHEN p.base_price > rp.avg_price THEN 1 + (p.base_price - rp.avg_price) / rp.avg_price
  466. ELSE 1 - (rp.avg_price - p.base_price) / rp.avg_price
  467. END AS demand_multiplier,
  468. hd.avg_sale_price,
  469. CASE
  470. WHEN p.product_name ilike '%cheap%' THEN 0.8
  471. ELSE 1.0
  472. END AS additional_discount
  473. FROM products p
  474. LEFT JOIN recent_prices rp ON p.product_id = rp.product_id
  475. LEFT JOIN promotion_effect pe ON p.product_id = pe.product_id
  476. JOIN popularity_score pop ON p.product_id = pop.product_id
  477. LEFT JOIN inventory_status inv ON p.product_id = inv.product_id
  478. LEFT JOIN high_demand_products hd ON p.product_id = hd.product_id
  479. )
  480. SELECT
  481. dp.product_id,
  482. dp.base_price * dp.popularity_adjustment * dp.promotion_discount * dp.stock_adjustment * dp.demand_multiplier * dp.additional_discount AS adjusted_price,
  483. p.last_update_time
  484. FROM dynamic_pricing dp
  485. JOIN products p ON dp.product_id = p.product_id;
  486. statement ok
  487. CREATE INDEX idx_product_id ON mv_dynamic_pricing(product_id);
  488. query T multiline
  489. EXPLAIN OPTIMIZED PLAN WITH (humanized expressions) AS VERBOSE TEXT FOR
  490. SELECT
  491. sc.product_id,
  492. sc.product_name,
  493. c.category_id,
  494. c.category_name,
  495. dp.adjusted_price AS price,
  496. COALESCE(SUM(i.stock), 0) as available_stock
  497. FROM
  498. shopping_cart sc
  499. JOIN
  500. products p ON sc.product_id = p.product_id
  501. JOIN
  502. categories c ON p.category_id = c.category_id
  503. JOIN
  504. dynamic_pricing dp ON p.product_id = dp.product_id
  505. LEFT JOIN
  506. inventory i ON p.product_id = i.product_id
  507. GROUP BY
  508. sc.product_id,
  509. sc.product_name,
  510. c.category_id,
  511. c.category_name,
  512. dp.adjusted_price;
  513. ----
  514. Explained Query:
  515. With
  516. cte l0 =
  517. Project (#0{product_id}, #1{product_name}, #3{category_id}, #5{category_name}, #7{adjusted_price})
  518. Join on=(#0{product_id} = #2{product_id} = #6{product_id} AND #3{category_id} = #4{category_id}) type=delta
  519. ArrangeBy keys=[[#0{product_id}]]
  520. Project (#0{product_id}, #1{product_name})
  521. ReadStorage materialize.public.shopping_cart
  522. ArrangeBy keys=[[#0{product_id}], [#1{category_id}]]
  523. Project (#0{product_id}, #3{category_id})
  524. ReadIndex on=products idx_products_product_name=[*** full scan ***]
  525. ArrangeBy keys=[[#0{category_id}]]
  526. Project (#0{category_id}, #1{category_name})
  527. ReadStorage materialize.public.categories
  528. ArrangeBy keys=[[#0{product_id}]]
  529. ReadIndex on=dynamic_pricing dynamic_pricing_product_id_idx=[delta join lookup]
  530. cte l1 =
  531. ArrangeBy keys=[[#0{product_id}]]
  532. Get l0
  533. cte l2 =
  534. Project (#0{product_id}..=#4{adjusted_price}, #7{stock})
  535. Join on=(#0{product_id} = #6{product_id}) type=differential
  536. Get l1
  537. ArrangeBy keys=[[#1{product_id}]]
  538. ReadIndex on=inventory idx_inventory_product_id=[differential join]
  539. Return
  540. Project (#0{product_id}..=#4{adjusted_price}, #6)
  541. Map (coalesce(#5{sum_stock}, 0))
  542. Reduce group_by=[#0{product_id}..=#4{adjusted_price}] aggregates=[sum(#5{stock})]
  543. Union
  544. Map (null)
  545. Union
  546. Negate
  547. Project (#0{product_id}..=#4{adjusted_price})
  548. Join on=(#0{product_id} = #5{product_id}) type=differential
  549. Get l1
  550. ArrangeBy keys=[[#0{product_id}]]
  551. Distinct project=[#0{product_id}]
  552. Project (#0{product_id})
  553. Get l2
  554. Get l0
  555. Get l2
  556. Source materialize.public.categories
  557. Source materialize.public.shopping_cart
  558. Used Indexes:
  559. - materialize.public.idx_products_product_name (*** full scan ***)
  560. - materialize.public.idx_inventory_product_id (differential join)
  561. - materialize.public.dynamic_pricing_product_id_idx (delta join lookup)
  562. Target cluster: quickstart
  563. EOF
  564. statement ok
  565. CREATE VIEW dynamic_price_shopping_cart AS SELECT
  566. sc.product_id,
  567. sc.product_name,
  568. c.category_id,
  569. c.category_name,
  570. dp.adjusted_price AS price,
  571. COALESCE(SUM(i.stock), 0) as available_stock
  572. FROM
  573. shopping_cart sc
  574. JOIN
  575. products p ON sc.product_id = p.product_id
  576. JOIN
  577. categories c ON p.category_id = c.category_id
  578. JOIN
  579. dynamic_pricing dp ON p.product_id = dp.product_id
  580. LEFT JOIN
  581. inventory i ON p.product_id = i.product_id
  582. GROUP BY
  583. sc.product_id,
  584. sc.product_name,
  585. c.category_id,
  586. c.category_name,
  587. dp.adjusted_price;
  588. statement ok
  589. CREATE DEFAULT INDEX IF NOT EXISTS dynamic_price_shopping_cart_idx ON dynamic_price_shopping_cart;
  590. query T multiline
  591. EXPLAIN OPTIMIZED PLAN WITH (humanized expressions) AS VERBOSE TEXT FOR
  592. WITH MUTUALLY RECURSIVE
  593. rollup(category_id int, total numeric(20,10), item_count int) AS (
  594. -- Base: calculate each category's direct total and item count
  595. SELECT
  596. c.category_id,
  597. COALESCE(SUM(d.price), 0)::numeric(20,10),
  598. COUNT(d.price)
  599. FROM categories c
  600. LEFT JOIN dynamic_price_shopping_cart d
  601. ON c.category_id = d.category_id
  602. GROUP BY c.category_id
  603. UNION ALL
  604. -- Recursive: bubble each category's totals upward to its parent
  605. SELECT
  606. c.parent_id,
  607. r.total,
  608. r.item_count
  609. FROM rollup r
  610. JOIN categories c
  611. ON r.category_id = c.category_id
  612. WHERE c.parent_id IS NOT NULL
  613. ),
  614. totals(category_id int, total numeric(20,10), item_count int) AS (
  615. SELECT
  616. c.category_id,
  617. SUM(r.total)::numeric(20,10) AS total,
  618. SUM(r.item_count) AS item_count
  619. FROM categories c
  620. JOIN rollup r
  621. ON c.category_id = r.category_id
  622. GROUP BY c.category_id
  623. HAVING SUM(r.item_count) > 0 -- only include categories with items
  624. ),
  625. has_subcategories(category_id int, has_subcategory boolean) AS (
  626. SELECT
  627. a.category_id,
  628. count(*) FILTER (WHERE b.parent_id IS NOT NULL) > 0 AS has_subcategory
  629. FROM categories a
  630. LEFT JOIN categories b ON a.category_id = b.parent_id
  631. GROUP BY a.category_id
  632. )
  633. SELECT
  634. t.category_id,
  635. c.parent_id,
  636. s.has_subcategory,
  637. c.category_name,
  638. t.total,
  639. t.item_count
  640. FROM totals t
  641. JOIN categories c USING (category_id)
  642. JOIN has_subcategories s USING (category_id);
  643. ----
  644. Explained Query:
  645. With
  646. cte l0 =
  647. Project (#0{category_id})
  648. ReadStorage materialize.public.categories
  649. cte l1 =
  650. ArrangeBy keys=[[#0{category_id}]]
  651. Get l0
  652. cte l2 =
  653. Project (#0{category_id}, #2{price})
  654. Join on=(#0{category_id} = #1{category_id}) type=differential
  655. Get l1
  656. ArrangeBy keys=[[#0{category_id}]]
  657. Project (#2{category_id}, #4{price})
  658. ReadIndex on=dynamic_price_shopping_cart dynamic_price_shopping_cart_idx=[*** full scan ***]
  659. Return
  660. With Mutually Recursive
  661. cte l3 =
  662. Project (#0{category_id}, #1, #3)
  663. Map (bigint_to_integer(#2{count_price}))
  664. Union
  665. Project (#0{category_id}, #3, #2{count_price})
  666. Map (adjust_numeric_scale(coalesce(#1{sum_price}, 0)))
  667. Reduce group_by=[#0{category_id}] aggregates=[sum(#1{price}), count(#1{price})]
  668. Union
  669. Map (null)
  670. Union
  671. Negate
  672. Project (#0{category_id})
  673. Join on=(#0{category_id} = #1{category_id}) type=differential
  674. Get l1
  675. ArrangeBy keys=[[#0{category_id}]]
  676. Distinct project=[#0{category_id}]
  677. Project (#0{category_id})
  678. Get l2
  679. Get l0
  680. Get l2
  681. Project (#4{parent_id}, #1, #5)
  682. Map (integer_to_bigint(#2))
  683. Join on=(#0{category_id} = #3{category_id}) type=differential
  684. ArrangeBy keys=[[#0{category_id}]]
  685. Get l3
  686. ArrangeBy keys=[[#0{category_id}]]
  687. Project (#0{category_id}, #2{parent_id})
  688. Filter (#2{parent_id}) IS NOT NULL
  689. ReadStorage materialize.public.categories
  690. Return
  691. With
  692. cte l4 =
  693. Project (#0{category_id})
  694. ReadStorage materialize.public.categories
  695. cte l5 =
  696. ArrangeBy keys=[[#0{category_id}]]
  697. Get l4
  698. cte l6 =
  699. Project (#0{category_id})
  700. Join on=(#0{category_id} = #1{parent_id}) type=differential
  701. Get l5
  702. ArrangeBy keys=[[#0{parent_id}]]
  703. Project (#2{parent_id})
  704. Filter (#2{parent_id}) IS NOT NULL
  705. ReadStorage materialize.public.categories
  706. Return
  707. Project (#0{category_id}, #5{parent_id}, #9, #4{category_name}, #1{sum}, #8)
  708. Filter (#2{sum} > 0)
  709. Map (bigint_to_integer(#2{sum}), (#7{count} > 0))
  710. Join on=(#0{category_id} = #3{category_id} = #6{category_id}) type=delta
  711. ArrangeBy keys=[[#0{category_id}]]
  712. Reduce group_by=[#0{category_id}] aggregates=[sum(#1{total}), sum(#2{item_count})]
  713. Project (#0{category_id}, #2, #3)
  714. Join on=(#0{category_id} = #1{category_id}) type=differential
  715. Get l5
  716. ArrangeBy keys=[[#0{category_id}]]
  717. Get l3
  718. ArrangeBy keys=[[#0{category_id}]]
  719. ReadStorage materialize.public.categories
  720. ArrangeBy keys=[[#0{category_id}]]
  721. Reduce group_by=[#0{category_id}] aggregates=[count((null OR (#1{category_id}) IS NOT NULL))]
  722. Union
  723. Map (null)
  724. Union
  725. Negate
  726. Project (#0{category_id})
  727. Join on=(#0{category_id} = #1{category_id}) type=differential
  728. Get l5
  729. ArrangeBy keys=[[#0{category_id}]]
  730. Distinct project=[#0{category_id}]
  731. Get l6
  732. Get l4
  733. Project (#0{category_id}, #0{category_id})
  734. Get l6
  735. Source materialize.public.categories
  736. Used Indexes:
  737. - materialize.public.dynamic_price_shopping_cart_idx (*** full scan ***)
  738. Target cluster: quickstart
  739. EOF
  740. statement ok
  741. CREATE VIEW category_totals AS
  742. WITH MUTUALLY RECURSIVE
  743. rollup(category_id int, total numeric(20,10), item_count int) AS (
  744. -- Base: calculate each category's direct total and item count
  745. SELECT
  746. c.category_id,
  747. COALESCE(SUM(d.price), 0)::numeric(20,10),
  748. COUNT(d.price)
  749. FROM categories c
  750. LEFT JOIN dynamic_price_shopping_cart d
  751. ON c.category_id = d.category_id
  752. GROUP BY c.category_id
  753. UNION ALL
  754. -- Recursive: bubble each category's totals upward to its parent
  755. SELECT
  756. c.parent_id,
  757. r.total,
  758. r.item_count
  759. FROM rollup r
  760. JOIN categories c
  761. ON r.category_id = c.category_id
  762. WHERE c.parent_id IS NOT NULL
  763. ),
  764. totals(category_id int, total numeric(20,10), item_count int) AS (
  765. SELECT
  766. c.category_id,
  767. SUM(r.total)::numeric(20,10) AS total,
  768. SUM(r.item_count) AS item_count
  769. FROM categories c
  770. JOIN rollup r
  771. ON c.category_id = r.category_id
  772. GROUP BY c.category_id
  773. HAVING SUM(r.item_count) > 0 -- only include categories with items
  774. ),
  775. has_subcategories(category_id int, has_subcategory boolean) AS (
  776. SELECT
  777. a.category_id,
  778. count(*) FILTER (WHERE b.parent_id IS NOT NULL) > 0 AS has_subcategory
  779. FROM categories a
  780. LEFT JOIN categories b ON a.category_id = b.parent_id
  781. GROUP BY a.category_id
  782. )
  783. SELECT
  784. t.category_id,
  785. c.parent_id,
  786. s.has_subcategory,
  787. c.category_name,
  788. t.total,
  789. t.item_count
  790. FROM totals t
  791. JOIN categories c USING (category_id)
  792. JOIN has_subcategories s USING (category_id);
  793. statement ok
  794. CREATE DEFAULT INDEX IF NOT EXISTS category_totals_category_id_idx ON category_totals;
  795. statement ok
  796. CREATE INDEX IF NOT EXISTS category_totals_parent_id_idx ON category_totals (parent_id);
  797. query T multiline
  798. EXPLAIN OPTIMIZED PLAN WITH (humanized expressions) AS VERBOSE TEXT FOR SELECT EXISTS( SELECT 1 FROM inventory WHERE product_id = 1 );
  799. ----
  800. Explained Query:
  801. With
  802. cte l0 =
  803. Distinct project=[]
  804. Project ()
  805. ReadIndex on=materialize.public.inventory idx_inventory_product_id=[lookup value=(1)]
  806. Return
  807. Union
  808. Map (true)
  809. Get l0
  810. Map (false)
  811. Union
  812. Negate
  813. Get l0
  814. Constant
  815. - ()
  816. Used Indexes:
  817. - materialize.public.idx_inventory_product_id (lookup)
  818. Target cluster: quickstart
  819. EOF
  820. query T multiline
  821. EXPLAIN OPTIMIZED PLAN WITH (humanized expressions) AS VERBOSE TEXT FOR WITH selected_product AS (
  822. SELECT product_id, product_name, category_id, base_price
  823. FROM products
  824. WHERE product_id != 1
  825. LIMIT 1
  826. )
  827. SELECT product_id, product_name, category_id, base_price
  828. FROM selected_product;
  829. ----
  830. Explained Query:
  831. TopK limit=1
  832. Project (#0{product_id}, #1{product_name}, #3{category_id}, #2{base_price})
  833. Filter (#0{product_id} != 1)
  834. ReadIndex on=products idx_products_product_name=[*** full scan ***]
  835. Used Indexes:
  836. - materialize.public.idx_products_product_name (*** full scan ***)
  837. Target cluster: quickstart
  838. EOF
  839. query T multiline
  840. EXPLAIN OPTIMIZED PLAN WITH (humanized expressions) AS VERBOSE TEXT FOR
  841. SELECT product_id, adjusted_price, last_update_time
  842. FROM dynamic_pricing
  843. WHERE product_id = 5;
  844. ----
  845. Explained Query (fast path):
  846. Project (#0{product_id}..=#2{last_update_time})
  847. ReadIndex on=materialize.public.dynamic_pricing dynamic_pricing_product_id_idx=[lookup value=(5)]
  848. Used Indexes:
  849. - materialize.public.dynamic_pricing_product_id_idx (lookup)
  850. Target cluster: quickstart
  851. EOF
  852. query T multiline
  853. EXPLAIN OPTIMIZED PLAN WITH (humanized expressions) AS VERBOSE TEXT FOR
  854. SELECT product_id, adjusted_price, last_update_time
  855. FROM mv_dynamic_pricing
  856. WHERE product_id = 5;
  857. ----
  858. Explained Query (fast path):
  859. Project (#0{product_id}..=#2{last_update_time})
  860. ReadIndex on=materialize.public.mv_dynamic_pricing idx_product_id=[lookup value=(5)]
  861. Used Indexes:
  862. - materialize.public.idx_product_id (lookup)
  863. Target cluster: quickstart
  864. EOF
  865. query T multiline
  866. EXPLAIN OPTIMIZED PLAN WITH (humanized expressions) AS VERBOSE TEXT FOR
  867. SELECT DISTINCT category_id, category_name
  868. FROM categories
  869. ORDER BY category_name;
  870. ----
  871. Explained Query:
  872. Finish order_by=[#1{category_name} asc nulls_last] output=[#0, #1]
  873. Distinct project=[#0{category_id}, #1{category_name}]
  874. Project (#0{category_id}, #1{category_name})
  875. ReadStorage materialize.public.categories
  876. Source materialize.public.categories
  877. Target cluster: quickstart
  878. EOF
  879. query T multiline
  880. EXPLAIN OPTIMIZED PLAN WITH (humanized expressions) AS VERBOSE TEXT FOR
  881. SELECT
  882. category_name,
  883. item_count,
  884. total as subtotal
  885. FROM category_totals
  886. ORDER BY category_name ASC;
  887. ----
  888. Explained Query (fast path):
  889. Finish order_by=[#0 asc nulls_last] output=[#0..=#2]
  890. Project (#3{category_name}, #5{item_count}, #4{total})
  891. ReadIndex on=materialize.public.category_totals category_totals_category_id_idx=[*** full scan ***]
  892. Used Indexes:
  893. - materialize.public.category_totals_category_id_idx (*** full scan ***)
  894. Target cluster: quickstart
  895. EOF