Changes between Version 27 and Version 28 of OtherTopics


Ignore:
Timestamp:
08/24/26 18:19:45 (13 days ago)
Author:
211099
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • OtherTopics

    v27 v28  
    332332        ROUND(AVG(ch.rating), 2) AS avg_rating
    333333    FROM story s
    334     JOIN writer w  ON s.user_id  = w.user_id
    335     JOIN users  u  ON w.user_id  = u.user_id
    336     JOIN status st ON s.story_id = st.story_id AND st.status = 'published'
    337     LEFT JOIN chapter ch ON s.story_id = ch.story_id
    338     LEFT JOIN likes l    ON s.story_id = l.story_id
    339     LEFT JOIN comment c  ON s.story_id = c.story_id
     334    JOIN writer w  ON s.user_id    = w.user_id
     335    JOIN users  u  ON w.user_id    = u.user_id
     336    LEFT JOIN chapter ch ON s.story_id   = ch.story_id
     337    LEFT JOIN likes l  ON s.story_id   = l.story_id
     338    LEFT JOIN comment c  ON s.story_id   = c.story_id
     339    WHERE s.status = 'published'
    340340    GROUP BY
    341341        DATE_TRUNC('quarter', s.story_created_at),
     
    343343),
    344344with_growth AS (
    345     SELECT *,
    346         LAG(total_views)    OVER (PARTITION BY user_id ORDER BY quarter) AS prev_views,
    347         LAG(total_likes)    OVER (PARTITION BY user_id ORDER BY quarter) AS prev_likes,
     345    SELECT
     346        *,
     347        LAG(total_views)  OVER (PARTITION BY user_id ORDER BY quarter) AS prev_views,
     348        LAG(total_likes)  OVER (PARTITION BY user_id ORDER BY quarter) AS prev_likes,
    348349        LAG(total_comments) OVER (PARTITION BY user_id ORDER BY quarter) AS prev_comments,
    349         ROUND((total_views - LAG(total_views) OVER (PARTITION BY user_id ORDER BY quarter))::DECIMAL
    350             / NULLIF(LAG(total_views) OVER (PARTITION BY user_id ORDER BY quarter), 0) * 100, 2) AS views_growth_pct,
    351         ROUND((total_likes - LAG(total_likes) OVER (PARTITION BY user_id ORDER BY quarter))::DECIMAL
    352             / NULLIF(LAG(total_likes) OVER (PARTITION BY user_id ORDER BY quarter), 0) * 100, 2) AS likes_growth_pct
     350        ROUND(
     351            (total_views - LAG(total_views) OVER (PARTITION BY user_id ORDER BY quarter))
     352            ::DECIMAL
     353            / NULLIF(LAG(total_views) OVER (PARTITION BY user_id ORDER BY quarter), 0)
     354            * 100, 2
     355        ) AS views_growth_pct,
     356        ROUND(
     357            (total_likes - LAG(total_likes) OVER (PARTITION BY user_id ORDER BY quarter))
     358            ::DECIMAL
     359            / NULLIF(LAG(total_likes) OVER (PARTITION BY user_id ORDER BY quarter), 0)
     360            * 100, 2
     361        ) AS likes_growth_pct
    353362    FROM quarterly_stats
    354363)
    355364SELECT
    356365    TO_CHAR(quarter, 'YYYY "Q"Q') AS period,
    357     username, user_name, surname,
    358     stories_published, chapters_written, total_words,
     366    username,
     367    user_name,
     368    surname,
     369    stories_published,
     370    chapters_written,
     371    total_words,
    359372    total_views,
    360373    COALESCE(views_growth_pct, 0) AS views_growth_pct,
     
    363376    total_comments,
    364377    COALESCE(avg_rating, 0) AS avg_rating,
    365     RANK() OVER (PARTITION BY quarter ORDER BY total_views DESC) AS rank_by_views
     378    RANK() OVER (
     379        PARTITION BY quarter
     380        ORDER BY total_views DESC
     381    ) AS rank_by_views
    366382FROM with_growth
    367383ORDER BY quarter DESC, rank_by_views;
     
    369385=== Analysis without indexes:
    370386{{{
    371 |QUERY PLAN                                                                                                                                                                                                               |
    372 |-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
    373 |Sort  (cost=688.46..689.71 rows=500 width=221) (actual time=9.579..9.586 rows=19 loops=1)                                                                                                                                |
    374 |  Sort Key: with_growth.quarter DESC, (rank() OVER (?))                                                                                                                                                                  |
    375 |  Sort Method: quicksort  Memory: 27kB                                                                                                                                                                                   |
    376 |  ->  WindowAgg  (cost=654.82..666.05 rows=500 width=221) (actual time=9.529..9.560 rows=19 loops=1)                                                                                                                     |
    377 |        ->  Sort  (cost=654.80..656.05 rows=500 width=181) (actual time=9.518..9.524 rows=19 loops=1)                                                                                                                    |
    378 |              Sort Key: with_growth.quarter, with_growth.total_views DESC                                                                                                                                                |
    379 |              Sort Method: quicksort  Memory: 27kB                                                                                                                                                                       |
    380 |              ->  Subquery Scan on with_growth  (cost=598.66..632.38 rows=500 width=181) (actual time=9.459..9.508 rows=19 loops=1)                                                                                      |
    381 |                    ->  WindowAgg  (cost=598.66..627.38 rows=500 width=209) (actual time=9.457..9.503 rows=19 loops=1)                                                                                                   |
    382 |                          ->  Sort  (cost=598.63..599.88 rows=500 width=121) (actual time=9.448..9.454 rows=19 loops=1)                                                                                                  |
    383 |                                Sort Key: u.user_id, (date_trunc('quarter'::text, s.story_created_at))                                                                                                                   |
    384 |                                Sort Method: quicksort  Memory: 27kB                                                                                                                                                     |
    385 |                                ->  GroupAggregate  (cost=440.37..576.22 rows=500 width=121) (actual time=6.339..9.436 rows=19 loops=1)                                                                                  |
    386 |                                      Group Key: (date_trunc('quarter'::text, s.story_created_at)), u.user_id                                                                                                            |
    387 |                                      ->  Sort  (cost=440.37..453.08 rows=5084 width=72) (actual time=6.235..6.563 rows=5518 loops=1)                                                                                    |
    388 |                                            Sort Key: (date_trunc('quarter'::text, s.story_created_at)), u.user_id, s.story_id                                                                                           |
    389 |                                            Sort Method: quicksort  Memory: 701kB                                                                                                                                        |
    390 |                                            ->  Hash Left Join  (cost=35.11..127.40 rows=5084 width=72) (actual time=0.461..3.099 rows=5518 loops=1)                                                                     |
    391 |                                                  Hash Cond: (s.story_id = l.story_id)                                                                                                                                   |
    392 |                                                  ->  Hash Right Join  (cost=26.63..46.59 rows=882 width=68) (actual time=0.349..0.628 rows=926 loops=1)                                                                 |
    393 |                                                        Hash Cond: (ch.story_id = s.story_id)                                                                                                                            |
    394 |                                                        ->  Seq Scan on chapter ch  (cost=0.00..9.36 rows=236 width=23) (actual time=0.008..0.048 rows=236 loops=1)                                                      |
    395 |                                                        ->  Hash  (cost=24.30..24.30 rows=187 width=49) (actual time=0.332..0.336 rows=190 loops=1)                                                                      |
    396 |                                                              Buckets: 1024  Batches: 1  Memory Usage: 24kB                                                                                                              |
    397 |                                                              ->  Hash Right Join  (cost=16.80..24.30 rows=187 width=49) (actual time=0.194..0.282 rows=190 loops=1)                                                     |
    398 |                                                                    Hash Cond: (c.story_id = s.story_id)                                                                                                                 |
    399 |                                                                    ->  Seq Scan on comment c  (cost=0.00..4.91 rows=191 width=8) (actual time=0.011..0.032 rows=191 loops=1)                                            |
    400 |                                                                    ->  Hash  (cost=16.19..16.19 rows=49 width=45) (actual time=0.176..0.179 rows=49 loops=1)                                                            |
    401 |                                                                          Buckets: 1024  Batches: 1  Memory Usage: 12kB                                                                                                  |
    402 |                                                                          ->  Hash Join  (cost=3.63..16.19 rows=49 width=45) (actual time=0.089..0.165 rows=49 loops=1)                                                  |
    403 |                                                                                Hash Cond: (s.user_id = u.user_id)                                                                                                       |
    404 |                                                                                ->  Nested Loop  (cost=2.40..14.78 rows=49 width=20) (actual time=0.057..0.118 rows=49 loops=1)                                          |
    405 |                                                                                      ->  Hash Join  (cost=2.24..6.88 rows=49 width=16) (actual time=0.039..0.070 rows=49 loops=1)                                       |
    406 |                                                                                            Hash Cond: (s.story_id = st.story_id)                                                                                        |
    407 |                                                                                            ->  Seq Scan on story s  (cost=0.00..4.50 rows=50 width=16) (actual time=0.007..0.017 rows=50 loops=1)                       |
    408 |                                                                                            ->  Hash  (cost=1.62..1.62 rows=49 width=4) (actual time=0.024..0.025 rows=49 loops=1)                                       |
    409 |                                                                                                  Buckets: 1024  Batches: 1  Memory Usage: 10kB                                                                          |
    410 |                                                                                                  ->  Seq Scan on status st  (cost=0.00..1.62 rows=49 width=4) (actual time=0.008..0.017 rows=49 loops=1)                |
    411 |                                                                                                        Filter: ((status)::text = 'published'::text)                                                                     |
    412 |                                                                                                        Rows Removed by Filter: 1                                                                                        |
    413 |                                                                                      ->  Memoize  (cost=0.17..1.14 rows=1 width=4) (actual time=0.001..0.001 rows=1 loops=49)                                           |
    414 |                                                                                            Cache Key: s.user_id                                                                                                         |
    415 |                                                                                            Cache Mode: logical                                                                                                          |
    416 |                                                                                            Hits: 44  Misses: 5  Evictions: 0  Overflows: 0  Memory Usage: 1kB                                                           |
    417 |                                                                                            ->  Index Only Scan using writer_pkey on writer w  (cost=0.15..1.13 rows=1 width=4) (actual time=0.003..0.003 rows=1 loops=5)|
    418 |                                                                                                  Index Cond: (user_id = s.user_id)                                                                                      |
    419 |                                                                                                  Heap Fetches: 5                                                                                                        |
    420 |                                                                                ->  Hash  (cost=1.10..1.10 rows=10 width=33) (actual time=0.025..0.026 rows=10 loops=1)                                                  |
    421 |                                                                                      Buckets: 1024  Batches: 1  Memory Usage: 9kB                                                                                       |
    422 |                                                                                      ->  Seq Scan on users u  (cost=0.00..1.10 rows=10 width=33) (actual time=0.016..0.019 rows=10 loops=1)                             |
    423 |                                                  ->  Hash  (cost=4.88..4.88 rows=288 width=8) (actual time=0.099..0.099 rows=288 loops=1)                                                                               |
    424 |                                                        Buckets: 1024  Batches: 1  Memory Usage: 20kB                                                                                                                    |
    425 |                                                        ->  Seq Scan on likes l  (cost=0.00..4.88 rows=288 width=8) (actual time=0.014..0.052 rows=288 loops=1)                                                          |
    426 |Planning Time: 3.502 ms                                                                                                                                                                                                  |
    427 |Execution Time: 9.792 ms                                                                                                                                                                                                 |
    428 }}}
    429 Average time: 9.913 ms
     387| QUERY PLAN |
     388| :--- |
     389| Sort  \(cost=35.16..35.26 rows=40 width=221\) \(actual time=0.444..0.447 rows=4 loops=1\) |
     390|   Sort Key: with\_growth.quarter DESC, \(rank\(\) OVER \(?\)\) |
     391|   Sort Method: quicksort  Memory: 25kB |
     392|   ->  WindowAgg  \(cost=33.20..34.10 rows=40 width=221\) \(actual time=0.430..0.437 rows=4 loops=1\) |
     393|         ->  Sort  \(cost=33.20..33.30 rows=40 width=181\) \(actual time=0.402..0.405 rows=4 loops=1\) |
     394|               Sort Key: with\_growth.quarter, with\_growth.total\_views DESC |
     395|               Sort Method: quicksort  Memory: 25kB |
     396|               ->  Subquery Scan on with\_growth  \(cost=29.43..32.13 rows=40 width=181\) \(actual time=0.389..0.398 rows=4 loops=1\) |
     397|                     ->  WindowAgg  \(cost=29.43..31.73 rows=40 width=209\) \(actual time=0.388..0.397 rows=4 loops=1\) |
     398|                           ->  Sort  \(cost=29.43..29.53 rows=40 width=121\) \(actual time=0.380..0.382 rows=4 loops=1\) |
     399|                                 Sort Key: u.user\_id, \(date\_trunc\('quarter'::text, s.story\_created\_at\)\) |
     400|                                 Sort Method: quicksort  Memory: 25kB |
     401|                                 ->  GroupAggregate  \(cost=25.92..28.37 rows=40 width=121\) \(actual time=0.337..0.375 rows=4 loops=1\) |
     402|                                       Group Key: \(date\_trunc\('quarter'::text, s.story\_created\_at\)\), u.user\_id |
     403|                                       ->  Sort  \(cost=25.92..26.09 rows=70 width=72\) \(actual time=0.286..0.292 rows=118 loops=1\) |
     404|                                             Sort Key: \(date\_trunc\('quarter'::text, s.story\_created\_at\)\), u.user\_id, s.story\_id |
     405|                                             Sort Method: quicksort  Memory: 36kB |
     406|                                             ->  Hash Left Join  \(cost=6.17..23.77 rows=70 width=72\) \(actual time=0.196..0.246 rows=118 loops=1\) |
     407|                                                   Hash Cond: \(s.story\_id = l.story\_id\) |
     408|                                                   ->  Hash Left Join  \(cost=4.76..21.30 rows=20 width=68\) \(actual time=0.166..0.183 rows=26 loops=1\) |
     409|                                                         Hash Cond: \(s.story\_id = c.story\_id\) |
     410|                                                         ->  Hash Left Join  \(cost=3.51..19.80 rows=9 width=64\) \(actual time=0.143..0.156 rows=10 loops=1\) |
     411|                                                               Hash Cond: \(s.story\_id = ch.story\_id\) |
     412|                                                               ->  Nested Loop  \(cost=1.27..17.43 rows=4 width=45\) \(actual time=0.122..0.133 rows=4 loops=1\) |
     413|                                                                     Join Filter: \(s.user\_id = w.user\_id\) |
     414|                                                                     ->  Hash Join  \(cost=1.11..2.30 rows=4 width=49\) \(actual time=0.102..0.105 rows=4 loops=1\) |
     415|                                                                           Hash Cond: \(u.user\_id = s.user\_id\) |
     416|                                                                           ->  Seq Scan on users u  \(cost=0.00..1.10 rows=10 width=33\) \(actual time=0.032..0.033 rows=10 loops=1\) |
     417|                                                                           ->  Hash  \(cost=1.06..1.06 rows=4 width=16\) \(actual time=0.019..0.019 rows=4 loops=1\) |
     418|                                                                                 Buckets: 1024  Batches: 1  Memory Usage: 9kB |
     419|                                                                                 ->  Seq Scan on story s  \(cost=0.00..1.06 rows=4 width=16\) \(actual time=0.011..0.013 rows=4 loops=1\) |
     420|                                                                                       Filter: \(\(status\)::text = 'published'::text\) |
     421|                                                                                       Rows Removed by Filter: 1 |
     422|                                                                     ->  Index Only Scan using writer\_pkey on writer w  \(cost=0.15..3.77 rows=1 width=4\) \(actual time=0.006..0.006 rows=1 loops=4\) |
     423|                                                                           Index Cond: \(user\_id = u.user\_id\) |
     424|                                                                           Heap Fetches: 4 |
     425|                                                               ->  Hash  \(cost=2.11..2.11 rows=11 width=23\) \(actual time=0.017..0.017 rows=11 loops=1\) |
     426|                                                                     Buckets: 1024  Batches: 1  Memory Usage: 9kB |
     427|                                                                     ->  Seq Scan on chapter ch  \(cost=0.00..2.11 rows=11 width=23\) \(actual time=0.009..0.013 rows=11 loops=1\) |
     428|                                                         ->  Hash  \(cost=1.11..1.11 rows=11 width=8\) \(actual time=0.018..0.018 rows=11 loops=1\) |
     429|                                                               Buckets: 1024  Batches: 1  Memory Usage: 9kB |
     430|                                                               ->  Seq Scan on comment c  \(cost=0.00..1.11 rows=11 width=8\) \(actual time=0.010..0.011 rows=11 loops=1\) |
     431|                                                   ->  Hash  \(cost=1.18..1.18 rows=18 width=8\) \(actual time=0.016..0.016 rows=18 loops=1\) |
     432|                                                         Buckets: 1024  Batches: 1  Memory Usage: 9kB |
     433|                                                         ->  Seq Scan on likes l  \(cost=0.00..1.18 rows=18 width=8\) \(actual time=0.010..0.012 rows=18 loops=1\) |
     434| Planning Time: 1.241 ms |
     435| Execution Time: 0.603 ms |
     436}}}
     437Average time: 1.596 ms
    430438=== We create indexes
    431439{{{
    432 CREATE INDEX idx_status_story_published
    433     ON status(story_id, status)
     440CREATE INDEX idx_story_status_published
     441    ON story(story_id, status)
    434442    WHERE status = 'published';
    435 CREATE INDEX idx_has_genre_genre_id
    436     ON has_genre(genre_id, story_id);
    437443CREATE INDEX idx_users_writer_covering
    438444    ON users(user_id, username, user_name, surname);
    439445
    440 ANALYZE status;
    441 ANALYZE has_genre;
     446ANALYZE story;
    442447ANALYZE users;
    443448}}}
    444449=== After indexes we get:
    445450{{{
    446 |QUERY PLAN                                                                                                                                                                                                               |
    447 |-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
    448 |Sort  (cost=688.46..689.71 rows=500 width=221) (actual time=9.541..9.548 rows=19 loops=1)                                                                                                                                |
    449 |  Sort Key: with_growth.quarter DESC, (rank() OVER (?))                                                                                                                                                                  |
    450 |  Sort Method: quicksort  Memory: 27kB                                                                                                                                                                                   |
    451 |  ->  WindowAgg  (cost=654.82..666.05 rows=500 width=221) (actual time=9.491..9.522 rows=19 loops=1)                                                                                                                     |
    452 |        ->  Sort  (cost=654.80..656.05 rows=500 width=181) (actual time=9.481..9.487 rows=19 loops=1)                                                                                                                    |
    453 |              Sort Key: with_growth.quarter, with_growth.total_views DESC                                                                                                                                                |
    454 |              Sort Method: quicksort  Memory: 27kB                                                                                                                                                                       |
    455 |              ->  Subquery Scan on with_growth  (cost=598.66..632.38 rows=500 width=181) (actual time=9.423..9.472 rows=19 loops=1)                                                                                      |
    456 |                    ->  WindowAgg  (cost=598.66..627.38 rows=500 width=209) (actual time=9.421..9.467 rows=19 loops=1)                                                                                                   |
    457 |                          ->  Sort  (cost=598.63..599.88 rows=500 width=121) (actual time=9.413..9.419 rows=19 loops=1)                                                                                                  |
    458 |                                Sort Key: u.user_id, (date_trunc('quarter'::text, s.story_created_at))                                                                                                                   |
    459 |                                Sort Method: quicksort  Memory: 27kB                                                                                                                                                     |
    460 |                                ->  GroupAggregate  (cost=440.37..576.22 rows=500 width=121) (actual time=6.315..9.403 rows=19 loops=1)                                                                                  |
    461 |                                      Group Key: (date_trunc('quarter'::text, s.story_created_at)), u.user_id                                                                                                            |
    462 |                                      ->  Sort  (cost=440.37..453.08 rows=5084 width=72) (actual time=6.220..6.549 rows=5518 loops=1)                                                                                    |
    463 |                                            Sort Key: (date_trunc('quarter'::text, s.story_created_at)), u.user_id, s.story_id                                                                                           |
    464 |                                            Sort Method: quicksort  Memory: 701kB                                                                                                                                        |
    465 |                                            ->  Hash Left Join  (cost=35.11..127.40 rows=5084 width=72) (actual time=0.458..3.066 rows=5518 loops=1)                                                                     |
    466 |                                                  Hash Cond: (s.story_id = l.story_id)                                                                                                                                   |
    467 |                                                  ->  Hash Right Join  (cost=26.63..46.59 rows=882 width=68) (actual time=0.346..0.619 rows=926 loops=1)                                                                 |
    468 |                                                        Hash Cond: (ch.story_id = s.story_id)                                                                                                                            |
    469 |                                                        ->  Seq Scan on chapter ch  (cost=0.00..9.36 rows=236 width=23) (actual time=0.008..0.048 rows=236 loops=1)                                                      |
    470 |                                                        ->  Hash  (cost=24.30..24.30 rows=187 width=49) (actual time=0.331..0.335 rows=190 loops=1)                                                                      |
    471 |                                                              Buckets: 1024  Batches: 1  Memory Usage: 24kB                                                                                                              |
    472 |                                                              ->  Hash Right Join  (cost=16.80..24.30 rows=187 width=49) (actual time=0.194..0.283 rows=190 loops=1)                                                     |
    473 |                                                                    Hash Cond: (c.story_id = s.story_id)                                                                                                                 |
    474 |                                                                    ->  Seq Scan on comment c  (cost=0.00..4.91 rows=191 width=8) (actual time=0.011..0.032 rows=191 loops=1)                                            |
    475 |                                                                    ->  Hash  (cost=16.19..16.19 rows=49 width=45) (actual time=0.176..0.179 rows=49 loops=1)                                                            |
    476 |                                                                          Buckets: 1024  Batches: 1  Memory Usage: 12kB                                                                                                  |
    477 |                                                                          ->  Hash Join  (cost=3.63..16.19 rows=49 width=45) (actual time=0.090..0.165 rows=49 loops=1)                                                  |
    478 |                                                                                Hash Cond: (s.user_id = u.user_id)                                                                                                       |
    479 |                                                                                ->  Nested Loop  (cost=2.40..14.78 rows=49 width=20) (actual time=0.059..0.119 rows=49 loops=1)                                          |
    480 |                                                                                      ->  Hash Join  (cost=2.24..6.88 rows=49 width=16) (actual time=0.040..0.071 rows=49 loops=1)                                       |
    481 |                                                                                            Hash Cond: (s.story_id = st.story_id)                                                                                        |
    482 |                                                                                            ->  Seq Scan on story s  (cost=0.00..4.50 rows=50 width=16) (actual time=0.006..0.017 rows=50 loops=1)                       |
    483 |                                                                                            ->  Hash  (cost=1.62..1.62 rows=49 width=4) (actual time=0.024..0.025 rows=49 loops=1)                                       |
    484 |                                                                                                  Buckets: 1024  Batches: 1  Memory Usage: 10kB                                                                          |
    485 |                                                                                                  ->  Seq Scan on status st  (cost=0.00..1.62 rows=49 width=4) (actual time=0.008..0.016 rows=49 loops=1)                |
    486 |                                                                                                        Filter: ((status)::text = 'published'::text)                                                                     |
    487 |                                                                                                        Rows Removed by Filter: 1                                                                                        |
    488 |                                                                                      ->  Memoize  (cost=0.17..1.14 rows=1 width=4) (actual time=0.001..0.001 rows=1 loops=49)                                           |
    489 |                                                                                            Cache Key: s.user_id                                                                                                         |
    490 |                                                                                            Cache Mode: logical                                                                                                          |
    491 |                                                                                            Hits: 44  Misses: 5  Evictions: 0  Overflows: 0  Memory Usage: 1kB                                                           |
    492 |                                                                                            ->  Index Only Scan using writer_pkey on writer w  (cost=0.15..1.13 rows=1 width=4) (actual time=0.003..0.003 rows=1 loops=5)|
    493 |                                                                                                  Index Cond: (user_id = s.user_id)                                                                                      |
    494 |                                                                                                  Heap Fetches: 5                                                                                                        |
    495 |                                                                                ->  Hash  (cost=1.10..1.10 rows=10 width=33) (actual time=0.024..0.024 rows=10 loops=1)                                                  |
    496 |                                                                                      Buckets: 1024  Batches: 1  Memory Usage: 9kB                                                                                       |
    497 |                                                                                      ->  Seq Scan on users u  (cost=0.00..1.10 rows=10 width=33) (actual time=0.014..0.017 rows=10 loops=1)                             |
    498 |                                                  ->  Hash  (cost=4.88..4.88 rows=288 width=8) (actual time=0.099..0.099 rows=288 loops=1)                                                                               |
    499 |                                                        Buckets: 1024  Batches: 1  Memory Usage: 20kB                                                                                                                    |
    500 |                                                        ->  Seq Scan on likes l  (cost=0.00..4.88 rows=288 width=8) (actual time=0.014..0.052 rows=288 loops=1)                                                          |
    501 |Planning Time: 3.608 ms                                                                                                                                                                                                  |
    502 |Execution Time: 9.728 ms                                                                                                                                                                                                 |                                                                                                                                               
    503 }}}
    504 Average time: 9.830 ms
    505 Three indexes were created for Scenario 2: idx_status_story_published, idx_has_genre_genre_id, and idx_users_writer_covering. The planner naturally uses writer_pkey as an Index Only Scan; remaining tables use Hash Joins with Seq Scans as they fit in memory at this data volume. Average execution time: 9.913 ms without indexes vs 9.830 ms with indexes. The indexes are kept and will be utilized as data volume grows.
     451| QUERY PLAN |
     452| :--- |
     453| Sort  \(cost=35.16..35.26 rows=40 width=221\) \(actual time=0.418..0.422 rows=4 loops=1\) |
     454|   Sort Key: with\_growth.quarter DESC, \(rank\(\) OVER \(?\)\) |
     455|   Sort Method: quicksort  Memory: 25kB |
     456|   ->  WindowAgg  \(cost=33.20..34.10 rows=40 width=221\) \(actual time=0.403..0.411 rows=4 loops=1\) |
     457|         ->  Sort  \(cost=33.20..33.30 rows=40 width=181\) \(actual time=0.390..0.394 rows=4 loops=1\) |
     458|               Sort Key: with\_growth.quarter, with\_growth.total\_views DESC |
     459|               Sort Method: quicksort  Memory: 25kB |
     460|               ->  Subquery Scan on with\_growth  \(cost=29.43..32.13 rows=40 width=181\) \(actual time=0.377..0.387 rows=4 loops=1\) |
     461|                     ->  WindowAgg  \(cost=29.43..31.73 rows=40 width=209\) \(actual time=0.377..0.386 rows=4 loops=1\) |
     462|                           ->  Sort  \(cost=29.43..29.53 rows=40 width=121\) \(actual time=0.367..0.369 rows=4 loops=1\) |
     463|                                 Sort Key: u.user\_id, \(date\_trunc\('quarter'::text, s.story\_created\_at\)\) |
     464|                                 Sort Method: quicksort  Memory: 25kB |
     465|                                 ->  GroupAggregate  \(cost=25.92..28.37 rows=40 width=121\) \(actual time=0.258..0.362 rows=4 loops=1\) |
     466|                                       Group Key: \(date\_trunc\('quarter'::text, s.story\_created\_at\)\), u.user\_id |
     467|                                       ->  Sort  \(cost=25.92..26.09 rows=70 width=72\) \(actual time=0.215..0.285 rows=118 loops=1\) |
     468|                                             Sort Key: \(date\_trunc\('quarter'::text, s.story\_created\_at\)\), u.user\_id, s.story\_id |
     469|                                             Sort Method: quicksort  Memory: 36kB |
     470|                                             ->  Hash Left Join  \(cost=6.17..23.77 rows=70 width=72\) \(actual time=0.129..0.176 rows=118 loops=1\) |
     471|                                                   Hash Cond: \(s.story\_id = l.story\_id\) |
     472|                                                   ->  Hash Left Join  \(cost=4.76..21.30 rows=20 width=68\) \(actual time=0.113..0.128 rows=26 loops=1\) |
     473|                                                         Hash Cond: \(s.story\_id = c.story\_id\) |
     474|                                                         ->  Hash Left Join  \(cost=3.51..19.80 rows=9 width=64\) \(actual time=0.099..0.110 rows=10 loops=1\) |
     475|                                                               Hash Cond: \(s.story\_id = ch.story\_id\) |
     476|                                                               ->  Nested Loop  \(cost=1.27..17.43 rows=4 width=45\) \(actual time=0.078..0.087 rows=4 loops=1\) |
     477|                                                                     Join Filter: \(w.user\_id = s.user\_id\) |
     478|                                                                     ->  Hash Join  \(cost=1.11..2.30 rows=4 width=49\) \(actual time=0.060..0.063 rows=4 loops=1\) |
     479|                                                                           Hash Cond: \(u.user\_id = s.user\_id\) |
     480|                                                                           ->  Seq Scan on users u  \(cost=0.00..1.10 rows=10 width=33\) \(actual time=0.026..0.026 rows=10 loops=1\) |
     481|                                                                           ->  Hash  \(cost=1.06..1.06 rows=4 width=16\) \(actual time=0.016..0.017 rows=4 loops=1\) |
     482|                                                                                 Buckets: 1024  Batches: 1  Memory Usage: 9kB |
     483|                                                                                 ->  Seq Scan on story s  \(cost=0.00..1.06 rows=4 width=16\) \(actual time=0.010..0.012 rows=4 loops=1\) |
     484|                                                                                       Filter: \(\(status\)::text = 'published'::text\) |
     485|                                                                                       Rows Removed by Filter: 1 |
     486|                                                                     ->  Index Only Scan using writer\_pkey on writer w  \(cost=0.15..3.77 rows=1 width=4\) \(actual time=0.005..0.005 rows=1 loops=4\) |
     487|                                                                           Index Cond: \(user\_id = u.user\_id\) |
     488|                                                                           Heap Fetches: 4 |
     489|                                                               ->  Hash  \(cost=2.11..2.11 rows=11 width=23\) \(actual time=0.017..0.017 rows=11 loops=1\) |
     490|                                                                     Buckets: 1024  Batches: 1  Memory Usage: 9kB |
     491|                                                                     ->  Seq Scan on chapter ch  \(cost=0.00..2.11 rows=11 width=23\) \(actual time=0.008..0.012 rows=11 loops=1\) |
     492|                                                         ->  Hash  \(cost=1.11..1.11 rows=11 width=8\) \(actual time=0.011..0.011 rows=11 loops=1\) |
     493|                                                               Buckets: 1024  Batches: 1  Memory Usage: 9kB |
     494|                                                               ->  Seq Scan on comment c  \(cost=0.00..1.11 rows=11 width=8\) \(actual time=0.006..0.007 rows=11 loops=1\) |
     495|                                                   ->  Hash  \(cost=1.18..1.18 rows=18 width=8\) \(actual time=0.010..0.010 rows=18 loops=1\) |
     496|                                                         Buckets: 1024  Batches: 1  Memory Usage: 9kB |
     497|                                                         ->  Seq Scan on likes l  \(cost=0.00..1.18 rows=18 width=8\) \(actual time=0.005..0.007 rows=18 loops=1\) |
     498| Planning Time: 1.316 ms |
     499| Execution Time: 0.610 ms |                                                                                                                                           
     500}}}
     501Average time: 0.998 ms
     502Two indexes were created for Scenario 2: idx_story_status_published and idx_users_writer_covering. The planner naturally uses writer_pkey as an Index Only Scan; remaining tables (story, chapter, comment, likes) use Hash Joins with Seq Scans as they fit in memory at this data volume. Average execution time: 1.596 ms without indexes vs 0.998 ms with indexes. The indexes are kept and will be utilized as data volume grows.
    506503
    507504