show server_version; -- ---------------------------------------------------------------------- -- extensions -- ---------------------------------------------------------------------- select * from pg_available_extensions; create schema postgis; create extension if not exists postgis schema postgis; create extension if not exists pgrouting schema postgis; create schema ext; create extension if not exists ltree schema ext; create extension if not exists pgcrypto schema ext; create extension if not exists vector schema ext; create extension if not exists isn schema ext; create schema pgtap; create extension if not exists pgtap schema pgtap; -- ---------------------------------------------------------------------- -- Pays -- ---------------------------------------------------------------------- create table pays ( code2 text not null, code3 text not null, code_num text not null, pays text not null, forme_longue text, nom_eng text, nom_spa text, drapeau_unicode character(2) ); comment on column pays.code2 is 'code ISO 3166-1 alpha 2'; comment on column pays.code3 is 'code ISO 3166-1 alpha 3'; comment on column pays.code_num is 'code ISO 3166-1 numérique. Identique à la division statistique des Nations Unies UN M.49'; create index pays_nom on pays using btree (pays asc nulls last); alter table pays add check (code2 ~ '^[A-Z]{2}$'); alter table pays add check (code3 ~ '^[A-Z]{3}$'); alter table pays add check (code_num ~ '^[0-9]{3}$'); create unique index pays_pk on pays using btree (code2); alter table pays add primary key using index pays_pk; \copy pays (code2, code3, code_num, pays, drapeau_unicode, forme_longue) from '/tmp/geo/pays.csv' (FORMAT CSV, header, delimiter ',', ENCODING 'UTF8'); -- Noms des pays en anglais et espagnol create temporary table pays_tmp ( nom text, code_num text, code3 text ); \copy pays_tmp FROM '/tmp/geo/pays_es.txt' (FORMAT CSV, delimiter E'\t', ENCODING 'UTF8'); update pays set nom_spa = (select t.nom from pays_tmp t where pays.code3 = t.code3); truncate table pays_tmp; \copy pays_tmp FROM '/tmp/geo/pays_en.txt' (FORMAT CSV, delimiter E'\t', ENCODING 'UTF8'); update pays set nom_eng = (select t.nom from pays_tmp t where pays.code3 = t.code3); update pays set nom_eng = 'Taiwan' where code2 = 'TW'; drop table pays_tmp; -- ---------------------------------------------------------------------- -- ---------------------------------------------------------------------- -- langues -- ---------------------------------------------------------------------- create table langues ( code3 char(3) not null, langue text default null, francais text default null ); comment on table langues is 'ISO 639-3'; alter table langues add check (code3 ~ '^[a-z]{3}$'); create unique index langues_pk on langues using btree (code3); alter table langues add primary key using index langues_pk; create table pays_langues ( pays_code char(2) not null, langue_code char(3) not null, officiel boolean default false, pourcentage decimal(4,1) not null DEFAULT '0.0' ); alter table pays_langues add check (pays_code ~ '^[A-Z]{2}$'); alter table pays_langues add check (langue_code ~ '^[a-z]{3}$'); alter table pays_langues add primary key (pays_code, langue_code); \copy langues from '/tmp/geo/langues.csv' (FORMAT CSV, header, delimiter ',', ENCODING 'UTF8'); \copy pays_langues from '/tmp/geo/langues_pays.csv' (FORMAT CSV, header, delimiter ',', ENCODING 'UTF8'); -- ---------------------------------------------------------------------- -- devises -- ---------------------------------------------------------------------- create table devises ( devise_code text not null, num4217 integer default null, symbole character varying(5) default null, nom text default null, format text default null, division integer default 0, minor text default null, minors text default null ); alter table devises add check (devise_code ~ '^[A-Z]{3}$'); create table pays_devises ( pays_code text not null, devise_code text not null, valide daterange default null ); alter table pays_devises add check (pays_code ~ '^[A-Z]{2}$'); alter table pays_devises add check (devise_code ~ '^[A-Z]{3}$'); create unique index devises_pk on devises using btree (devise_code); alter table devises add primary key using index devises_pk; \copy devises from '/tmp/geo/devises.csv' (FORMAT CSV, header, delimiter ',', ENCODING 'UTF8'); \copy pays_devises from '/tmp/geo/devises_pays.csv' (FORMAT CSV, header, delimiter ',', ENCODING 'UTF8'); select '=============== GEO' as msg; -- pays_langues -> pays alter table only pays_langues add foreign key (pays_code) references pays (code2) match simple on update no action on delete no action; -- pays_langues -> langues alter table only pays_langues add foreign key (langue_code) references langues (code3) match simple on update no action on delete no action; -- pays_devises -> pays alter table only pays_devises add foreign key (pays_code) references pays (code2); -- pays_devises -> devises alter table only pays_devises add foreign key (devise_code) references devises (devise_code); select '=============== FIN DES CLES ETRANGERES Geo' as msg; -- ---------------------------------------------------------------------- -- ---------------------------------------------------------------------- -- Fournisseurs -- ---------------------------------------------------------------------- create table fournisseur ( id int primary key, fournisseur text not null ); -- ---------------------------------------------------------------------- -- Produits -- ---------------------------------------------------------------------- create table produit ( id bigint primary key, ean13 ext.EAN13 null, nom text not null, marque text null, categorie text null, energie int not null, proteines float4 null, glucides float4 null, sucres float4 null, graisses float4 null, graisses_saturees float4 null, sel float4 null, fibres float4 null, nutriscore int null, additifs int null, additifs_list text[] null, potassium float null, calcium float null, magnesium float null, sodium float null, chlorure float null, sulfate float null, nitrate float null, hydrogenocarbonate float null, silice float null, fluor float null, residu float null, ph float null, vitamin_a float null, vitamin_c float null ); -- Ajouter les commentaires comment on column produit.potassium IS 'K⁺ en mg/L'; comment on column produit.calcium IS 'Ca²⁺ en mg/L'; comment on column produit.magnesium IS 'Mg²⁺ en mg/L'; comment on column produit.sodium IS 'Na⁺ en mg/L'; comment on column produit.chlorure IS 'Cl⁻ en mg/L'; comment on column produit.sulfate IS 'SO₄²⁻ en mg/L'; comment on column produit.nitrate IS 'NO₃⁻ en mg/L'; comment on column produit.hydrogenocarbonate IS 'HCO₃⁻ en mg/L'; comment on column produit.silice IS 'SiO₂ en mg/L'; comment on column produit.fluor IS 'F en mg/L'; create table adherent ( id int primary key, nom text, prenom text, genre smallint, naissance date, codepostal text ); create table famille ( code text primary key, famille text, code_parent text, -- references famille(code) arborescence ext.ltree ); create table article ( code text primary key, article text, famille_code text, -- references famille(code) factpoids boolean, unitevente int, prix decimal, suivistock int ); create table ticket ( id int primary key, date_ticket timestamp, adherent_id int, -- references adherent(id) mode_rglt int ); create table ligne ( id int primary key, ticket_id int, --references ticket (id), article_code text, -- references article (code) prix_unitaire decimal, quantite decimal ); alter table ligne add column total decimal generated always as (prix_unitaire * quantite) stored; create table prix_historique ( id int generated always as identity, article_code text not null, prix_unitaire decimal not null, dates daterange not null ); create table marque ( id int primary key, marque text not null, fournisseur_id int ); create table categorie ( id int primary key, categorie text not null ); create table region ( id int primary key, region text not null ); insert into region values (1, 'Est'), (2, 'Ouest'), (3, 'Nord'), (4, 'Sud'), (5, 'Centre'); CREATE TABLE genres ( genre_id int primary key, genre text ); INSERT INTO genres (genre_id, genre) VALUES (1,'Science-Fiction'), (2,'Fantasy'), (3,'Young adult'), (4,'Bit-lit'), (5,'Policier'), (6,'Romance'), (7,'Espionnage'), (8,'Aventure'), (9,'Fantastique'), (10,'Historique'), (11,'Noir'), (12,'Biographie'), (13,'Cyberpunk'), (14,'Steampunk'); -- ---------------------------------------------------------------------- -- Banque -- ---------------------------------------------------------------------- create table personne ( id bigint generated always as identity, prenom text, nom text, telephone text, ville text ); create table societe ( id bigint generated always as identity, societe text ) insert into societe OVERRIDING SYSTEM VALUE values (1, 'Supérette'), (2, 'Boulangerie Lagarde'), (3, 'Pharmacie Martin'), (4, 'Diminutif'), (5, 'Vélocité'), (6, 'Café du Marché'), (7, 'La Maison Fleurie'), (8, 'Librairie des Tilleuls'), (9, 'MétalTech SARL'), (10, 'BoisDesign'), (11, 'Les Délices du Terroir'), (12, 'VitiVerte'), (13, 'ÉlectroServ'), (14, 'Ateliers du Moulin'), (15, 'Comptexpert'), (16, 'Assur O'' Poil'), (17, 'Banque Régionale du Centre'), (18, 'ImmoVilla'), (19, 'ITLink Solutions'), (20, 'Studio Graphica '), (21, 'Mairie de Batz'), (22, 'Hôtel du rivage'), (23, 'Collège Marie Curie'), (24, 'École primaire des Lilas'), (25, 'Maison de retraite Les Acacias'), (26, 'Cabinet Médical du Parc'), (27, 'Banque de l''Étoile'), (28, 'Pizzeria Geppetto'); CREATE TABLE emplois ( id bigint generated always as identity, id_personne int NOT NULL, id_societe int NOT NULL, dates daterange, temps_travail decimal(5,2) DEFAULT 151,67 CHECK(temps_travail > 0 AND temps_travail <= 400), salaire_mensuel decimal(10,2) NOT NULL, poste text, FOREIGN KEY (id_personne) REFERENCES personnes(id_personne), FOREIGN KEY (id_societe) REFERENCES societe(id) ); insert into emplois values CREATE TABLE account ( id bigint generated always as identity, account_number TEXT UNIQUE NOT NULL, balance NUMERIC(18,2) NOT NULL DEFAULT 0, currency CHAR(3) NOT NULL, created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now() ); CREATE TABLE "transaction" ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), reference TEXT, amount NUMERIC(18,2) NOT NULL, currency CHAR(3) NOT NULL, from_account BIGINT NOT NULL REFERENCES account(id), to_account BIGINT NOT NULL REFERENCES account(id), created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), processed BOOLEAN NOT NULL DEFAULT FALSE -- indique si ledger + soldes ont été appliqués ); -- ledger (écritures comptables immuables) : append-only CREATE TABLE ledger_entry ( id bigint generated always as identity, transaction_id UUID NOT NULL REFERENCES "transaction"(id), account_id BIGINT NOT NULL REFERENCES account(id), amount NUMERIC(18,2) NOT NULL, -- convention: positif = crédit, négatif = débit (ici from = -amount, to = +amount) created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), description TEXT ); -- index pour performance et idempotence par transaction/account CREATE UNIQUE INDEX ux_ledger_tx_account ON ledger_entry(transaction_id, account_id); -- outbox pour publisher reliable (pattern outbox) CREATE TABLE outbox_event ( id bigint generated always as identity, occurrenced_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), topic TEXT NOT NULL, payload JSONB NOT NULL, transaction_id UUID, -- lien optionnel processed BOOLEAN NOT NULL DEFAULT FALSE, processed_at TIMESTAMP WITH TIME ZONE NULL ); -- table very simple de blockchain / chain d'audit CREATE TABLE block_chain ( id bigint generated always as identity, block_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), tx_id UUID NOT NULL, -- transaction incluse dans ce bloc (ou multiple selon choix) previous_hash TEXT NULL, block_hash TEXT NOT NULL, block_data JSONB NOT NULL -- stockage lisible des éléments du bloc (pour audit) ); CREATE INDEX idx_block_chain_txid ON block_chain(tx_id);