Fonctions SQL

Sur les différents SGBD, il y a une multitude de fonctions :

« Enseigner, ce n’est pas remplir un vase, c’est allumer un feu » Une phrase qui ne date pas d’hier puisqu’elle est de Montaigne …

L’objectif n’est pas de les voir toutes mais d’en utiliser certaines (si possible les plus pertinentes), pour voir les différences entre SGBD et en retenir quelques une.

fonction sur les dates

SELECT CURDATE();     --  fonction sur MYSQL (juste la date)
SELECT GETDATE();     -- fonction transactSQL de SQLSERVER
SELECT SYSDATE();     -- fonction sur ORACLE

SELECT CURRENT_TIMESTAMP;   -- norme SQL  : fonction "système" pas de  paramètres 
-- (fonctionne sur MYSQL et PostGreSQL , mais pas sur ORACLE et SQLSERVER)

SELECT NOW();    --  fonction sur MYSQL (date+heure)
  1. Quelles sont les fonctions pour obtenir la date d’aujourd’hui sur les principaux SGBDR ?

  2. Indiquer si ces fonctions tiennent compte du fuseau horaire et le format de la date retournée.

DROP TABLE IF EXISTS telephone;
CREATE TABLE telephone 
(
idTelephone       INTEGER AUTO_INCREMENT  
, type_id         CHAR(2)
, marque_id       INTEGER 
, date_achat   DATE
, prix         DECIMAL (9,2)       /* NUMBER(9,2) sur ORACLE */
, proprietaire_id INTEGER
, couleur       VARCHAR(25)
, PRIMARY KEY(idTelephone)
);

INSERT INTO telephone (idTelephone,type_id,marque_id,date_achat,prix,proprietaire_id,couleur) VALUES (1,'SP' ,1,STR_TO_DATE('15/01/2020', '%d/%m/%Y'),139.99,190120,'ROUGE');
-- il est déconseillé de mettre une valeur à une clé primaire => pour vérification
INSERT INTO telephone (idTelephone,type_id,marque_id,date_achat,prix,proprietaire_id) VALUES (NULL,'SP' ,2,STR_TO_DATE('14/03/2020', '%d/%m/%Y'), 99.99,190215);
INSERT INTO telephone
(idTelephone,type_id,marque_id,date_achat,prix,proprietaire_id,couleur) VALUES
(NULL,'CL' ,3,STR_TO_DATE('02/05/2020', '%d/%m/%Y'), 49.11,190001,'NOIR');
INSERT INTO telephone
(idTelephone,type_id,marque_id,date_achat,prix,proprietaire_id,couleur) VALUES
(NULL,'CO' ,4,STR_TO_DATE('25/07/2020', '%d/%m/%Y'), 89.14,190222,'BLANC');
INSERT INTO telephone
(idTelephone,type_id,marque_id,date_achat,prix,proprietaire_id)         VALUES
(NULL,'IP' ,5,STR_TO_DATE('30/09/2020', '%d/%m/%Y'),359.49,190561);
INSERT INTO telephone
(idTelephone,type_id,marque_id,date_achat,prix,proprietaire_id,couleur) VALUES
(NULL,'CO' ,5,STR_TO_DATE('01/01/2021', '%d/%m/%Y'), 99.51,122120,'BLANC'),
(NULL,'SP' ,1,'2013-01-15',189,190622,'ROUGE'),
(NULL,NULL ,NULL,'2013-01-15',20,190623,'ROUGE'),
(NULL,NULL ,1,'2013-01-15',NULL,NULL,NULL);

exemple :

-- MYSQL
SELECT date_achat, 
    YEAR(date_achat) as Annee,
    DATEDIFF(date_achat, CURDATE()) as "Difference" ,
    DATE_ADD(date_achat, INTERVAL 30 DAY) as Plus30j ,
    DATE_SUB(date_achat, INTERVAL 30 DAY) as Moins30j ,
    DATE_ADD(date_achat, INTERVAL -30 DAY) as Moins30j2    
FROM telephone;

-- SQLSERVER
SELECT *, 
    YEAR(date_achat) as Annee,
    DATEDIFF(DAY, date_achat, DateDemandee) as [Difference],
    DATEADD(DAY, 30, date_achat) as Plus30j,
    DATEADD(DAY, -30, date_achat) as Moins30j
FROM telephone;

-- ORACLE
SELECT *, 
    EXTRACT(YEAR FROM date_achat) as Annee,
    DateDemandee - date_achat as "Difference",
    date_achat + 30 as Plus30j,
    date_achat - 30 as Moins30j
FROM telephone;

-- ADD_MONTHS(date_achat, 2)

exemple de configuration de la “time_zone”

documentation datetime timestamp - limite

DROP TABLE IF EXISTS posts;
CREATE TABLE posts (
id_post  INT  AUTO_INCREMENT
, title VARCHAR(255) NOT NULL
, content LONGTEXT
, online VARCHAR(255) DEFAULT 0
, dt_created_at DATETIME  NOT NULL DEFAULT CURRENT_TIMESTAMP
, dt_updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
, ts_created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
, ts_updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
, PRIMARY KEY (id_post)
);

Dans la documentation, beaucoup de synonymes :
https://dev.mysql.com/doc/refman/8.0/en/timestamp-initialization.html

INSERT INTO posts(title, online) VALUES('titre post1', 0);
INSERT INTO posts(title, online) VALUES('titre post2', 1);
INSERT INTO posts SET title='titre post3' , online=2;
INSERT INTO posts VALUES
(NULL, 'titre post4', NULL, 0, '2023-01-01 12:00:00', '2023-01-01 12:00:00', '2023-01-01 12:00:00', '2023-01-01 12:00:00');
SELECT * FROM posts;
UPDATE posts SET title='titre post1 new' WHERE id_post=1;
SELECT * FROM posts;

ATTENTION à la configuration (localisation) du serveur

SELECT @@global.time_zone, @@session.time_zone;

SET time_zone = '+03:00';

SELECT @@global.time_zone, @@session.time_zone;

INSERT INTO posts(title, online) VALUES('titre post5', 0);
INSERT INTO posts(title, online) VALUES('titre post6', 1);
SELECT * FROM posts;

timestamp nombre de seconde depuis 1970

différence timezone timestamp(=> sauvegarde au format UTC)


date au format UTC (temps universel) : UTC_TIMESTAMP() n’est pas affecté par les fuseaux horaires locaux, ce qui le rend utile pour les scénarios dans lesquels une mesure cohérente du temps est requise sur différents emplacements.

SELECT UTC_TIMESTAMP,UTC_TIMESTAMP();
SELECT UTC_TIMESTAMP()+0;

Pour connaitre la configuation du serveur

SELECT @@global.time_zone, @@session.time_zone;

SELECT TIMEDIFF(NOW(), CONVERT_TZ(NOW(),  @@session.time_zone, '+00:00'));

Documentation UTC :

https://fr.wikipedia.org/wiki/Temps_universel_coordonn%C3%A9 : calcul du temps UTC

sur pythonanywhere (AWS en angleterre)
INSERT INTO posts VALUES(NULL, 'titre post7', NULL, 0, NOW(), NOW(), NOW(), NOW() );
INSERT INTO posts VALUES(NULL, 'titre post8', NULL,1, CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP() , CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP());
INSERT INTO posts VALUES(NULL, 'titre post9', NULL, 0, '2023-03-29 12:00:00', '2023-03-29 12:00:00', '2023-03-29 12:00:00', '2023-03-29 12:00:00');

SELECT * FROM posts;


Il existe beaucoup de fonctions : exemple de fonctions

fonction sur les chaînes de caractères

Exemple : Avec une table et un champ composé du nom et du prénom (pas d’espace dans le nom) récupérer le nom et le prénom des personnes

-- MYSQL
SELECT libelle,
    INSTR(libelle, ' '),
    LEFT(libelle, INSTR(libelle, ' ')- 1),
    SUBSTRING(libelle, INSTR(libelle, ' ')+ 1, 100),
    LTRIM(SUBSTRING(libelle, INSTR(libelle, ' '), 100))
FROM marque_tel;

-- SQL SERVER
SELECT libelle,
    CHARINDEX(' ', libelle),
    LEFT(libelle, CHARINDEX(' ', libelle) - 1),
    SUBSTRING(libelle, CHARINDEX(' ', libelle) + 1, 100),
    LTRIM(SUBSTRING(libelle, CHARINDEX(' ', libelle), 100))
FROM marque_tel;

-- ORACLE
SELECT libelle,
    INSTR(libelle, ' '),
    SUBSTR(libelle, 1, INSTR(libelle, ' ')- 1),
    SUBSTR(libelle, INSTR(libelle, ' ')+ 1, 100),
    TRIM(SUBSTR(libelle, INSTR(libelle, ' '), 100))
FROM marque_tel;

fonctions sur les numériques

-- pour SQL Server et MySQL
SELECT 
    idTelephone
    , idTelephone / 2   -- attention au trans-typage CAST
    , prix
    , COALESCE(prix, 0) * 2
    , CEILING(prix)          -- CEIL sur ORACLE
    , FLOOR(prix)
    , ROUND(prix, 1)
FROM telephone;

SELECT avancé

efficacité de la clause WHERE

IN et BETWEEN

IF

SELECT tel.prix AS PRIX
       , IF(tel.prix <99 ,"premier Prix", IF(tel.prix<259,"prix medium","prix élevé"))
FROM telephone AS tel
WHERE tel.prix IS NOT NULL;

CASE

SELECT tel.date_achat AS ACHAT
       , tel.type_id AS type
       , tel.marque_id AS MARQUE
       , marq.libelle AS LIBELLE
       , CASE tel.marque_id
            WHEN 1 THEN 'MEILLEURS VENTES'
            WHEN 5 THEN 'Très bonnes ventes'
            ELSE 'AUTRES ventes'
        END AS type_de_vente
FROM telephone tel        
INNER JOIN  marque_tel AS marq
  ON tel.marque_id = marq.idMarque ;
SELECT tel.DATE_ACHAT AS dateAchat
       , type.libelle AS type
       , marq.libelle AS marque
       , tel.PRIX AS prix
       , CASE
            WHEN tel.PRIX <= 99 THEN 'premier Prix'
            WHEN tel.PRIX >= 300 THEN 'Prix élevé'
            WHEN tel.PRIX > 49 AND tel.PRIX < 300 THEN 'PRIX MOYEN'
        END AS type_PRIX  
FROM telephone AS tel
INNER JOIN type_tel AS type
  ON tel.type_id   = type.idType
INNER JOIN  marque_tel AS marq
  ON tel.marque_id = marq.idMarque;

EXERCICE

exo 1 démonstration : données géolocalisées

– vente d’article (style le bon-coin)

DROP TABLE IF EXISTS offre;
CREATE TABLE offre (
id_offre  INT  AUTO_INCREMENT
, article_offre VARCHAR(255)
, ville VARCHAR(255)
, location POINT
, PRIMARY KEY (id_offre)
);
INSERT INTO offre VALUES (null, 'article1', 'Belfort, Fr', ST_GEOMFROMTEXT('POINT(6.862100 47.638770)'));
INSERT INTO offre VALUES (null, 'article2', 'Besancon, Fr', ST_GEOMFROMTEXT('POINT(6.025530 47.241268)'));
INSERT INTO offre VALUES (null, 'article3', 'Poitiers', ST_GeomFromText('POINT(0.340196 46.580260)'));
INSERT INTO offre VALUES (null, 'article4', 'Montpellier', ST_GeomFromText('POINT(3.876734 43.611242)'));

SELECT *
FROM offre;

doc mariadb - doc mysql - doc postgre
ST_DISTANCE_SPHERE(g1,g2,[r])
par défaut le radius est celui de la terre

SELECT ST_DISTANCE_SPHERE( ST_GEOMFROMTEXT('POINT(6.862100 47.638770)'),offre.location )
, CONCAT('distance entre BELFORT et ',offre.ville)
FROM offre;

SELECT
ROUND( ST_DISTANCE_SPHERE( ST_GEOMFROMTEXT('POINT(6.862100 47.638770)'),offre.location )/1000)
, CONCAT('distance entre BELFORT et ',offre.ville)
FROM offre;
SET @distance_max=100;
SET @ma_localisation=ST_GEOMFROMTEXT('POINT(6.862100 47.638770)');

SELECT
ROUND( ST_DISTANCE_SPHERE( @ma_localisation,offre.location )/1000)
, CONCAT('distance entre BELFORT et ',offre.ville)
FROM offre;

SELECT *
FROM offre
WHERE ROUND( ST_DISTANCE_SPHERE( @ma_localisation ,offre.location )/1000) < @distance_max;

possibilité de modéliser un polygon https://www.data.gouv.fr/fr/datasets/contours-des-departements-francais-issus-d-openstreetmap/

exo 2 démonstration : Recherche fulltext

https://dev.mysql.com/doc/refman/8.0/en/fulltext-search.html
MATCH (col1,col2,...) AGAINST (expr [search_modifier])

DROP TABLE IF EXISTS article;
CREATE TABLE article (
id_article  INT  AUTO_INCREMENT
, nom VARCHAR(255)
, description_courte TEXT          -- 65,535 characters
, description_longue MEDIUMTEXT    --  16,777,215 characters 
, PRIMARY KEY (id_article)
, FULLTEXT ft_nom_desc_c_desc_long (nom, description_courte, description_longue)
);


INSERT INTO article VALUES (null, 'article1'
, 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. '
, 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
proident, sunt in culpa qui officia deserunt mollit anim id est laborum.');

INSERT INTO article VALUES (null, 'article2 but'
, 'Lorem ipsum dolor sit amet, but consectetur adipisicing elit, sed do eiusmod
tempor incididunt ut labore et dolore but magna aliqua. but 3'
, 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
tempor incididunt ut labore et but  dolore magna aliqua. Ut enim ad minim veniam,
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
consequat. but Duis aute irure dolor in reprehenderit in but voluptate velit esse
cillum dolore eu fugiat nulla pariatur. but Excepteur sint occaecat cupidatat non
proident, sunt in culpa qui officia deserunt mollit anim id est laborum. but 5');

INSERT INTO article VALUES (null, 'article1 info'
, 'Lorem ipsum dolor info sit amet, consectetur adipisicing elit, sed do eiusmod
tempor incididunt ut labore et info  dolore magna aliqua. info 3'
, 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
tempor incididunt ut info labore et dolore magna aliqua. Ut enim ad minim veniam,
quis nostrud exercitation ullamco laboris nisi info ut aliquip ex ea commodo
consequat. Duis aute irure dolor info in reprehenderit in voluptate velit esse
cillum dolore eu fugiat nulla pariatur. info Excepteur sint occaecat cupidatat non
proident, sunt in culpa qui officia deserunt mollit anim id est laborum. info 5');

INSERT INTO article VALUES (null, 'article1 but info'
, 'Lorem ipsum dolor but info sit amet, consectetur adipisicing elit, but info sed do eiusmod
tempor incididunt ut labore but info et dolore magna aliqua. '
, 'Lorem ipsum dolor but info sit amet, consectetur adipisicing elit, sed do eiusmod
tempor incididunt ut but info labore et dolore magna aliqua. Ut enim ad minim veniam,
quis nostrud exercitation ullamco but info laboris nisi ut aliquip ex ea commodo
consequat. Duis aute irure dolor in but info reprehenderit in voluptate velit esse
cillum dolore eu fugiat nulla pariatur. but info Excepteur sint occaecat cupidatat non
proident, sunt in culpa qui officia deserunt mollit anim id est laborum. but info 5');
SELECT *
FROM article;

SHOW CREATE TABLE article; 


ALTER TABLE article ADD FULLTEXT ft_nom (nom);
ALTER TABLE article ADD FULLTEXT ft_description_courte (description_courte);
ALTER TABLE article ADD FULLTEXT ft_description_longue (description_longue);

SHOW CREATE TABLE article; 

SELECT
nom,
MATCH(nom) AGAINST('info')  AS  score_nom,
MATCH(description_courte) AGAINST('info') AS score_description_courte,
MATCH(description_longue) AGAINST('info') AS score_description_longue
FROM article;


SELECT
nom,
MATCH(nom) AGAINST('but info')  AS  score_nom,
MATCH(description_courte) AGAINST('but info') AS score_description_courte,
MATCH(description_longue) AGAINST('but info') AS score_description_longue
FROM article;


SET @recherche_texte='but info';
SELECT
nom,
MATCH(nom) AGAINST(@recherche_texte)  AS  score_nom ,
MATCH(description_courte) AGAINST(@recherche_texte) AS score_description_courte ,
MATCH(description_longue) AGAINST(@recherche_texte) AS score_description_longue , 
MATCH(nom) AGAINST(@recherche_texte) * 1
+ MATCH(description_courte) AGAINST(@recherche_texte) * 0.5
+ MATCH(description_longue) AGAINST(@recherche_texte) * 0.1 AS calcul_score
FROM article
WHERE
MATCH(nom) AGAINST(@recherche_texte) OR
MATCH(description_courte) AGAINST(@recherche_texte) OR
MATCH(description_longue) AGAINST(@recherche_texte)
ORDER BY (score_nom+score_description_courte*0.5+score_description_longue*0.1) DESC;

plusieurs modes

SELECT nom 
FROM article 
WHERE 
MATCH (description_courte)
AGAINST ('+info -but' IN BOOLEAN MODE);

SELECT nom , description_courte
FROM article 
WHERE 
MATCH (description_courte)
AGAINST ('inf*' IN BOOLEAN MODE);
SELECT nom , description_courte
FROM article 
WHERE 
MATCH (description_courte)
AGAINST ('info' WITH QUERY EXPANSION);

expansion aveugle des requêtes : Cela fonctionne en effectuant la recherche deux fois, où la phrase de recherche pour la deuxième recherche est la phrase de recherche d’origine concaténée avec les quelques documents les plus pertinents de la première recherche.

exo 3 :

+-------------+-------------------+------------+--------+------------+-------------------+
| idTelephone | libelle           | libelle    | prix   | date_achat | date_fin_garantie |
+-------------+-------------------+------------+--------+------------+-------------------+
|           7 | société SAMSUNG   | Smartphone | 189.00 | 2013-01-15 | 2014-01-15        |
|           1 | société SAMSUNG   | Smartphone | 139.99 | 2020-01-15 | 2021-01-15        |
|           2 | entreprise SONY   | Smartphone |  99.99 | 2020-03-14 | 2021-03-14        |
|           3 | groupe PHILIPS    | clapet     |  49.11 | 2020-05-02 | 2021-05-02        |
|           4 | marque MOTOROLA   | COULISSANT |  89.14 | 2020-07-25 | 2021-07-25        |
|           5 | SOCIETE APPLE     | IPHONE     | 359.49 | 2020-09-30 | 2021-09-30        |
+-------------+-------------------+------------+--------+------------+-------------------+

exo 4 :

+-------------+-------------------+------------+--------+------------+-------------------+
|           7 | société SAMSUNG   | Smartphone | 189.00 | 2013-01-15 | 2015-01-15        |
|           1 | société SAMSUNG   | Smartphone | 139.99 | 2020-01-15 | 2022-01-15        |
|           2 | entreprise SONY   | Smartphone |  99.99 | 2020-03-14 | 2021-03-14        |
|           3 | groupe PHILIPS    | clapet     |  49.11 | 2020-05-02 | 2021-05-02        |
|           4 | marque MOTOROLA   | COULISSANT |  89.14 | 2020-07-25 | 2021-07-25        |
|           5 | SOCIETE APPLE     | IPHONE     | 359.49 | 2020-09-30 | 2023-09-30        |
+-------------+-------------------+------------+--------+------------+-------------------+

exo 5:

+-----------------+-------------------+------------+--------+------------+------------+---------+
| proprietaire_id | libelle           | libelle    | prix   | date_achat | calcul_tmp | remise  |
+-----------------+-------------------+------------+--------+------------+------------+---------+
|          190120 | société SAMSUNG   | Smartphone | 139.99 | 2020-01-15 |       -709 |  9.7993 |
|          190215 | entreprise SONY   | Smartphone |  99.99 | 2020-03-14 |       -767 |  4.9995 |
|          190001 | groupe PHILIPS    | clapet     |  49.11 | 2020-05-02 |       -816 |  2.4555 |
|          190222 | marque MOTOROLA   | COULISSANT |  89.14 | 2020-07-25 |       -900 |  4.4570 |
|          190561 | SOCIETE APPLE     | IPHONE     | 359.49 | 2020-09-30 |       -967 | 35.9490 |
+-----------------+-------------------+------------+--------+------------+------------+---------+



exo 6:(concaténation, casting)

objectif : mettre les dates au format JJ/MM/AAAA “strict”, faire une recherche sur la fonction LPAD

+-----------------+-------------------+------------+--------+---------------+
| proprietaire_id | libelle           | libelle    | prix   | DATE_ACHAT_FR |
+-----------------+-------------------+------------+--------+---------------+
|          190622 | société SAMSUNG   | Smartphone | 189.00 | 15/01/2013    |
|          190120 | société SAMSUNG   | Smartphone | 139.99 | 15/01/2020    |
|          190215 | entreprise SONY   | Smartphone |  99.99 | 14/03/2020    |
|          190001 | groupe PHILIPS    | clapet     |  49.11 | 02/05/2020    |
|          190222 | marque MOTOROLA   | COULISSANT |  89.14 | 25/07/2020    |
|          190561 | SOCIETE APPLE     | IPHONE     | 359.49 | 30/09/2020    |
+-----------------+-------------------+------------+--------+---------------+

ANNEXES

les variables (voir PL/SQL) https://docs.oracle.com/cd/E17952_01/mysql-5.7-en/user-variables.html

SET @t1=1, @t2=2, @t3:=4;
SELECT @t1, @t2, @t3, @t4 := @t1+@t2+@t3;

https://docs.oracle.com/cd/E11882_01/server.112/e16604/ch_twelve050.htm#SQPUG133
* sur sqlserver

https://docs.microsoft.com/fr-fr/sql/t-sql/language-elements/variables-transact-sql?view=sql-server-ver15

https://docs.postgresql.fr/8.1/plpgsql-declarations.html https://www.postgresqltutorial.com/plpgsql-variables/

critiques mysql