Changes between Version 1 and Version 2 of DatabaseCreation


Ignore:
Timestamp:
05/20/26 15:13:48 (6 days ago)
Author:
231198
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • DatabaseCreation

    v1 v2  
    310310);
    311311```
     312
     313=== DML Имплементација ===
     314
     315DML скриптите се користат за внесување, генерирање и управување со податоците во базата на податоци. Во овој проект, преку DML наредби се пополнуваат табелите со иницијални и тест податоци потребни за функционирање и симулација на ride-sharing системот.
     316
     317Во скриптите се опфатени:
     318
     319внесување на почетни податоци (INSERT INTO)
     320
     321генерирање на големи количини тест податоци
     322
     323користење на SELECT, CASE, WITH и JOIN изрази за автоматско креирање податоци
     324
     325симулација на реални сценарија за возења, плаќања, откажувања и нотификации
     326
     327batch обработка и оптимизација за работа со милионски datasets
     328
     329користење на PostgreSQL DO $$ блокови и procedural логика
     330
     331Со овие DML скрипти се обезбедува реалистичен dataset кој овозможува тестирање, анализа и евалуација на перформансите на системот.
     332
     333=== DML Скрипти ===
     334```
     335INSERT INTO Status (status_tip, status_text) VALUES
     336                                                 ('driver', 'active'),
     337                                                 ('driver', 'inactive');
     338INSERT INTO Status (status_tip, status_text) VALUES
     339                                                 ('user', 'active'),
     340                                                 ('user', 'inactive'),
     341                                                 ('user', 'blocked'),
     342                                                 ('user', 'deleted');
     343INSERT INTO Status (status_tip, status_text) VALUES
     344                                                 ('admin', 'active'),
     345                                                 ('admin', 'inactive'),
     346                                                 ('admin', 'blocked');
     347INSERT INTO Status (status_tip, status_text) VALUES
     348                                                 ('ride', 'requested'),
     349                                                 ('ride', 'accepted'),
     350                                                 ('ride', 'in_progress'),
     351                                                 ('ride', 'completed'),
     352                                                 ('ride', 'cancelled');
     353INSERT INTO Status (status_tip, status_text) VALUES
     354                                                 ('payment', 'pending'),
     355                                                 ('payment', 'completed'),
     356                                                 ('payment', 'failed'),
     357                                                 ('payment', 'refunded');
     358
     359INSERT INTO Payment_methods (payment_tip) VALUES
     360                                              ('CASH'),
     361                                              ('CARD');                                                
     362
     363INSERT INTO Messages (message_text) VALUES
     364                                        ('Vaseto baranje e uspesno isprateno'),
     365                                        ('Vozacot go prifati vaseto baranje'),
     366                                        ('Vozacot go odbi vaseto baranje'),
     367                                        ('Vozacot pristigna na lokacijata za podiganje'),
     368                                        ('Vozenjeto e otkazano od strana na vozacot'),
     369                                        ('Vozenjeto e otkazano od vasa strana'),
     370                                        ('Plakjanjeto e uspesno'),
     371                                        ('Plakjanjeto ne uspea'),
     372                                        ('Nemate dostapni vozaci vo momentot');  
     373                                        
     374INSERT INTO Vehicles_model (Model) VALUES
     375                                       ('Volkswagen'),
     376                                       ('Skoda'),
     377                                       ('Toyota'),
     378                                       ('Opel'),
     379                                       ('Mercedes-Benz'),
     380                                       ('BMW'),
     381                                       ('Audi'),
     382                                       ('Hyundai'),
     383                                       ('Kia'),
     384                                       ('Renault');    
     385                                       
     386INSERT INTO Vehicle_types (type_name, description, Vehicles_model_id)
     387SELECT
     388    m.model_name,
     389    CASE
     390        WHEN m.model_name IN ('Golf','Octavia','Corolla','Astra','Polo','Fabia','Yaris','Corsa','Elantra','Clio',
     391                              'i30','Rio','Megane','Passat','Superb','Camry','Insignia','Tucson','Ceed','Captur')
     392            THEN 'Economy class'
     393        ELSE 'Luxury class'
     394        END as description,
     395    m.brand_id
     396FROM (
     397         SELECT vm.Vehicles_model_id as brand_id, unnest(models) as model_name
     398         FROM Vehicles_model vm
     399                  JOIN (VALUES
     400                            ('Volkswagen', ARRAY['Golf','Passat','Tiguan','Polo','Arteon']),
     401                            ('Skoda',      ARRAY['Octavia','Superb','Kodiaq','Fabia','Karoq']),
     402                            ('Toyota',     ARRAY['Corolla','Yaris','RAV4','Camry','C-HR']),
     403                            ('Opel',       ARRAY['Astra','Insignia','Corsa','Mokka','Grandland']),
     404                            ('Mercedes-Benz', ARRAY['E-Class','C-Class','S-Class','GLE','CLA']),
     405                            ('BMW',        ARRAY['3 Series','5 Series','X5','X3','1 Series']),
     406                            ('Audi',       ARRAY['A4','A6','Q5','A3','Q7']),
     407                            ('Hyundai',    ARRAY['Elantra','Tucson','Santa Fe','i30','Kona']),
     408                            ('Kia',        ARRAY['Sportage','Ceed','Rio','Sorento','Niro']),
     409                            ('Renault',    ARRAY['Clio','Megane','Captur','Kadjar','Talisman'])
     410         ) AS t(brand, models) ON vm.Model = t.brand
     411     ) m;
     412    
     413INSERT INTO Pricing_rules (base_price, price_per_km, vehicle_type_id)
     414SELECT
     415    CASE
     416        WHEN description = 'Economy class' THEN 80.00
     417        ELSE 100.00
     418        END as base_price,
     419    CASE
     420        WHEN description = 'Economy class' THEN 40.00
     421        ELSE 60.00
     422        END as price_per_km,
     423    vehicle_type_id
     424FROM Vehicle_types;    
     425
     426INSERT INTO Locations (latitude, longitude, grad, naselba, ulica, broj, created_at)
     427WITH street_data AS (
     428    SELECT * FROM (VALUES
     429                       ('Centar', 42.1322, 21.7144, ARRAY[
     430                           '11 Oktomvri', 'Goce Delchev', 'Tane Georgiev', 'Gorce Petrov', 'Plostad Marsal Tito',
     431                           'Done Bozhinov', 'Ljubo Atanasov', 'Ivo Lola Ribar', 'Leninova', 'Narodna Revolucija',
     432                           'Svetozar Markovic', 'Krste Misirkov', 'Jane Sandanski', 'Dragan Stoparevic', 'Bratstvo Edinstvo',
     433                           'Marsal Tito', 'JNA', 'Stiv Naumov', 'Guro Pucar Stari', 'Hristijan Karpos'
     434                           ]),
     435                       ('Zelen Rid', 42.1400, 21.7000, ARRAY[
     436                           'Nikola Tesla', 'Zheleznichka', 'Vidoe Smilevski Bato', 'Vasil Glavinov', 'Sremska',
     437                           'Kozjak', 'Osogovska', 'Shar Planina', 'Belasica', 'Ruen',
     438                           'Prilepska', 'Bitolska', 'Ohridska', 'Strumicka', 'Vardar',
     439                           'Pelister', 'Skopska', 'Tetovska', 'Gevgeliska', 'Veleska'
     440                           ]),
     441                       ('Goce Delchev', 42.1250, 21.7250, ARRAY[
     442                           'Treta Makedonska Udarna Brigada', 'Vera Kotorka', 'Boro Mikic', 'Filip Vtori', 'Srbo Tomovik',
     443                           'Zikica Jovanovic Spanac', 'Boro Menkov', 'Milan Zecar', 'Dositej Obradovic', 'Kuzman Josifovski Pitu',
     444                           'Tomaki Dimitrovski', 'Todor Velkov', 'Vasko Karangelevski', 'Niksicka', 'Pero Nakov',
     445                           'Bajram Shabani', 'Mite Bogoevski', 'Slavko Janevski', 'Gjorce Petrov', 'Kiro Fetak'
     446                           ]),
     447                       ('Pero Chicho', 42.1150, 21.7300, ARRAY[
     448                           'Pero Chicho', '11 Noemvri', 'Boro Mendkov', 'Josip Pancic', 'Banatska',
     449                           'Moravska', 'Drinska', 'Sredorek', 'Vojvodina', 'Sumadinska',
     450                           'Toplicki Odred', 'Zajecarska', 'Niska', 'Vranjska', 'Leskovacka',
     451                           'Sremska', 'Prizrenska', 'Dakovicka', 'Pecka', 'Kosovska'
     452                           ]),
     453                       ('Karposh', 42.1200, 21.7500, ARRAY[
     454                           'Oktomvriska Revolucija', 'Srecko Puzalka', 'Mito Hadzivasilev Jasmin', 'Sava Kovacevic', 'Karposova',
     455                           'Metodija Andonov Cento', 'ASNOM', 'Dame Gruev', 'Pitu Guli', 'Partizanska',
     456                           'Kiro Burnaz', 'Pere Tosev', 'Gjorce Petrov', 'Hristijan Todorovski', 'Miroslav Krleza',
     457                           'Blagoja Stevkovski', 'Kosta Racin', 'Koco Racin', 'Mirce Acev', 'Vasil Antevski'
     458                           ]),
     459                       ('Bedinje', 42.1500, 21.7100, ARRAY[
     460                           'Srbo Tomovikj', 'Zhivko Tomovski', 'Guro Djakovic', 'Karaorman', 'Njegoseva',
     461                           'Ivan Milutinovic', 'Mojsa Pijade', 'Zheleznichka', 'Revolucionerna', 'Gjorce Petrov',
     462                           'Cede Filipovski', 'Bratstvo', 'Edinstvo', 'Prvomajska', 'Sloboda',
     463                           'Proleterska', 'Makedonska', 'Ilindenska', 'Vidoe Smilevski', 'Josip Pancic'
     464                           ]),
     465                       ('Ajducka Cheshma', 42.1480, 21.6950, ARRAY[
     466                           'Bajram Shabani', 'Prilepska', 'Blagoja Stevkovski', 'Romanovska', 'Kumanovska',
     467                           'Debarska', 'Krusevska', 'Dojranska', 'Prespanska', 'Gevgeliska',
     468                           'Negotinska', 'Kavadarska', 'Radoviska', 'Berovska', 'Delcevska',
     469                           'Vinicka', 'Kratovska', 'Zletovska', 'Probistipska', 'Svetinikolska'
     470                           ])
     471                  ) AS t(naselba, lat_base, lon_base, ulici)
     472),
     473     expanded AS (
     474         SELECT
     475             naselba,
     476             lat_base,
     477             lon_base,
     478             unnest(ulici) as ulica
     479         FROM street_data
     480     ),
     481     numbered AS (
     482         SELECT
     483             naselba,
     484             lat_base,
     485             lon_base,
     486             ulica,
     487             row_number() OVER (PARTITION BY naselba ORDER BY ulica) as street_num
     488         FROM expanded
     489     )
     490SELECT
     491    (lat_base
     492        + (street_num * 0.0008)
     493        + (n.num * 0.000045)
     494        + (random() * 0.00005 - 0.000025)
     495        )::NUMERIC(9,6)                                         AS latitude,
     496
     497    (lon_base
     498        + (street_num * 0.0010)
     499        + (n.num * 0.000060)
     500        + (random() * 0.00005 - 0.000025)
     501        )::NUMERIC(9,6)                                         AS longitude,
     502
     503    'Kumanovo'                                              AS grad,
     504    nd.naselba                                              AS naselba,
     505    nd.ulica                                                AS ulica,
     506    n.num::VARCHAR                                          AS број,
     507
     508    (NOW() - interval '5 years' - (random() * interval '365 days'))::TIMESTAMP AS created_at
     509
     510FROM numbered nd
     511         CROSS JOIN generate_series(1, 100) AS n(num);
     512        
     513        
     514INSERT INTO Users (
     515    name,
     516    username,
     517    phone,
     518    email,
     519    password_hash,
     520    created_at,
     521    status_id
     522)
     523WITH data_pools AS (
     524    SELECT
     525        ARRAY['Marija','Ana','Ivana','Elena','Sara','Katerina','Jana','Mila','Teodora','Kristina','Sofija','Jovana','Tamara','Simona','Biljana','Vesna','Diana','Lidija','Monika','Angela','Ema','Tijana','Marina','Sonja','Nina','Anita','Valentina','Dragana','Ivona','Aleksandra','Natasha','Katarina','Emilija','Andrea','Gabriela','Tea','Danica','Zvezdana','Slobodanka','Gordana','Zorica','Ljubica','Rada','Mirjana','Despina','Kalina','Dafina','Hristina','Anastasija','Snezhana','Zlatka','Stojanka','Stana','Milka','Roksana','Romina','Karolina','Alina','Elina','Selina','Adelina','Darija','Dorotea','Nela','Neda','Zana','Hana','Ines','Iva','Irina','Arina','Lara','Tara','Dara','Kira','Lila','Tina','Jasmina','Bilja','Vanja','Maja','Bojana','Milena','Stefanija','Viktorija','Tatjana','Slavica','Ruzica'] AS f_names,
     526        ARRAY['Petar','Marko','Nikola','Stefan','Daniel','Goran','Bojan','Andrej','Filip','Viktor','Aleksandar','Dejan','Zoran','Ilija','Dimitar','Kiril','Hristijan','Vladimir','Vasko','Riste','Martin','Igor','Kristijan','Nenad','Damjan','Darko','Oliver','Simeon','Luka','Emil','Alen','Stojan','Metodija','Vlatko','Boris','Davor','Radoslav','Kosta','Mladen','Mile','Petre','Gjorgi','Trajan','Jovan','Ivan','Spas','Sasho','Zlatko','Bojcho','Risto','Bogdan','Tome','Kole','Vangel','Naum','Gligor','Stanko','Rade','Lazo','Arsen','Milko','Pavle','Dragan','Zdravko','Ilko','Vuk','Dame','Gorancho','Stevo','Miro'] AS m_names,
     527        ARRAY['Arsova','Dimitrova','Stojanova','Petrovska','Ilievska','Trajkova','Georgieva','Kostova','Maneva','Ristova','Popova','Jovanova','Nikolova','Mitrevska','Ivanova','Sokolova','Markova','Spasova','Angelova','Vasilevska','Pavlova','Gruevska','Nikolovska','Filipova','Stefanova','Andonova','Zafirova','Koleva','Stamenova','Bojkova','Todorova','Petkova','Hristova','Kitanova','Ilieva','Ruseva','Dimovska','Miloshevska','Petreska','Arnaudova','Georgievska','Markoska','Velkova','Stojkovska','Kirovska','Zlateva','Milevska','Naceva','Spiridonova','Lazarevska','Doneva','Atanasova','Bogdanova','Simeonova','Rangelova','Gligorova','Tasevska','Pancheva','Jankova','Bogoeva'] AS f_surnames,
     528        ARRAY['Arsov','Dimitrov','Stojanov','Petrovski','Iliev','Trajkov','Georgiev','Kostov','Manev','Ristov','Popov','Jovanov','Nikolov','Mitrev','Ivanov','Sokolov','Markov','Spasov','Angelov','Vasilev','Pavlov','Gruev','Nikolovski','Filipov','Stefanov','Andonov','Zafirov','Kolev','Stamenov','Bojkov','Todorov','Petkov','Hristov','Kitanov','Rusev','Dimovski','Miloshevski','Petreski','Arnaudov','Georgievski','Markoski','Velkov','Stojkovski','Kirovski','Zlatev','Milevski','Nacev','Spiridonov','Lazarevski','Donev','Atanasov','Bogdanov','Simeonov','Rangelov','Gligorov','Tasev','Panchev','Jankov','Bogoev','Karov','Trifunov','Gavrilov','Savov','Ilkov','Vukov','Zdravkov','Stojanovski','Danev'] AS m_surnames
     529)
     530SELECT
     531    p.fname || ' ' || p.lname                              AS name,
     532    LOWER(p.fname || '.' || p.lname || i)                  AS username,
     533    '+3897' || (1000000 + i)                               AS phone,
     534    LOWER(p.fname || '.' || p.lname || i || '@gmail.com')  AS email,
     535    md5(random()::text)                                    AS password_hash,
     536    NOW() - (random() * interval '3 years')                AS created_at,
     537    (SELECT status_id FROM Status WHERE status_tip = 'user' AND status_text = 'active' LIMIT 1) AS status_id
     538FROM generate_series(1, 70000) i
     539         CROSS JOIN data_pools d
     540         CROSS JOIN LATERAL (
     541    SELECT
     542        CASE WHEN i % 2 = 0
     543                 THEN d.f_names[  (i % cardinality(d.f_names))   + 1]
     544             ELSE d.m_names[  (i % cardinality(d.m_names))   + 1]
     545            END AS fname,
     546        CASE WHEN i % 2 = 0
     547                 THEN d.f_surnames[(i % cardinality(d.f_surnames)) + 1]
     548             ELSE d.m_surnames[(i % cardinality(d.m_surnames)) + 1]
     549            END AS lname
     550    ) p;
     551   
     552INSERT INTO Drivers (name, phone, license_number, registration_date, rating, status_id)
     553WITH data_pools AS (
     554    SELECT
     555        ARRAY['Petar','Marko','Nikola','Stefan','Daniel','Goran','Bojan','Andrej','Filip','Viktor',
     556            'Aleksandar','Dejan','Zoran','Ilija','Dimitar','Kiril','Hristijan','Vladimir','Vasko','Riste',
     557            'Martin','Igor','Damjan','Darko','Oliver','Luka','Emil','Stojan','Vlatko','Boris',
     558            'Radoslav','Kosta','Mladen','Mile','Petre','Gjorgi','Jovan','Ivan','Sasho','Zlatko',
     559            'Bogdan','Tome','Naum','Gligor','Stanko','Rade','Lazo','Arsen','Pavle','Dragan'] AS m_names,
     560        ARRAY['Arsov','Dimitrov','Stojanov','Petrovski','Iliev','Trajkov','Georgiev','Kostov','Manev',
     561            'Ristov','Popov','Jovanov','Nikolov','Mitrev','Ivanov','Sokolov','Markov','Spasov','Angelov',
     562            'Vasilev','Pavlov','Gruev','Nikolovski','Filipov','Stefanov','Andonov','Zafirov','Kolev',
     563            'Stamenov','Bojkov','Todorov','Petkov','Hristov','Kitanov','Rusev','Dimovski','Miloshevski',
     564            'Petreski','Arnaudov','Georgievski','Markoski','Velkov','Stojkovski','Kirovski','Zlatev',
     565            'Milevski','Spiridonov','Lazarevski','Atanasov','Bogdanov'] AS m_surnames
     566)
     567SELECT
     568    p.fname || ' ' || p.lname                                          AS name,
     569    '+3897' || (2000000 + i)                                           AS phone,
     570    CHR(65 + (i % 26)) || LPAD((floor(random() * 9000000) + 1000000)::int::text, 7, '0') AS license_number,
     571    (NOW() - interval '3 years' - (random() * interval '2 years'))::DATE AS registration_date,
     572    ROUND((3.5 + random() * 1.5)::numeric, 2)                          AS rating,
     573    (SELECT status_id FROM Status WHERE status_tip = 'driver' AND status_text = 'active' LIMIT 1) AS status_id
     574FROM generate_series(1, 200) i
     575         CROSS JOIN data_pools d
     576         CROSS JOIN LATERAL (
     577    SELECT
     578        d.m_names[(i % cardinality(d.m_names)) + 1]       AS fname,
     579        d.m_surnames[(i % cardinality(d.m_surnames)) + 1]  AS lname
     580    ) p;   
     581                                      
     582
     583INSERT INTO Admins (name, username, phone, email, password_hash, created_at, status_id)
     584VALUES
     585    ('Aleksandar Dimitrov', 'admin.aleksandar', '+38971100001', 'aleksandar.dimitrov@taxikumanovo.mk', md5(random()::text), NOW() - interval '4 years', (SELECT status_id FROM Status WHERE status_tip = 'admin' AND status_text = 'active' LIMIT 1)),
     586    ('Marija Petrovska',    'admin.marija',     '+38971100002', 'marija.petrovska@taxikumanovo.mk',    md5(random()::text), NOW() - interval '3 years', (SELECT status_id FROM Status WHERE status_tip = 'admin' AND status_text = 'active' LIMIT 1)),
     587    ('Nikola Trajkov',      'admin.nikola',     '+38971100003', 'nikola.trajkov@taxikumanovo.mk',      md5(random()::text), NOW() - interval '3 years', (SELECT status_id FROM Status WHERE status_tip = 'admin' AND status_text = 'active' LIMIT 1)),
     588    ('Elena Georgieva',     'admin.elena',      '+38971100004', 'elena.georgieva@taxikumanovo.mk',     md5(random()::text), NOW() - interval '2 years', (SELECT status_id FROM Status WHERE status_tip = 'admin' AND status_text = 'active' LIMIT 1)),
     589    ('Stefan Iliev',        'admin.stefan',     '+38971100005', 'stefan.iliev@taxikumanovo.mk',        md5(random()::text), NOW() - interval '2 years', (SELECT status_id FROM Status WHERE status_tip = 'admin' AND status_text = 'active' LIMIT 1)),
     590    ('Ana Nikolova',        'admin.ana',        '+38971100006', 'ana.nikolova@taxikumanovo.mk',        md5(random()::text), NOW() - interval '1 year',  (SELECT status_id FROM Status WHERE status_tip = 'admin' AND status_text = 'active' LIMIT 1));
     591   
     592   
     593INSERT INTO Vehicle_ownership (Plate, Traffic_permit, Color, year, vehicle_type_id)
     594WITH colors AS (
     595    SELECT ARRAY['Crna','Bela','Siva','Srebrena','Crvena','Sina','Zelena','Beza','Kafena','Temnosina'] AS cols
     596)
     597SELECT
     598    -- Табличка: 2 букви + 3 цифри + 2 букви, пр: SK123AB
     599    'KU' ||
     600    LPAD((100 + i)::text, 4, '0') ||
     601    CHR(65 + ((i * 5) % 26)) || CHR(65 + ((i * 7) % 26))      AS Plate,
     602
     603    -- Сообраќајна: KU + 6 цифри
     604    CHR(65 + (i % 26)) || CHR(65 + ((i * 3) % 26)) || LPAD((100000 + i)::text, 6, '0')                   AS Traffic_permit,
     605
     606    -- Боја
     607    c.cols[(i % cardinality(c.cols)) + 1]                      AS Color,
     608
     609    -- Година: возилата се од 2010 до 2023
     610    2010 + (i % 14)                                            AS year,
     611
     612    -- vehicle_type_id: рандом од 1 до 50
     613    (1 + (i % 50))                                             AS vehicle_type_id
     614
     615FROM generate_series(1, 300) i
     616         CROSS JOIN colors c;
     617   
     618INSERT INTO Active_drivers (driver_id, current_location_id, is_available, working_start, working_end,vehicle_ownership_id)
     619SELECT
     620    d.driver_id,
     621    -- Random location_id од постоечките локации
     622    (1 + (d.driver_id % 14000))                                    AS current_location_id,
     623    -- 70% се available, 30% се зафатени
     624    CASE WHEN d.driver_id % 10 < 7 THEN true ELSE false END        AS is_available,
     625    -- Работната смена почнала пред 1-8 часа
     626    (NOW() - (random() * interval '8 hours'))::TIMESTAMP            AS working_start,
     627    -- Работната смена завршува за 2-10 часа
     628    (NOW() + (random() * interval '10 hours'))::TIMESTAMP           AS working_end,
     629    -- Да се знае кое авто моментлано го вози
     630    CASE
     631        WHEN d.driver_id < 100
     632            THEN d.driver_id + (random(0,1) * 200)
     633        ELSE d.driver_id
     634        END AS vehicle_ownership_id
     635FROM Drivers d
     636-- Земаме само 150 од 200 возачи (75%)
     637WHERE d.driver_id % 4 != 0
     638LIMIT 150;
     639
     640
     641INSERT INTO Drivers_Vehicle_ownership (driver_id, Vehicle_ownership_id)
     642
     643-- Sekoj vozac dobiva svoe primарно vozilo (1:1)
     644SELECT
     645    d.driver_id,
     646    -- Voziloto se mapira deterministicki: vozac 1→vozilo 1, vozac 2→vozilo 2 ...
     647    d.driver_id AS Vehicle_ownership_id
     648FROM Drivers d
     649WHERE d.driver_id <= 200  -- site 200 vozaci
     650
     651UNION ALL
     652
     653-- Prvite 100 vozaci dobivaat i vtoro vozilo (od 201 do 300)
     654SELECT
     655    d.driver_id,
     656    d.driver_id + 200 AS Vehicle_ownership_id
     657FROM Drivers d
     658WHERE d.driver_id <= 100;
     659
     660--Rides
     661DO $$
     662    DECLARE
     663        batch_size   INT := 1000000;
     664        total_rows   INT := 10000000;
     665        batches      INT := total_rows / batch_size;
     666        b            INT;
     667
     668        max_user     INT;
     669        max_driver   INT;
     670        max_location INT;
     671
     672        sid_requested   INT;
     673        sid_accepted    INT;
     674        sid_in_progress INT;
     675        sid_completed   INT;
     676        sid_cancelled   INT;
     677    BEGIN
     678        SELECT MAX(user_id)     INTO max_user     FROM Users;
     679        SELECT MAX(driver_id)   INTO max_driver   FROM Drivers;
     680        SELECT MAX(location_id) INTO max_location FROM Locations;
     681
     682        SELECT status_id INTO sid_requested   FROM Status WHERE status_tip='ride' AND status_text='requested'   LIMIT 1;
     683        SELECT status_id INTO sid_accepted    FROM Status WHERE status_tip='ride' AND status_text='accepted'    LIMIT 1;
     684        SELECT status_id INTO sid_in_progress FROM Status WHERE status_tip='ride' AND status_text='in_progress' LIMIT 1;
     685        SELECT status_id INTO sid_completed   FROM Status WHERE status_tip='ride' AND status_text='completed'   LIMIT 1;
     686        SELECT status_id INTO sid_cancelled   FROM Status WHERE status_tip='ride' AND status_text='cancelled'   LIMIT 1;
     687
     688        RAISE NOTICE 'max_user=%, max_driver=%, max_location=%', max_user, max_driver, max_location;
     689
     690        FOR b IN 1..batches LOOP
     691
     692                INSERT INTO Rides (
     693                    user_id,
     694                    driver_id,
     695                    pickup_location_id,
     696                    dropoff_location_id,
     697                    status_id,
     698                    request_time,
     699                    pickup_time,
     700                    start_time,
     701                    end_time,
     702                    distance_km,
     703                    final_price,
     704                    discount_percentage,
     705                    vehicle_ownership_id
     706                )
     707                WITH
     708
     709                    -- --------------------------------------------------------
     710                    -- CTE 1: Realna cena po vozac spored tipot na voziloto
     711                    --        Economy:  base=80,  per_km=40
     712                    --        Luxury:   base=100, per_km=60
     713                    --        Ako ima 2 vozila → go zemame luksoznoto (MAX cena)
     714                    -- --------------------------------------------------------
     715                    driver_pricing AS (
     716                        SELECT
     717                            dvo.driver_id,
     718                            MAX(pr.base_price)    AS base_price,
     719                            MAX(pr.price_per_km)  AS price_per_km,
     720                            MAX(vt.description)   AS vehicle_class
     721                        FROM Drivers_Vehicle_ownership dvo
     722                                 JOIN Vehicle_ownership vo ON dvo.Vehicle_ownership_id = vo.Vehicle_ownership_id
     723                                 JOIN Vehicle_types     vt ON vo.vehicle_type_id       = vt.vehicle_type_id
     724                                 JOIN Pricing_rules     pr ON vt.vehicle_type_id       = pr.vehicle_type_id
     725                        GROUP BY dvo.driver_id
     726                    ),
     727
     728                    -- --------------------------------------------------------
     729                    -- CTE 2: Base pool so site ogranichuvanja primeneti
     730                    -- --------------------------------------------------------
     731                    base_pool AS (
     732                        SELECT
     733                            s.i,
     734
     735                            -- USER / DRIVER (realni, razlichni)
     736                            1 + (s.i % max_user)                                AS user_id,
     737                            1 + ((s.i + floor(random() * 200000)+ 37)::INTEGER % max_driver)                       AS driver_id,
     738
     739                            -- LOKACII
     740                            1 + (s.i + floor(random() * 200000))::INTEGER % max_location                          AS pickup_location_id,
     741                            1 + ((s.i + floor(random() * 200000)+ (14000 / 2))::INTEGER % max_location)    AS dropoff_location_id,
     742
     743                            -- STATUS
     744                            CASE
     745                                WHEN (s.i % 20) = 0         THEN sid_requested
     746                                WHEN (s.i % 20) IN (1, 2)   THEN sid_accepted
     747                                WHEN (s.i % 20) IN (3, 4)   THEN sid_in_progress
     748                                WHEN (s.i % 20) IN (18, 19) THEN sid_cancelled
     749                                ELSE                              sid_completed
     750                                END                                                 AS status_id,
     751
     752                            -- BASE TIME: random den (poslednite 2 godini), cas 06:00-23:00
     753                            date_trunc('day', now() - (floor(random() * 730) * interval '1 day'))
     754                                + (21600 + floor(random() * 61200)) * interval '1 second'
     755                                AS request_time,
     756
     757                            -- DISTANCA: 0.5 .. 10.0 km  (OGRANICHENO NA MAX 10km)
     758                            ROUND((0.5 + random() * 9.5)::numeric, 2)          AS distance_km
     759
     760                        FROM generate_series(
     761                                     (b - 1) * batch_size + 1,
     762                                     b      * batch_size
     763                             ) s(i)
     764                    ),
     765
     766                    -- --------------------------------------------------------
     767                    -- CTE 3: Presmetaj popust i vreminja vrz osnova na distanca
     768                    -- --------------------------------------------------------
     769                    enriched AS (
     770                        SELECT
     771                            bp.*,
     772
     773                            -- POPUST:
     774                            --   >= 9km  → 10%
     775                            --   <  9km  →  0%
     776                            CASE
     777                                WHEN bp.distance_km >= 9 THEN 10.0
     778                                ELSE                          0.0
     779                                END                                                 AS discount_pct,
     780
     781                            -- VREMINJA (site od ista request_time baza):
     782                            -- pickup  = request + 2..10 min
     783                            bp.request_time
     784                                + (2 + floor(random() * 9))  * interval '1 minute'
     785                                                                                    AS pickup_time,
     786
     787                            -- start   = request + 3..13 min  (sekogash posle pickup)
     788                            bp.request_time
     789                                + (3 + floor(random() * 11)) * interval '1 minute'
     790                                                                                    AS start_time,
     791
     792                            -- end     = start  + 5..15 min  (OGRANICHENO: min 5, max 15)
     793                            bp.request_time
     794                                + (3 + floor(random() * 11)) * interval '1 minute'  -- ista start baza
     795                                + (5 + floor(random() * 11)) * interval '1 minute'  -- + 5..15 min
     796                                                                                    AS end_time
     797
     798                        FROM base_pool bp
     799                    )
     800
     801                -- --------------------------------------------------------
     802                -- FINALEN SELECT: JOIN so realni ceni od vozacovoto vozilo
     803                -- --------------------------------------------------------
     804                SELECT
     805                    en.user_id,
     806                    en.driver_id,
     807                    en.pickup_location_id,
     808                    en.dropoff_location_id,
     809                    en.status_id,
     810
     811                    en.request_time,
     812                    en.pickup_time,
     813                    en.start_time,
     814                    en.end_time,
     815
     816                    en.distance_km,
     817
     818                    -- CENA spored tipot na voziloto:
     819                    --   Economy → (80  + km * 40) * (1 - popust%)
     820                    --   Luxury  → (100 + km * 60) * (1 - popust%)
     821                    --   Ako vozacot nema vozilo → fallback na Economy cena
     822                    ROUND(
     823                            (COALESCE(dp.base_price, 80) + en.distance_km * COALESCE(dp.price_per_km, 40))
     824                                * (1.0 - en.discount_pct / 100.0)
     825                                ::numeric, 2)                                           AS final_price,
     826
     827                    en.discount_pct                                         AS discount_percentage,
     828                    CASE
     829                        WHEN en.driver_id < 100
     830                            THEN en.driver_id + (random(0,1) * 200)
     831                        ELSE 
     832                            en.driver_id 
     833                        END AS vehicle_ownership_id
     834
     835
     836                FROM enriched en
     837                         LEFT JOIN driver_pricing dp ON en.driver_id = dp.driver_id;
     838
     839                RAISE NOTICE 'Batch % / % zavrshen — % redovi vkupno', b, batches, b * batch_size;
     840
     841            END LOOP;
     842
     843        update rides set pickup_location_id = 1 + (Rides.ride_id + floor(random() * 200000))::INTEGER% max_location
     844        where  dropoff_location_id=pickup_location_id;
     845
     846        RAISE NOTICE '*** GOTOVO: 10 000 000 redovi vo Rides ***';
     847    END $$;
     848   
     849--Payments   
     850DO $$
     851    DECLARE
     852        batch_size  INT := 1000000;
     853        total_rows  INT := 10000000;
     854        batches     INT := total_rows / batch_size;
     855        b           INT;
     856
     857        -- Payment method IDs
     858        pm_cash     INT;
     859        pm_card     INT;
     860
     861        -- Payment status IDs
     862        sid_pending   INT;
     863        sid_completed INT;
     864        sid_failed    INT;
     865        sid_refunded  INT;
     866
     867        -- Ride status IDs (za da odredime payment status)
     868        rid_completed   INT;
     869        rid_cancelled   INT;
     870
     871    BEGIN
     872        -- Payment methods
     873        SELECT payment_method_id INTO pm_cash FROM Payment_methods WHERE payment_tip = 'CASH' LIMIT 1;
     874        SELECT payment_method_id INTO pm_card FROM Payment_methods WHERE payment_tip = 'CARD' LIMIT 1;
     875
     876        -- Payment statusi
     877        SELECT status_id INTO sid_pending   FROM Status WHERE status_tip='payment' AND status_text='pending'   LIMIT 1;
     878        SELECT status_id INTO sid_completed FROM Status WHERE status_tip='payment' AND status_text='completed' LIMIT 1;
     879        SELECT status_id INTO sid_failed    FROM Status WHERE status_tip='payment' AND status_text='failed'    LIMIT 1;
     880        SELECT status_id INTO sid_refunded  FROM Status WHERE status_tip='payment' AND status_text='refunded'  LIMIT 1;
     881
     882        -- Ride statusi
     883        SELECT status_id INTO rid_completed FROM Status WHERE status_tip='ride' AND status_text='completed'  LIMIT 1;
     884        SELECT status_id INTO rid_cancelled FROM Status WHERE status_tip='ride' AND status_text='cancelled'  LIMIT 1;
     885
     886        RAISE NOTICE 'pm_cash=%, pm_card=%', pm_cash, pm_card;
     887        RAISE NOTICE 'payment statusi: pending=%, completed=%, failed=%, refunded=%',
     888            sid_pending, sid_completed, sid_failed, sid_refunded;
     889
     890        FOR b IN 1..batches LOOP
     891
     892                INSERT INTO Payments (
     893                    amount,
     894                    payment_method_id,
     895                    payment_time,
     896                    ride_id,
     897                    status_id
     898                )
     899                SELECT
     900                    -- Amount = tocnata cena od ridot
     901                    r.final_price                                               AS amount,
     902
     903                    -- Payment method: 60% CASH, 40% CARD
     904                    CASE
     905                        WHEN (r.ride_id % 10) < 6 THEN pm_cash
     906                        ELSE                           pm_card
     907                        END                                                         AS payment_method_id,
     908
     909                    -- Payment time = end_time + 1..5 min
     910                    r.end_time + (1 + floor(random() * 5)) * interval '1 minute'
     911                        AS payment_time,
     912
     913                    r.ride_id                                                   AS ride_id,
     914
     915                    -- Payment status spored status na ridot:
     916                    --   completed   → completed
     917                    --   cancelled   → 50% failed, 50% refunded
     918                    --   site drugi  → pending
     919                    CASE
     920                        WHEN r.status_id = rid_completed THEN sid_completed
     921                        WHEN r.status_id = rid_cancelled THEN
     922                            CASE WHEN r.ride_id % 2 = 0 THEN sid_failed
     923                                 ELSE                        sid_refunded
     924                                END
     925                        ELSE sid_pending
     926                        END                                                         AS status_id
     927
     928                FROM Rides r
     929                WHERE r.ride_id BETWEEN (b - 1) * batch_size + 1
     930                          AND b       * batch_size;
     931
     932                RAISE NOTICE 'Batch % / % zavrshen — % payments vkupno', b, batches, b * batch_size;
     933
     934            END LOOP;
     935
     936        RAISE NOTICE '*** GOTOVO: 10 000 000 redovi vo Payments ***';
     937    END $$;
     938   
     939INSERT INTO Cancellations (
     940    cancelled_by_type,
     941    cancelled_by_id,
     942    reason,
     943    cancellation_fee,
     944    created_at,
     945    ride_id
     946)
     947WITH
     948
     949-- Ride status za cancelled
     950cancelled_status AS (
     951    SELECT status_id FROM Status WHERE status_tip='ride' AND status_text='cancelled' LIMIT 1
     952),
     953
     954-- Realna base_price po vozac (Economy=80, Luxury=100)
     955driver_pricing AS (
     956    SELECT
     957        dvo.driver_id,
     958        MAX(pr.base_price) AS base_price
     959    FROM Drivers_Vehicle_ownership dvo
     960             JOIN Vehicle_ownership vo ON dvo.Vehicle_ownership_id = vo.Vehicle_ownership_id
     961             JOIN Vehicle_types     vt ON vo.vehicle_type_id       = vt.vehicle_type_id
     962             JOIN Pricing_rules     pr ON vt.vehicle_type_id       = pr.vehicle_type_id
     963    GROUP BY dvo.driver_id
     964),
     965
     966-- Samo cancelled rides + row_number za ramномерна distribucija
     967cancelled_rides AS (
     968    SELECT
     969        r.ride_id,
     970        r.user_id,
     971        r.driver_id,
     972        r.request_time,
     973        COALESCE(dp.base_price, 80)             AS base_price,
     974        -- row_number() garantira 0..9 ramномерно bez zavisnost od ride_id vrednostite
     975        (row_number() OVER (ORDER BY r.ride_id) - 1) % 10   AS rn
     976    FROM Rides r
     977             JOIN cancelled_status cs ON r.status_id = cs.status_id
     978             LEFT JOIN driver_pricing dp ON r.driver_id = dp.driver_id
     979)
     980
     981SELECT
     982    -- cancelled_by_type: 50% USER (0-4), 30% DRIVER (5-7), 20% ADMIN (8-9)
     983    CASE
     984        WHEN rn IN (0,1,2,3,4) THEN 'USER'
     985        WHEN rn IN (5,6,7)     THEN 'DRIVER'
     986        ELSE                        'ADMIN'
     987        END                                                         AS cancelled_by_type,
     988
     989    -- cancelled_by_id: realen ID
     990    CASE
     991        WHEN rn IN (0,1,2,3,4) THEN user_id
     992        WHEN rn IN (5,6,7)     THEN driver_id
     993        ELSE                        1 + (ride_id % 6)
     994        END                                                         AS cancelled_by_id,
     995
     996    -- Reason spored tipot
     997    CASE
     998        WHEN rn IN (0,1,2,3,4) THEN
     999            CASE (ride_id % 3)
     1000                WHEN 0 THEN 'Променив планови и повеќе не ми треба превоз'
     1001                WHEN 1 THEN 'Чекањето беше предолго'
     1002                ELSE        'Внесов погрешна локација за подигање'
     1003                END
     1004        WHEN rn IN (5,6,7) THEN
     1005            CASE (ride_id % 3)
     1006                WHEN 0 THEN 'Не можам да ја пронајдам точната локација на патникот'
     1007                WHEN 1 THEN 'Патникот не се појави на местото за подигање'
     1008                ELSE        'Технички проблем со возилото'
     1009                END
     1010        ELSE
     1011            CASE (ride_id % 3)
     1012                WHEN 0 THEN 'Вожњата е откажана поради сомнителна активност'
     1013                WHEN 1 THEN 'Откажано поради дупликат барање'
     1014                ELSE        'Откажано поради системска грешка'
     1015                END
     1016        END                                                         AS reason,
     1017
     1018    -- Fee: samo USER plaka, Driver i Admin = 0
     1019    CASE
     1020        WHEN rn IN (0,1,2,3,4) THEN base_price
     1021        ELSE                        0.00
     1022        END                                                         AS cancellation_fee,
     1023
     1024    -- created_at = request_time + 1..30 min
     1025    request_time + (1 + floor(random() * 30)) * interval '1 minute'
     1026                                                                    AS created_at,
     1027
     1028    ride_id
     1029FROM cancelled_rides;        
     1030
     1031   
     1032
     1033DO $$
     1034    DECLARE
     1035        batch_size INT := 500000;
     1036        b          INT;
     1037        max_ride   INT;
     1038        batches    INT;
     1039
     1040        mid_1 INT; mid_2 INT; mid_3 INT; mid_4 INT;
     1041        mid_5 INT; mid_6 INT; mid_7 INT; mid_8 INT; mid_9 INT;
     1042
     1043        sid_notif_sent   INT;
     1044        sid_notif_fail   INT;
     1045
     1046        rid_completed   INT;
     1047        rid_cancelled   INT;
     1048        rid_in_progress INT;
     1049        rid_accepted    INT;
     1050        rid_requested   INT;
     1051
     1052        pay_completed INT;
     1053        pay_failed    INT;
     1054    BEGIN
     1055        -- Message IDs
     1056        SELECT message_id INTO mid_1 FROM Messages WHERE message_text LIKE 'Vaseto baranje e uspesno%'                LIMIT 1;
     1057        SELECT message_id INTO mid_2 FROM Messages WHERE message_text LIKE 'Vozacot go prifati%'                      LIMIT 1;
     1058        SELECT message_id INTO mid_3 FROM Messages WHERE message_text LIKE 'Vozacot go odbi%'                         LIMIT 1;
     1059        SELECT message_id INTO mid_4 FROM Messages WHERE message_text LIKE 'Vozacot pristigna%'                       LIMIT 1;
     1060        SELECT message_id INTO mid_5 FROM Messages WHERE message_text LIKE 'Vozenjeto e otkazano od strana na vozac%' LIMIT 1;
     1061        SELECT message_id INTO mid_6 FROM Messages WHERE message_text LIKE 'Vozenjeto e otkazano od vasa%'            LIMIT 1;
     1062        SELECT message_id INTO mid_7 FROM Messages WHERE message_text LIKE 'Plakjanjeto e uspesno%'                   LIMIT 1;
     1063        SELECT message_id INTO mid_8 FROM Messages WHERE message_text LIKE 'Plakjanjeto ne uspea%'                    LIMIT 1;
     1064        SELECT message_id INTO mid_9 FROM Messages WHERE message_text LIKE 'Nemate dostapni%'                         LIMIT 1;
     1065
     1066        -- Notification statusi (koristime ride status kako proxy za sent/fail)
     1067        SELECT status_id INTO sid_notif_sent FROM Status WHERE status_tip='ride' AND status_text='completed' LIMIT 1;
     1068        SELECT status_id INTO sid_notif_fail FROM Status WHERE status_tip='ride' AND status_text='cancelled' LIMIT 1;
     1069
     1070        -- Ride statusi
     1071        SELECT status_id INTO rid_completed   FROM Status WHERE status_tip='ride' AND status_text='completed'   LIMIT 1;
     1072        SELECT status_id INTO rid_cancelled   FROM Status WHERE status_tip='ride' AND status_text='cancelled'   LIMIT 1;
     1073        SELECT status_id INTO rid_in_progress FROM Status WHERE status_tip='ride' AND status_text='in_progress' LIMIT 1;
     1074        SELECT status_id INTO rid_accepted    FROM Status WHERE status_tip='ride' AND status_text='accepted'    LIMIT 1;
     1075        SELECT status_id INTO rid_requested   FROM Status WHERE status_tip='ride' AND status_text='requested'   LIMIT 1;
     1076
     1077        -- Payment statusi
     1078        SELECT status_id INTO pay_completed FROM Status WHERE status_tip='payment' AND status_text='completed' LIMIT 1;
     1079        SELECT status_id INTO pay_failed    FROM Status WHERE status_tip='payment' AND status_text='failed'    LIMIT 1;
     1080
     1081        SELECT MAX(ride_id) INTO max_ride FROM Rides;
     1082        batches := CEIL(max_ride::numeric / batch_size);
     1083
     1084        RAISE NOTICE 'Pocnuvame so % batches po % rides', batches, batch_size;
     1085
     1086        -- ===========================================================
     1087        --  FLOW 1: COMPLETED rides
     1088        --  Poraki: 1→2→4→7
     1089        --  Vreminja: request→pickup→start→end+2min
     1090        -- ===========================================================
     1091        FOR b IN 1..batches LOOP
     1092                INSERT INTO Notifications (message_id, sent_time, user_id, ride_id, status_id)
     1093                SELECT * FROM (
     1094
     1095                                  -- Poraka 1: Baranjeto e uspesno isprateno → request_time
     1096                                  SELECT mid_1, r.request_time, r.user_id, r.ride_id, sid_notif_sent
     1097                                  FROM Rides r
     1098                                  WHERE r.ride_id BETWEEN (b-1)*batch_size+1 AND b*batch_size
     1099                                    AND r.status_id = rid_completed
     1100
     1101                                  UNION ALL
     1102
     1103                                  -- Poraka 2: Vozacot go prifati → pickup_time
     1104                                  SELECT mid_2, r.pickup_time, r.user_id, r.ride_id, sid_notif_sent
     1105                                  FROM Rides r
     1106                                  WHERE r.ride_id BETWEEN (b-1)*batch_size+1 AND b*batch_size
     1107                                    AND r.status_id = rid_completed
     1108
     1109                                  UNION ALL
     1110
     1111                                  -- Poraka 4: Vozacot pristigna → start_time
     1112                                  SELECT mid_4, r.start_time, r.user_id, r.ride_id, sid_notif_sent
     1113                                  FROM Rides r
     1114                                  WHERE r.ride_id BETWEEN (b-1)*batch_size+1 AND b*batch_size
     1115                                    AND r.status_id = rid_completed
     1116
     1117                                  UNION ALL
     1118
     1119                                  -- Poraka 7: Plakjanjeto uspesno → end_time + 2..5 min
     1120                                  SELECT mid_7,
     1121                                         r.end_time + (2 + floor(random()*4)) * interval '1 minute',
     1122                                         r.user_id, r.ride_id, sid_notif_sent
     1123                                  FROM Rides r
     1124                                           JOIN Payments p ON r.ride_id = p.ride_id AND p.status_id = pay_completed
     1125                                  WHERE r.ride_id BETWEEN (b-1)*batch_size+1 AND b*batch_size
     1126                                    AND r.status_id = rid_completed
     1127
     1128                              ) flows;
     1129
     1130                RAISE NOTICE '[COMPLETED] Batch % / %', b, batches;
     1131            END LOOP;
     1132
     1133        -- ===========================================================
     1134        --  FLOW 2: CANCELLED BY USER
     1135        --  Poraki: 1→6→8
     1136        --  Vreminja: request→request+1..10min→end+2min
     1137        -- ===========================================================
     1138        FOR b IN 1..batches LOOP
     1139                INSERT INTO Notifications (message_id, sent_time, user_id, ride_id, status_id)
     1140                SELECT * FROM (
     1141
     1142                                  -- Poraka 1: Baranjeto isprateno → request_time
     1143                                  SELECT mid_1, r.request_time, r.user_id, r.ride_id, sid_notif_sent
     1144                                  FROM Rides r
     1145                                           JOIN Cancellations c ON r.ride_id = c.ride_id AND c.cancelled_by_type = 'USER'
     1146                                  WHERE r.ride_id BETWEEN (b-1)*batch_size+1 AND b*batch_size
     1147                                    AND r.status_id = rid_cancelled
     1148
     1149                                  UNION ALL
     1150
     1151                                  -- Poraka 6: Otkazano od vasa strana → request_time + 1..10 min
     1152                                  SELECT mid_6,
     1153                                         r.request_time + (1 + floor(random()*10)) * interval '1 minute',
     1154                                         r.user_id, r.ride_id, sid_notif_fail
     1155                                  FROM Rides r
     1156                                           JOIN Cancellations c ON r.ride_id = c.ride_id AND c.cancelled_by_type = 'USER'
     1157                                  WHERE r.ride_id BETWEEN (b-1)*batch_size+1 AND b*batch_size
     1158                                    AND r.status_id = rid_cancelled
     1159
     1160                                  UNION ALL
     1161
     1162                                  -- Poraka 8: Plakjanjeto ne uspea → end_time + 2..5 min
     1163                                  SELECT mid_8,
     1164                                         r.end_time + (2 + floor(random()*4)) * interval '1 minute',
     1165                                         r.user_id, r.ride_id, sid_notif_fail
     1166                                  FROM Rides r
     1167                                           JOIN Payments p ON r.ride_id = p.ride_id AND p.status_id = pay_failed
     1168                                           JOIN Cancellations c ON r.ride_id = c.ride_id AND c.cancelled_by_type = 'USER'
     1169                                  WHERE r.ride_id BETWEEN (b-1)*batch_size+1 AND b*batch_size
     1170                                    AND r.status_id = rid_cancelled
     1171
     1172                              ) flows;
     1173
     1174                RAISE NOTICE '[CANCELLED USER] Batch % / %', b, batches;
     1175            END LOOP;
     1176
     1177        -- ===========================================================
     1178        --  FLOW 3: CANCELLED BY DRIVER
     1179        --  Poraki: 1→2→5
     1180        --  Vreminja: request→pickup→request+5..15min
     1181        -- ===========================================================
     1182        FOR b IN 1..batches LOOP
     1183                INSERT INTO Notifications (message_id, sent_time, user_id, ride_id, status_id)
     1184                SELECT * FROM (
     1185
     1186                                  -- Poraka 1: Baranjeto isprateno → request_time
     1187                                  SELECT mid_1, r.request_time, r.user_id, r.ride_id, sid_notif_sent
     1188                                  FROM Rides r
     1189                                           JOIN Cancellations c ON r.ride_id = c.ride_id AND c.cancelled_by_type = 'DRIVER'
     1190                                  WHERE r.ride_id BETWEEN (b-1)*batch_size+1 AND b*batch_size
     1191                                    AND r.status_id = rid_cancelled
     1192
     1193                                  UNION ALL
     1194
     1195                                  -- Poraka 2: Vozacot prifati → pickup_time
     1196                                  SELECT mid_2, r.pickup_time, r.user_id, r.ride_id, sid_notif_sent
     1197                                  FROM Rides r
     1198                                           JOIN Cancellations c ON r.ride_id = c.ride_id AND c.cancelled_by_type = 'DRIVER'
     1199                                  WHERE r.ride_id BETWEEN (b-1)*batch_size+1 AND b*batch_size
     1200                                    AND r.status_id = rid_cancelled
     1201
     1202                                  UNION ALL
     1203
     1204                                  -- Poraka 5: Otkazano od vozac → request_time + 5..15 min
     1205                                  SELECT mid_5,
     1206                                         r.request_time + (5 + floor(random()*11)) * interval '1 minute',
     1207                                         r.user_id, r.ride_id, sid_notif_fail
     1208                                  FROM Rides r
     1209                                           JOIN Cancellations c ON r.ride_id = c.ride_id AND c.cancelled_by_type = 'DRIVER'
     1210                                  WHERE r.ride_id BETWEEN (b-1)*batch_size+1 AND b*batch_size
     1211                                    AND r.status_id = rid_cancelled
     1212
     1213                              ) flows;
     1214
     1215                RAISE NOTICE '[CANCELLED DRIVER] Batch % / %', b, batches;
     1216            END LOOP;
     1217
     1218        -- ===========================================================
     1219        --  FLOW 4: CANCELLED BY ADMIN
     1220        --  Poraki: 1→9
     1221        --  Vreminja: request→request (momentalno)
     1222        -- ===========================================================
     1223        FOR b IN 1..batches LOOP
     1224                INSERT INTO Notifications (message_id, sent_time, user_id, ride_id, status_id)
     1225                SELECT * FROM (
     1226
     1227                                  -- Poraka 1: Baranjeto isprateno → request_time
     1228                                  SELECT mid_1, r.request_time, r.user_id, r.ride_id, sid_notif_sent
     1229                                  FROM Rides r
     1230                                           JOIN Cancellations c ON r.ride_id = c.ride_id AND c.cancelled_by_type = 'ADMIN'
     1231                                  WHERE r.ride_id BETWEEN (b-1)*batch_size+1 AND b*batch_size
     1232                                    AND r.status_id = rid_cancelled
     1233
     1234                                  UNION ALL
     1235
     1236                                  -- Poraka 9: Nema dostapni vozaci → request_time (vednas)
     1237                                  SELECT mid_9, r.request_time, r.user_id, r.ride_id, sid_notif_fail
     1238                                  FROM Rides r
     1239
     1240```