-- 2. Ecrire un bloc PL/pgSQL anonyme qui affiche

DO $$
    DECLARE
        a numeric := 0;
        b numeric := 1;
        c numeric;
        i integer := 0;
    BEGIN
        WHILE i<10 LOOP
            c=a+b;
            RAISE NOTICE 'a=% b=% c=%', a, b, c;
            a=b;
            b=c;
            i=i+1;
        END LOOP;
    END;
$$;

-- 3. Faire la meme mais en fonction pgSQL

CREATE OR REPLACE FUNCTION fibonacci(n integer) RETURNS SETOF integer AS $$
    DECLARE
        a numeric := 0;
        b numeric := 1;
        c numeric;
        i integer := 0;
    BEGIN
        WHILE i<(n-1) LOOP
            c=a+b;
            -- RAISE NOTICE 'a=% b=% c=%', a, b, c;
            a=b;
            b=c;
            i=i+1;
            RETURN NEXT c;
        END LOOP;
    END;
$$ LANGUAGE plpgsql;

-- 4. Appel de la fonction avec comme parametre 0, 1, 2, 10

SELECT fibonacci(0);

SELECT * FROM fibonacci(1);

SELECT * FROM fibonacci(2);

SELECT * FROM fibonacci(10);

-- fibonacci 
-- -----------
-- (0 ligne)

--  fibonacci 
-- -----------
-- (0 ligne)

--  fibonacci 
-- -----------
--          1
-- (1 ligne)

--  fibonacci 
-- -----------
--          1
--          2
--          3
--          5
--          8
--         13
--         21
--         34
--         55
-- (9 lignes)



-- 5. Ecrire une fonction en plpgsql nb_athletes qui retourn le nombre d'athlètes d'un pays dont le code est passé en paramètre

CREATE OR REPLACE FUNCTION nb_athletes(pays_code character varying) RETURNS integer AS $$
    DECLARE
        nb_athletes integer;
    BEGIN
        SELECT COUNT(*) INTO nb_athletes FROM athlete WHERE pays = pays_code;
        RETURN nb_athletes;
    END;
$$ LANGUAGE plpgsql;

-- 6. Utiliser la fonction précédente pour calculer le nombre d'athlèthes français

SELECT nb_athletes('FRA');

-- nb_athletes 
-- -------------
--          396
-- (1 ligne)


-- 7. Ecrire une requete sql utilisant la fonction nb_athletes pour afficher le code et le nom de tous les pays ayant plus d'athlètes que la France

SELECT pays, nom 
FROM pays 
WHERE nb_athletes(pays.code) > nb_athletes('FRA');

-- pays                |            nom             
-- ------------------------------------+----------------------------
--  (AUS,Australia)                    | Australia
--  (USA,"United States of America")   | United States of America
--  (GER,Germany)                      | Germany
--  (JPN,Japan)                        | Japan
--  (CHN,"People's Republic of China") | People's Republic of China
-- (5 lignes)

-- 8. Ecrire une fonction en pgpsql epr qui prend deux paramètres (le nom d'une discipline et une date) et qui retourne les epreuves (code et nom) de cette discipline ayant lieu à la date donnée

CREATE OR REPLACE FUNCTION epr(discipline varchar, date date) RETURNS SETOF epreuve AS $$
    DECLARE
        epreuve epreuve;
    BEGIN
        FOR epreuve IN SELECT * 
                        FROM epreuve e, discipline d
                        WHERE e.discipl = d.code AND e.dateE = date LOOP
            RETURN NEXT epreuve;
        END LOOP;
    END;
$$ LANGUAGE plpgsql;

-- %TYPE pour recuperer le type de la colonne

-- 9. Utiliser la fonction epr pour afficher les noms des epreuves d'athlètisme ('Athletics') ayant lieu le 3 août 2021

SELECT epr('Athletics', '2021-08-03');

-- epr                                  
-- ----------------------------------------------------------------------
--  (E206,GAR,"Men's Horizontal Bar",2021-08-03)
--  (E207,GAR,"Men's Parallel Bars",2021-08-03)
--  (E208,GAR,"Women's Balance Beam",2021-08-03)
--  (E209,ATH,"Men's 400m Hurdles",2021-08-03)
--  (E210,ATH,"Men's Pole Vault",2021-08-03)
--  (E211,ATH,"Women's 200m",2021-08-03)
--  (E212,ATH,"Women's 800m",2021-08-03)
--  (E213,ATH,"Women's Hammer Throw",2021-08-03)
--  (E214,ATH,"Women's Long Jump",2021-08-03)
--  (E215,BOX,"Men's Feather (52-57kg)",2021-08-03)
--  (E216,BOX,"Men's Heavy (81-91kg)",2021-08-03)
--  (E217,BOX,"Men's Welter (63-69kg)",2021-08-03)
--  (E218,BOX,"Women's Feather (54-57kg)",2021-08-03)
--  (E219,CSP,"Men's Canoe Double 1000m",2021-08-03)
--  (E220,CSP,"Men's Kayak Single 1000m",2021-08-03)
--  (E221,CSP,"Women's Kayak Double 500m",2021-08-03)
--  (E222,CSP,"Women's Kayak Single 200m",2021-08-03)
--  (E223,CTR,"Men's Team Sprint",2021-08-03)
--  (E224,CTR,"Women's Team Pursuit",2021-08-03)
--  (E225,DIV,"Men's 3m Springboard",2021-08-03)
--  (E226,SAL,"Men's One Person Dinghy (Heavyweight) - Finn",2021-08-03)
--  (E227,SAL,"Men's Skiff - 49er",2021-08-03)
--  (E228,SAL,"Mixed Multihull - Nacra 17 Foiling",2021-08-03)
--  (E229,SAL,"Women's Skiff - 49er FX",2021-08-03)
--  (E230,WLF,"Men's 109kg",2021-08-03)
--  (E231,WRE,"Men's Greco-Roman 77kg",2021-08-03)
--  (E232,WRE,"Men's Greco-Roman 97kg",2021-08-03)
--  (E233,WRE,"Women's Freestyle 68kg",2021-08-03)
-- (28 lignes)

-- 10. Le mauvais temps empêche la tenue de compétition en exterieur. Utiliser la fonction epr pour décaler d'un jour toutes les epreuves d'équitation ('Equestrian') ayant lieu le 2 août 2021

SELECT epr('Equestrian', '2021-08-02');

-- epr                        
-- ---------------------------------------------------
--  (E186,GAR,"Men's Rings",2021-08-02)
--  (E187,GAR,"Men's Vault",2021-08-02)
--  (E188,GAR,"Women's Floor Exercise",2021-08-02)
--  (E189,ATH,"Men's 3000m Steeplechase",2021-08-02)
--  (E190,ATH,"Men's Long Jump",2021-08-02)
--  (E191,ATH,"Women's 100m Hurdles",2021-08-02)
--  (E192,ATH,"Women's 5000m",2021-08-02)
--  (E193,ATH,"Women's Discus Throw",2021-08-02)
--  (E194,BDM,"Men's Singles",2021-08-02)
--  (E195,BDM,"Women's Doubles",2021-08-02)
--  (E196,CTR,"Women's Team Sprint",2021-08-02)
--  (E197,EQU,"Eventing Individual",2021-08-02)
--  (E198,EQU,"Eventing Team",2021-08-02)
--  (E199,SHO,"25m Rapid Fire Pistol Men",2021-08-02)
--  (E200,SHO,"50m Rifle 3 Positions Men",2021-08-02)
--  (E201,WLF,"Women's +87kg",2021-08-02)
--  (E202,WLF,"Women's 87kg",2021-08-02)
--  (E203,WRE,"Men's Greco-Roman 130kg",2021-08-02)
--  (E204,WRE,"Men's Greco-Roman 60kg",2021-08-02)
--  (E205,WRE,"Women's Freestyle 76kg",2021-08-02)
-- (20 lignes)

-- UPDATE 0

-- 11. Ecrire une fonction pratique qui retourne le nom de la discipline pratiqué par un athlète dont on passe le code en paramètre

CREATE OR REPLACE FUNCTION pratique(code_athlete athlete.code%TYPE) RETURNS discipline.nom%TYPE AS $$
    DECLARE
        res discipline.nom%TYPE;
    BEGIN
        SELECT d.nom INTO STRICT res FROM pratiquer p
        INNER JOIN discipline d ON d.code = p.discipl
        WHERE p.athlete = code_athlete;
        return res;
    EXCEPTION
        WHEN TOO_MANY_ROWS THEN
            RAISE EXCEPTION 'L''athlète % pratique plusieurs sports.', code_athlete;
        WHEN NO_DATA_FOUND THEN
            RAISE EXCEPTION 'Athlète % inconnu.', code_athlete;
    END;
$$ LANGUAGE plpgsql;


-- ? 12. Utiliser la fonction pratique pour trouver le sport pratiqué par Earvin NGAPETH

SELECT pratique(
    (
        SELECT code FROM athlete WHERE prenom = 'Earvin' AND nom = upper('Ngapeth')
    )
);

--   pratique  
-- ------------
--  Volleyball
-- (1 ligne)

-- ? 13. Meme question pour l'athlete Lotte KOPECKY

SELECT pratique(
    (
        SELECT code FROM athlete WHERE prenom = 'Lotte' AND nom = upper('Kopecky')
    )
);

-- ERREUR:  L'athlète A05315 pratique plusieurs sports.
-- CONTEXTE : fonction PL/pgSQL pratique(character), ligne 11 à RAISE

-- ? 14. Meme question pour l'thlete de code 'B01' (qui n'existe pas)

SELECT pratique('B01');

-- ASTUCE : N'a pas pu choisir un meilleur candidat dans les fonctions. Vous pourriez
-- avoir besoin d'ajouter des conversions explicites de type.

-- ? 15 Ecrire une fonction medailles calculant le pourcentage d'athlèthes médaillés parmis ceux du pays passé en paramètre

CREATE OR REPLACE FUNCTION medailles(code_pays pays.code%TYPE)
RETURNS FLOAT AS $$
DECLARE
    total_athletes INT;
    athletes_medailles INT;
BEGIN
    SELECT COUNT(*) INTO total_athletes
    FROM athlete
    WHERE pays = code_pays;

    IF total_athletes = 0 THEN
        RAISE EXCEPTION 'Le pays [%] % n''a pas d''athlète.', code_pays, (SELECT nom FROM pays WHERE code = code_pays);
    END IF;

    SELECT COUNT(DISTINCT r.athlete) INTO athletes_medailles
    FROM resultat r
    INNER JOIN athlete a ON a.code = r.athlete
    WHERE a.pays = code_pays;
    return athletes_medailles::FLOAT / total_athletes;
END;
$$ LANGUAGE plpgsql;

-- ? 16. Tester votre fonction medailles avec la France

SELECT medailles(
    (
        SELECT code FROM pays WHERE nom = 'France'
    )
);

--      medailles      
-- --------------------
--  0.3282828282828283
-- (1 ligne)

-- ? 17. Même question avec l'URSS

SELECT medailles(
    (
        SELECT code FROM pays WHERE nom = 'URSS'
    )
);

-- ERREUR:  Le pays [URS] URSS n'a pas d'athlète.
-- CONTEXTE : fonction PL/pgSQL medailles(character), ligne 11 à RAISE


-- ? 18. Utiliser la fonction medailles pour afficher tous les pays (nom et pourcentage de médaillés). avec les pays les plus détaillés (proportionellement parlant) en premier. Le pourcentage de médaillés devra avoir au plus 2décimales.

SELECT p.nom, ROUND((medailles(p.code) * 100)::numeric, 2) medailles
FROM resultat r
INNER JOIN athlete a ON a.code = r.athlete
INNER JOIN pays p ON p.code = a.pays
GROUP BY p.code
ORDER BY 2 DESC;

--             nom             | medailles 
-- ----------------------------+-----------
--  Fiji                       |     81.25
--  San Marino                 |     60.00
--  Bermuda                    |     50.00
--  Dominican Republic         |     46.97
--  United States of America   |     40.60
--  Serbia                     |     40.23
--  ROC                        |     37.54
--  France                     |     32.83
--  Botswana                   |     28.57
--  Great Britain              |     28.32
--  People's Republic of China |     27.08
--  Denmark                    |     26.85
--  New Zealand                |     26.75
--  Hungary                    |     26.74
--  Qatar                      |     25.00
--  Norway                     |     24.73
--  Bulgaria                   |     23.81
--  Armenia                    |     23.53
--  Chinese Taipei             |     23.53
--  Sweden                     |     23.53
--  Cuba                       |     23.19
--  Jamaica                    |     22.95
--  Georgia                    |     22.86
--  Argentina                  |     22.75
--  Netherlands                |     21.91
--  Indonesia                  |     21.43
--  Philippines                |     21.05
--  Spain                      |     20.83
--  Australia                  |     20.25
--  Belgium                    |     20.00
--  Greece                     |     19.51
--  India                      |     19.05
--  Kyrgyzstan                 |     18.75
--  Japan                      |     18.57
--  Canada                     |     18.39
--  Croatia                    |     18.33
--  Kosovo                     |     18.18
--  Italy                      |     17.11
--  Slovakia                   |     17.07
--  Germany                    |     17.07
--  Brazil                     |     16.93
--  Syrian Arab Republic       |     16.67
--  Grenada                    |     16.67
--  Mexico                     |     16.67
--  Ukraine                    |     16.34

-- 94 lignes

-- ? 19. Ecrire une fonction ageMoy qui calcule et retourne l'âge moyen des athlètes d'un pays dont le code est passé en paramètres

CREATE OR REPLACE FUNCTION ageMoy_V1(code_pays pays.code%TYPE)
RETURNS FLOAT AS $$ 
BEGIN
    return (
        SELECT AVG(CURRENT_DATE - dateNaiss) / 365.2422
        FROM athlete
        WHERE pays = code_pays
    );
END;
$$ LANGUAGE plpgsql;

-- ? 20. Modifier la fonction ageMoy pour qu'elle retourn 0 si aucun athlète ne concourt pour le pays ou si on ne connait l'âge d'aucun de ses athlètes

DROP FUNCTION IF EXISTS ageMoy_V2;
CREATE OR REPLACE FUNCTION ageMoy_V2(code_pays pays.code%TYPE)
RETURNS FLOAT AS $$
DECLARE
    i INT;
    fin INT;
    total INT := 0;
    nb INT := 0;
    date_naiss athlete.dateNaiss%TYPE;
BEGIN
    -- Oui.
    SELECT SUBSTR(MIN(code), 2)::int INTO i FROM athlete WHERE pays = code_pays;
    SELECT SUBSTR(MAX(code), 2)::int INTO fin FROM athlete WHERE pays = code_pays;

    if i IS NULL then
        return 0;
    end if;

    -- Teste pour chaque code possible
    while i <= fin loop
        SELECT dateNaiss INTO date_naiss FROM athlete WHERE code = to_char(i, 'FMA00000');
        if date_naiss IS NOT NULL then
            total = (CURRENT_DATE - date_naiss) + total;
            nb = nb + 1;
        end if;
        i = i + 1;
    end loop;

    if nb = 0 then
        return 0;
    end if;

    return total::FLOAT / nb / 365.2422;
END;
$$ LANGUAGE plpgsql;

-- ? 21. Utiliser cette fonction pour trouver le pays dont les thlètes sont en moyenne les plus jeunes

-- ? 22. Même question pour le pays dont les athlètes sont en moyenne les plus jeunes

CREATE OR REPLACE FUNCTION ageMoy_V1(code_pays pays.code%TYPE)
RETURNS FLOAT AS $$ 
BEGIN
    return (
        SELECT COALESCE(AVG(CURRENT_DATE - dateNaiss), 0) / 365.2422
        FROM athlete
        WHERE pays = code_pays AND dateNaiss IS NOT NULL
    );
END;
$$ LANGUAGE plpgsql;

SELECT ageMoy_V1('') v1, ageMoy_V2('') v2;

-- v1 | v2 
-- ----+----
--   0 |  0
-- (1 ligne)

WITH age_moyen AS (
    SELECT nom, ageMoy_V1(code) AS age_moyen FROM pays
) SELECT nom, age_moyen
FROM age_moyen
ORDER BY age_moyen DESC
LIMIT 1;

--          nom          |     age_moyen     
-- ----------------------+-------------------
--  United Arab Emirates | 34.15103731167976
-- (1 ligne)

WITH age_moyen AS (
    SELECT nom, ageMoy_V1(code) AS age_moyen FROM pays
) SELECT nom, age_moyen
FROM age_moyen
WHERE age_moyen != 0
ORDER BY age_moyen
LIMIT 1;

--               nom              |     age_moyen      
-- -------------------------------+--------------------
--  St Vincent and the Grenadines | 21.159475365478944
-- (1 ligne)

-- ? 23. Creer une fonction gagner qui prend en paramètre un code d'athlète et une couleur de médaille ('Or', 'Argent' ou 'Bronze') et qui retourne le nombre de médailles de cette couleur gagnées par cet athlète

CREATE OR REPLACE FUNCTION gagner(code_athlete athlete.code%TYPE, couleur_medaille medaille.couleur%TYPE)
RETURNS INT AS $$ 
BEGIN
    return (
        SELECT COUNT(*) FROM resultat WHERE athlete = code_athlete AND medaille = (
            SELECT place FROM medaille WHERE couleur = couleur_medaille
        )
    );
END;
$$ LANGUAGE plpgsql;

-- ? 24. Tester votre fonction en affichant le nombre de médailles d'or, d'argent et de bronze gagnées par l'athlète Teddy RINER

SELECT gagner(
    (SELECT code FROM athlete WHERE prenom = 'Teddy' AND nom = 'RINER'),
    'Or'
) medailles_or, gagner(
    (SELECT code FROM athlete WHERE prenom = 'Teddy' AND nom = 'RINER'),
    'Argent'
) medailles_argent, gagner(
    (SELECT code FROM athlete WHERE prenom = 'Teddy' AND nom = 'RINER'),
    'Bronze'
) medailles_bronze;

-- medailles_or | medailles_argent | medailles_bronze 
-- --------------+------------------+------------------
--             1 |                0 |                1
-- (1 ligne)

-- ? 25. Creer une table Resultat_FR(code, nom, prenom, dateNaiss, nb_or, nb_argent, nb_bronze) qui servira pour contenir les résultats des athlètes français

CREATE TABLE IF NOT EXISTS resultat_fr (
    code CHAR(6) PRIMARY KEY REFERENCES athlete(code),
    nom VARCHAR(40) NOT NULL,
    prenom VARCHAR(40) NOT NULL,
    dateNaiss DATE,
    nb_or INT DEFAULT 0,
    nb_argent INT DEFAULT 0,
    nb_bronze INT DEFAULT 0
);

-- ? 26. Remplir la table Resultat_FR avec une requete INSERT

INSERT INTO resultat_fr
    SELECT code, nom, prenom, dateNaiss, gagner(code, 'Or'), gagner(code, 'Argent'), gagner(code, 'Bronze')
    FROM athlete
    WHERE pays = (SELECT code FROM pays WHERE nom = 'France');

-- INSERT 0 396

-- ? 27. Afficher les resultats des athlètes fraçais en les ordonnant en fonction du nombre total de médailles gagnées (les gagnants du plus grand nombre de médailles en premier), puis par date de naissance (les athlètes les plus agés en premier)

SELECT * 
FROM resultat_fr 
ORDER BY (nb_or + nb_argent + nb_bronze) DESC, dateNaiss;

-- code  |         nom         |       prenom       | datenaiss  | nb_or | nb_argent | nb_bronze 
-- --------+---------------------+--------------------+------------+-------+-----------+-----------
--  A08619 | RINER               | Teddy              | 1989-04-07 |     1 |         0 |         1
--  A00129 | AGBEGNENOU          | Clarisse           | 1992-10-25 |     2 |         0 |         0
--  A06252 | MALONGA             | Madeleine          | 1993-12-25 |     1 |         1 |         0
--  A01407 | BUCHARD             | Amandine           | 1995-07-12 |     1 |         1 |         0
--  A01390 | BRUNET              | Manon              | 1996-02-07 |     0 |         1 |         1
--  A02151 | CYSIQUE             | Sarah Leonie       | 1998-07-06 |     1 |         1 |         0
--  A02474 | DICKO               | Romane             | 1999-09-30 |     1 |         0 |         1
--  A05542 | LAGHOUAG            | Karim Florent      | 1975-08-04 |     0 |         0 |         1
--  A10338 | TOUZAINT            | Nicolas            | 1980-05-10 |     0 |         0 |         1
--  A03395 | GENTY               | Yann               | 1981-12-26 |     1 |         0 |         0
--  A05650 | le PECHOUX          | Erwann             | 1982-01-13 |     1 |         0 |         0
--  A03762 | GUIGOU              | Michael            | 1982-01-28 |     1 |         0 |         0
--  A03801 | GUYART              | Astrid             | 1983-03-17 |     0 |         1 |         0
--  A04842 | KARABATIC           | Nikola             | 1984-04-11 |     1 |         0 |         0
--  A00006 | ABALO               | Luc                | 1984-09-06 |     1 |         0 |         0
--  A08096 | PICON               | Charline           | 1984-12-23 |     0 |         1 |         0
--  A05666 | LECOINTRE           | Camille            | 1985-02-25 |     0 |         0 |         1
--  A09538 | SIX                 | Christopher        | 1985-12-12 |     0 |         0 |         1
--  A04242 | HORTA               | Fanny              | 1986-01-22 |     0 |         1 |         0
--  A05786 | LEYNAUD             | Amandine           | 1986-05-02 |     1 |         0 |         0
--  A01702 | CHAINE              | Guillaume          | 1986-10-24 |     1 |         0 |         0
--  A03406 | GERARD              | Vincent            | 1986-12-16 |     1 |         0 |         0
--  A01964 | CLERGET             | Axel               | 1987-02-28 |     1 |         0 |         0
--  A05532 | LACRABERE           | Alexandra          | 1987-04-27 |     1 |         0 |         0
--  A02279 | de COLO             | Nando              | 1987-06-23 |     0 |         1 |         0
--  A03725 | GRUDA               | Sandrine           | 1987-06-25 |     0 |         0 |         1
--  A02203 | DANCETTE            | Blandine           | 1988-02-14 |     1 |         0 |         0
--  A04841 | KARABATIC           | Luka               | 1988-04-19 |     1 |         0 |         0
--  A06873 | MIYEM               | Endene             | 1988-05-15 |     0 |         0 |         1
--  A06706 | MERTINE             | Julien             | 1988-06-25 |     1 |         0 |         0
--  A02719 | EDWIGE              | Beatrice           | 1988-10-03 |     1 |         0 |         0
--  A00863 | BATUM               | Nicolas            | 1988-12-14 |     0 |         1 |         0
--  A06746 | MICHEL              | Sarah              | 1989-01-10 |     0 |         0 |         1
--  A04110 | HEURTEL             | Thomas             | 1989-04-10 |     0 |         1 |         0
--  A08133 | PINEAU              | Allison            | 1989-05-02 |     1 |         0 |         0
--  A06053 | LUIS                | Vincent            | 1989-06-27 |     0 |         0 |         1
--  A02223 | DARLEUX             | Cleopatre          | 1989-07-01 |     1 |         0 |         0
--  A05645 | le BLOUCH           | Kilian             | 1989-10-07 |     1 |         0 |         0
--  A10287 | TONIUTTI            | Benjamin           | 1989-10-30 |     1 |         0 |         0
--  A07942 | PELLE               | Chloe              | 1989-11-14 |     0 |         1 |         0
--  A00982 | BERDER              | Cecilia            | 1989-12-13 |     0 |         1 |         0
--  A01912 | CIAK                | Helena             | 1989-12-15 |     0 |         0 |         1
--  A00223 | ALBICY              | Andrew             | 1990-03-21 |     0 |         1 |         0
--  A00434 | ANDRODIAS           | Matthieu           | 1990-06-11 |     1 |         0 |         0
--  A03667 | GREBENNIKOV         | Jenia              | 1990-08-13 |     1 |         0 |         0

--  396 lignes