Changes between Initial Version and Version 1 of DatabaseProgramming


Ignore:
Timestamp:
05/26/26 11:11:34 (14 hours ago)
Author:
231550
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • DatabaseProgramming

    v1 v1  
     1== Procedures/Functions/Triggers ==
     2
     3-- 1. FUNCTION: Calculate total calories for a user on a specific date
     4CREATE OR REPLACE FUNCTION get_user_daily_calories(
     5    p_user_id BIGINT,
     6    p_date DATE
     7)
     8RETURNS NUMERIC AS $$
     9DECLARE
     10    total_calories NUMERIC := 0;
     11BEGIN
     12    SELECT COALESCE(SUM(f.calories_per_100g * fl.quantity / 100), 0)
     13    INTO total_calories
     14    FROM FoodLogs fl
     15    JOIN Food f ON fl.FoodId = f.id
     16    WHERE fl.UserId = p_user_id
     17    AND DATE(fl.dateTime) >= p_date AND DATE(fl.dateTime) < (p_date + 1);
     18
     19    RETURN total_calories;
     20END;
     21$$ LANGUAGE plpgsql;
     22
     23-- 2. PROCEDURE: Log a food entry for a user
     24CREATE OR REPLACE PROCEDURE log_food_entry(
     25    p_user_id   BIGINT,
     26    p_food_id   BIGINT,
     27    p_quantity  NUMERIC,
     28    p_unit      VARCHAR(10),
     29    p_meal_type VARCHAR(20)
     30)
     31LANGUAGE plpgsql AS $$
     32BEGIN
     33    IF p_meal_type NOT IN ('breakfast', 'lunch', 'dinner', 'snack') THEN
     34        RAISE EXCEPTION 'Invalid meal type: %. Must be breakfast, lunch, dinner or snack.', p_meal_type;
     35    END IF;
     36
     37    IF p_quantity <= 0 THEN
     38        RAISE EXCEPTION 'Quantity must be greater than 0.';
     39    END IF;
     40
     41    IF NOT EXISTS (SELECT 1 FROM "User" WHERE id = p_user_id) THEN
     42        RAISE EXCEPTION 'User with id % does not exist.', p_user_id;
     43    END IF;
     44
     45    IF NOT EXISTS (SELECT 1 FROM Food WHERE id = p_food_id) THEN
     46        RAISE EXCEPTION 'Food with id % does not exist.', p_food_id;
     47    END IF;
     48
     49    INSERT INTO FoodLogs (quantity, unit, meal_type, dateTime, UserId, FoodId)
     50    VALUES (p_quantity, p_unit, p_meal_type, NOW(), p_user_id, p_food_id);
     51
     52    RAISE NOTICE 'Food log entry created for user % at %', p_user_id, NOW();
     53END;
     54$$;
     55
     56-- 3. TRIGGER: Automatically update calories_per_100g when nutrients are inserted
     57CREATE OR REPLACE FUNCTION update_food_calories()
     58RETURNS TRIGGER AS $$
     59DECLARE
     60    kcal_nutrient_id BIGINT;
     61BEGIN
     62    SELECT id INTO kcal_nutrient_id
     63    FROM Nutrient
     64    WHERE LOWER(nutrient) LIKE '%energy-kcal_100g%'
     65    LIMIT 1;
     66
     67    IF NEW.NutrientId = kcal_nutrient_id THEN
     68        UPDATE Food
     69        SET calories_per_100g = NEW.amount::INT
     70        WHERE id = NEW.FoodId;
     71
     72        RAISE NOTICE 'Updated calories_per_100g for food % to %', NEW.FoodId, NEW.amount;
     73    END IF;
     74
     75    RETURN NEW;
     76END;
     77$$ LANGUAGE plpgsql;
     78
     79CREATE OR REPLACE TRIGGER trg_update_food_calories
     80AFTER INSERT OR UPDATE ON Food_nutrients
     81FOR EACH ROW
     82EXECUTE FUNCTION update_food_calories();
     83
     84-- 4. FUNCTION: Calculate total calories burned for a user on a specific date
     85CREATE OR REPLACE FUNCTION get_user_daily_calories_burned(
     86    p_user_id BIGINT,
     87    p_date    DATE
     88)
     89RETURNS NUMERIC AS $$
     90DECLARE
     91    total_burned NUMERIC := 0;
     92BEGIN
     93    SELECT COALESCE(SUM(al.Calories_burned), 0)
     94    INTO total_burned
     95    FROM ActivityLogs al
     96    WHERE al.UserId = p_user_id
     97    AND DATE(al.dateTime) = p_date;
     98
     99    RETURN total_burned;
     100END;
     101$$ LANGUAGE plpgsql;
     102
     103-- 5. PROCEDURE: Log an activity entry for a user
     104CREATE OR REPLACE PROCEDURE log_activity_entry(
     105    p_user_id     BIGINT,
     106    p_exercise_id BIGINT,
     107    p_duration    INT
     108)
     109LANGUAGE plpgsql AS $$
     110DECLARE
     111    v_calories_per_minute NUMERIC;
     112    v_calories_burned     NUMERIC;
     113BEGIN
     114    IF p_duration <= 0 THEN
     115        RAISE EXCEPTION 'Duration must be greater than 0.';
     116    END IF;
     117
     118    IF NOT EXISTS (SELECT 1 FROM "User" WHERE id = p_user_id) THEN
     119        RAISE EXCEPTION 'User with id % does not exist.', p_user_id;
     120    END IF;
     121
     122    SELECT calories_per_minute INTO v_calories_per_minute
     123    FROM Exercise WHERE id = p_exercise_id;
     124
     125    IF NOT FOUND THEN
     126        RAISE EXCEPTION 'Exercise with id % does not exist.', p_exercise_id;
     127    END IF;
     128
     129    v_calories_burned := v_calories_per_minute * p_duration;
     130
     131    INSERT INTO ActivityLogs (dateTime, UserId, Duration_minutes, Calories_burned, ExerciseId)
     132    VALUES (NOW(), p_user_id, p_duration, v_calories_burned, p_exercise_id);
     133
     134    RAISE NOTICE 'Activity logged for user %. Duration: % min, Calories burned: %',
     135        p_user_id, p_duration, v_calories_burned;
     136END;
     137$$;
     138
     139-- 6. TRIGGER: Automatically update streak when activity log is inserted
     140CREATE OR REPLACE FUNCTION update_activity_streak()
     141RETURNS TRIGGER AS $$
     142DECLARE
     143    v_streak_type_id BIGINT;
     144    v_last_updated   DATE;
     145    v_current_count  INT;
     146BEGIN
     147    SELECT id INTO v_streak_type_id
     148    FROM Streak_type
     149    WHERE LOWER(metric_type) = 'workouts'
     150    LIMIT 1;
     151
     152    IF v_streak_type_id IS NULL THEN
     153        RETURN NEW;
     154    END IF;
     155
     156    SELECT last_updated, current_count
     157    INTO v_last_updated, v_current_count
     158    FROM Streak
     159    WHERE User_id = NEW.UserId
     160    AND Streak_type_id = v_streak_type_id;
     161
     162    IF NOT FOUND THEN
     163        INSERT INTO Streak (User_id, Streak_type_id, current_count, longest_count, last_updated)
     164        VALUES (NEW.UserId, v_streak_type_id, 1, 1, CURRENT_DATE);
     165
     166    ELSIF v_last_updated = CURRENT_DATE THEN
     167        RETURN NEW;
     168
     169    ELSIF v_last_updated = CURRENT_DATE - INTERVAL '1 day' THEN
     170        UPDATE Streak
     171        SET current_count = current_count + 1,
     172            longest_count = GREATEST(longest_count, current_count + 1),
     173            last_updated  = CURRENT_DATE
     174        WHERE User_id = NEW.UserId
     175        AND Streak_type_id = v_streak_type_id;
     176
     177    ELSE
     178        UPDATE Streak
     179        SET current_count = 1,
     180            last_updated  = CURRENT_DATE
     181        WHERE User_id = NEW.UserId
     182        AND Streak_type_id = v_streak_type_id;
     183    END IF;
     184
     185    RETURN NEW;
     186END;
     187$$ LANGUAGE plpgsql;
     188
     189CREATE OR REPLACE TRIGGER trg_update_activity_streak
     190AFTER INSERT ON ActivityLogs
     191FOR EACH ROW
     192EXECUTE FUNCTION update_activity_streak();
     193
     194-- 7. FUNCTION: Check if user has met their daily calorie target
     195CREATE OR REPLACE FUNCTION check_calorie_target(p_user_id BIGINT, p_date DATE)
     196RETURNS TABLE (
     197    calorie_target INT,
     198    actual_calories NUMERIC,
     199    difference NUMERIC,
     200    target_met BOOLEAN
     201              ) AS $$
     202BEGIN
     203    RETURN QUERY
     204    WITH daily_stats AS (
     205        SELECT get_user_daily_calories(p_user_id, p_date) as actual
     206    )
     207    SELECT
     208        d.calorie_target,
     209        s.actual,
     210        s.actual - d.calorie_target,
     211        s.actual >= d.calorie_target
     212    FROM User_Diets ud
     213    CROSS JOIN daily_stats s
     214    JOIN Diet d ON ud.Diet_id = d.id
     215    WHERE ud.user_id = p_user_id
     216    AND p_date BETWEEN ud.start_date AND ud.end_date
     217    LIMIT 1;
     218END;
     219$$ LANGUAGE plpgsql;
     220
     221-- 8. PROCEDURE: Assign a diet to a user
     222CREATE OR REPLACE PROCEDURE assign_diet_to_user(
     223    p_user_id    BIGINT,
     224    p_diet_id    BIGINT,
     225    p_start_date DATE,
     226    p_end_date   DATE
     227)
     228LANGUAGE plpgsql AS $$
     229BEGIN
     230    IF p_end_date < p_start_date THEN
     231        RAISE EXCEPTION 'End date cannot be before start date.';
     232    END IF;
     233
     234    IF NOT EXISTS (SELECT 1 FROM "User" WHERE id = p_user_id) THEN
     235        RAISE EXCEPTION 'User with id % does not exist.', p_user_id;
     236    END IF;
     237
     238    IF NOT EXISTS (SELECT 1 FROM Diet WHERE id = p_diet_id) THEN
     239        RAISE EXCEPTION 'Diet with id % does not exist.', p_diet_id;
     240    END IF;
     241
     242    IF EXISTS (
     243        SELECT 1 FROM User_Diets
     244        WHERE user_id = p_user_id
     245        AND (p_start_date, p_end_date) OVERLAPS (start_date, end_date)
     246    ) THEN
     247        RAISE EXCEPTION 'User already has an active diet in this period.';
     248    END IF;
     249
     250    INSERT INTO User_Diets (user_id, Diet_id, start_date, end_date)
     251    VALUES (p_user_id, p_diet_id, p_start_date, p_end_date);
     252
     253    RAISE NOTICE 'Diet % assigned to user % from % to %',
     254        p_diet_id, p_user_id, p_start_date, p_end_date;
     255END;
     256$$;
     257
     258-- 9. PROCEDURE: Add body measurements
     259CREATE OR REPLACE PROCEDURE add_body_measurement(
     260    p_user_id  BIGINT,
     261    p_weight   NUMERIC,
     262    p_waist    NUMERIC,
     263    p_body_fat NUMERIC
     264)
     265LANGUAGE plpgsql AS $$
     266BEGIN
     267    IF NOT EXISTS (SELECT 1 FROM "User" WHERE id = p_user_id) THEN
     268        RAISE EXCEPTION 'User with id % does not exist.', p_user_id;
     269    END IF;
     270
     271    IF EXISTS (
     272        SELECT 1 FROM BodyMeasurements
     273        WHERE User_id = p_user_id
     274        AND DATE(dateTime) = CURRENT_DATE
     275    ) THEN
     276        UPDATE BodyMeasurements
     277        SET weight   = p_weight,
     278            waist    = p_waist,
     279            body_fat = p_body_fat,
     280            dateTime = NOW()
     281        WHERE User_id = p_user_id
     282        AND DATE(dateTime) = CURRENT_DATE;
     283
     284        RAISE NOTICE 'Updated existing measurement for user % today.', p_user_id;
     285    ELSE
     286        INSERT INTO BodyMeasurements (User_id, weight, waist, body_fat, dateTime)
     287        VALUES (p_user_id, p_weight, p_waist, p_body_fat, NOW());
     288
     289        RAISE NOTICE 'New measurement added for user %.', p_user_id;
     290    END IF;
     291END;
     292$$;
     293
     294-- 10. FUNCTION: Get daily nutrition summary
     295CREATE OR REPLACE FUNCTION get_user_nutrition_summary(
     296    p_user_id BIGINT,
     297    p_date    DATE
     298)
     299RETURNS TABLE (
     300    total_protein   NUMERIC,
     301    total_carbs     NUMERIC,
     302    total_fat       NUMERIC,
     303    total_fiber     NUMERIC
     304) AS $$
     305BEGIN
     306    RETURN QUERY
     307    SELECT
     308        COALESCE(SUM(CASE WHEN LOWER(n.nutrient) LIKE '%protein%'
     309            THEN fn.amount * fl.quantity / 100 ELSE 0 END), 0) AS total_protein,
     310        COALESCE(SUM(CASE WHEN LOWER(n.nutrient) LIKE '%carbohydrat%'
     311            THEN fn.amount * fl.quantity / 100 ELSE 0 END), 0) AS total_carbs,
     312        COALESCE(SUM(CASE WHEN LOWER(n.nutrient) LIKE '%fat%'
     313            AND LOWER(n.nutrient) NOT LIKE '%saturated%'
     314            THEN fn.amount * fl.quantity / 100 ELSE 0 END), 0) AS total_fat,
     315        COALESCE(SUM(CASE WHEN LOWER(n.nutrient) LIKE '%fiber%'
     316            OR LOWER(n.nutrient) LIKE '%fibre%'
     317            THEN fn.amount * fl.quantity / 100 ELSE 0 END), 0) AS total_fiber
     318    FROM FoodLogs fl
     319    JOIN Food_nutrients fn ON fl.FoodId = fn.FoodId
     320    JOIN Nutrient n ON fn.NutrientId = n.id
     321    WHERE fl.UserId = p_user_id
     322    AND DATE(fl.dateTime) >= p_date AND DATE(fl.dateTime) < (p_date + 1);
     323END;
     324$$ LANGUAGE plpgsql;