Changes between Initial Version and Version 1 of AdvancedDatabaseDevelopment


Ignore:
Timestamp:
02/10/26 20:36:01 (3 weeks ago)
Author:
231136
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • AdvancedDatabaseDevelopment

    v1 v1  
     1= Напреден развој на базата
     2
     3== Процедури
     4
     5=== Најпопуларна песна, албум и артист од изминатиот ден
     6
     7{{{
     8CREATE OR REPLACE PROCEDURE yesterdays_most_popular()
     9    language plpgsql
     10as
     11$$
     12DECLARE
     13    yesterday TIMESTAMP;
     14BEGIN
     15    yesterday := CURRENT_DATE - 1;
     16
     17    IF EXISTS (
     18        SELECT 1
     19        FROM listens
     20        WHERE timestamp BETWEEN yesterday AND CURRENT_DATE
     21    ) THEN
     22        INSERT INTO daily_top_songs (day, song_id, play_count)
     23        SELECT CURRENT_DATE - 1, s.id, COUNT(*)
     24        FROM songs s
     25        JOIN listens l ON l.song_id = s.id
     26        WHERE l.timestamp BETWEEN yesterday AND NOW()
     27        GROUP BY s.id
     28        ORDER BY COUNT(*) DESC
     29        LIMIT 1;
     30
     31        INSERT INTO daily_top_albums(day, album_id, play_count)
     32        SELECT CURRENT_DATE - 1, a.id, COUNT(*)
     33        FROM albums a
     34        JOIN songs s on s.album_id = a.id
     35        JOIN listens l on l.song_id = s.id
     36        WHERE l.timestamp BETWEEN yesterday AND NOW()
     37        GROUP BY a.id
     38        ORDER BY COUNT(*) DESC
     39        LIMIT 1;
     40
     41        INSERT INTO daily_top_artists(day, artist_id, play_count)
     42        SELECT CURRENT_DATE - 1, a.user_id, COUNT(*)
     43        FROM artists a
     44        JOIN musical_entities me on me.released_by = a.user_id
     45        JOIN listens l on l.song_id = me.id
     46        WHERE l.timestamp BETWEEN yesterday AND NOW()
     47        GROUP BY a.user_id
     48        ORDER BY COUNT(*) DESC
     49        LIMIT 1;
     50
     51    ELSE
     52        INSERT INTO daily_top_songs (day, song_id, play_count)
     53        VALUES (CURRENT_DATE - 1, NULL, 0);
     54
     55        INSERT INTO daily_top_albums (day, album_id, play_count)
     56        VALUES (CURRENT_DATE - 1, NULL, 0);
     57
     58        INSERT INTO daily_top_artists (day, artist_id, play_count)
     59        VALUES (CURRENT_DATE - 1, NULL, 0);
     60    END IF;
     61END;
     62$$;
     63}}}
     64
     65=== Известување на артисти кои треба да настапат на настан во наредните 7 дена
     66
     67Прво ја додаваме табелата каде ќе ги чуваме нотификациите:
     68{{{
     69CREATE TABLE event_notifications(
     70    event_id BIGINT REFERENCES events(event_id),
     71    artist_id BIGINT REFERENCES artists(user_id),
     72    artist_has_read BOOLEAN,
     73    message TEXT,
     74
     75    CONSTRAINT pk_event_notifications PRIMARY KEY (event_id, artist_id)
     76);
     77}}}
     78
     79Потоа, процедурата:
     80
     81{{{
     82CREATE OR REPLACE PROCEDURE notify_event_performers()
     83    language plpgsql
     84AS
     85$$
     86DECLARE
     87    to_notify RECORD;
     88    next_seven_days DATE;
     89BEGIN
     90    next_seven_days := CURRENT_DATE + 7;
     91
     92    FOR to_notify IN SELECT
     93                         e.event_id,
     94                         a.user_id,
     95                         e.name,
     96                         e.venue,
     97                         e.location,
     98                         e.DATE - current_date AS days_until
     99    FROM events e
     100    JOIN performs_at pa ON pa.event_id=e.event_id
     101    JOIN artists a ON pa.artist_id=a.user_id
     102    WHERE e.DATE BETWEEN current_date AND next_seven_days AND
     103    NOT EXISTS(
     104        SELECT 1
     105        FROM event_notifications en
     106        WHERE en.event_id=e.event_id AND en.artist_id=pa.artist_id
     107    )
     108
     109    LOOP
     110        INSERT INTO event_notifications (event_id, artist_id, artist_has_read, message)
     111        VALUES(
     112               to_notify.event_id,
     113               to_notify.user_id,
     114               FALSE,
     115               format(
     116                  'You have an event at %s, %s IN %s days.',
     117                   to_notify.venue,
     118                   to_notify.location,
     119                   to_notify.days_until
     120               )
     121            );
     122    END LOOP;
     123
     124END;
     125$$;
     126}}}
     127
     128=== Известување артистите доколку нивна песна достигне одреден број на слушања
     129
     130Прво ги додаваме потребните табели:
     131{{{
     132CREATE TABLE song_play_counts (
     133    song_id     BIGINT PRIMARY KEY REFERENCES songs(id),
     134    play_count  BIGINT NOT NULL DEFAULT 0
     135);
     136
     137CREATE TABLE song_play_milestones (
     138    song_id  BIGINT PRIMARY KEY REFERENCES songs(id),
     139    last_milestone BIGINT NOT NULL DEFAULT 0
     140);
     141
     142CREATE TABLE milestone_notifications(
     143    user_id BIGINT REFERENCES users(user_id),
     144    song_id BIGINT REFERENCES songs(id),
     145    milestone BIGINT ,
     146    message TEXT,
     147    artist_has_read BOOLEAN,
     148
     149    CONSTRAINT pk_milestone_notifications PRIMARY KEY (user_id,song_id,milestone)
     150);
     151
     152CREATE TABLE last_processed_listen(
     153    last_timestamp TIMESTAMP
     154);
     155}}}
     156
     157Иницијално поставуваме `last_timestamp` на `-infinity` за при првиот внес сите слушања да се земат предвид.
     158{{{
     159INSERT INTO last_processed_listen (last_timestamp)
     160VALUES ('-infinity');
     161}}}
     162
     163
     164Потребна ни е процедура што периодично ќе ги земе сите нови слушања и соодветно ќе ги зголеми бројачите во `song_play_counts`.
     165{{{
     166
     167CREATE OR REPLACE PROCEDURE update_song_play_counts()
     168    language plpgsql
     169AS
     170$$
     171DECLARE
     172
     173BEGIN
     174    WITH new_listens AS (
     175        SELECT l.song_id, COUNT(*) AS cnt
     176        FROM listens l
     177        WHERE l.TIMESTAMP > (
     178            SELECT last_timestamp FROM last_processed_listen
     179        )
     180        GROUP BY l.song_id
     181    )
     182    INSERT INTO song_play_counts (song_id, play_count)
     183    SELECT song_id, cnt
     184    FROM new_listens
     185    ON CONFLICT (song_id)
     186        DO UPDATE
     187        SET play_count = song_play_counts.play_count + EXCLUDED.play_count;
     188
     189    UPDATE last_processed_listen
     190    SET last_timestamp = NOW();
     191END;
     192$$;
     193}}}
     194
     195
     196Потоа, процедура што ги наоѓа песните кои постигнале одреден успех - во нашиот случај, бидејќи базата е со малку податоци, сметаме дека 10 слушања се доволно голем успех за да го известиме артистот за истите. Откога песната ќе добие 10 слушања и артистот ќе биде известен, понатаму ќе биде известуван на 20, 30, 40 итн.
     197
     198{{{
     199CREATE OR REPLACE PROCEDURE notify_artists_on_reached_milestone()
     200    language plpgsql
     201AS
     202$$
     203DECLARE
     204
     205BEGIN
     206
     207    INSERT INTO milestone_notifications (user_id, song_id, milestone, message, artist_has_read)
     208    SELECT
     209        u.user_id,
     210        s.id,
     211        spm.last_milestone+10,
     212        format(
     213            'Milestone reached! Song %s reached %s listens!',
     214             me.title,
     215             spm.last_milestone + 10
     216        ),
     217        FALSE
     218    FROM song_play_counts spc
     219    LEFT JOIN song_play_milestones spm ON spc.song_id = spm.song_id
     220    LEFT JOIN songs s ON spc.song_id = s.id
     221    LEFT JOIN musical_entities me ON s.id = me.id
     222    LEFT JOIN users u ON u.user_id=me.released_by
     223    WHERE spc.play_count >= coalesce(spm.last_milestone,0)+10
     224    ON CONFLICT DO NOTHING;
     225
     226    INSERT INTO song_play_milestones (song_id, last_milestone)
     227    SELECT
     228        song_id,
     229        MAX(milestone)
     230    FROM milestone_notifications
     231    GROUP BY song_id
     232    ON CONFLICT (song_id)
     233        DO UPDATE SET last_milestone = EXCLUDED.last_milestone;
     234
     235END;
     236$$;
     237}}}
     238
     239== Тригери
     240
     241=== Тригер што проверува дали дали албум или песна се слушани од корисник пред да бидат оценети.
     242
     243Не сакаме во базата да имаме оценки за песни или албуми коишто не биле слушани, но не може тоа да го спречиме со едноставен constraint, па воведуваме тригер при нови записи во базата.
     244
     245{{{
     246CREATE OR REPLACE FUNCTION check_has_listened()
     247    RETURNS TRIGGER
     248AS $$
     249BEGIN
     250    IF EXISTS(
     251        SELECT 1 FROM listens l
     252        WHERE l.song_id = NEW.musical_entity_id
     253          AND l.listener_id = NEW.listener_id
     254    )
     255        OR EXISTS(
     256            SELECT 1 FROM albums a
     257            JOIN songs s ON s.album_id = a.id
     258            JOIN listens l ON s.id = l.song_id
     259            WHERE a.id = NEW.musical_entity_id AND l.listener_id = NEW.listener_id
     260        )
     261    THEN RETURN NEW;
     262    ELSE RAISE EXCEPTION 'listener_id=% has NOT listened to musical_entity_id=%',
     263        NEW.listener_id,
     264        NEW.musical_entity_id;
     265    END IF;
     266END;
     267$$
     268    LANGUAGE plpgsql;
     269
     270CREATE OR REPLACE TRIGGER has_listened_before_review
     271    BEFORE INSERT ON reviews
     272    FOR EACH ROW
     273EXECUTE FUNCTION check_has_listened();
     274}}}