Cédric BRASSEUR
mis à jour le 23/12/2024
Data
Toute entreprise a besoin de sauvegarder ses données.
Il existe plusieurs structures de sauvegardes :
{
"premierNoeudTableauObject": [
{
"premierObjet" : {
"nom": "BRASSEUR",
"prenom": "Cédric",
},
},
{
"secondObjet" : {
"nom": "JEAN",
"prenom": "Sébastien",
}
}
],
"secondNoeudSimpleValeur" : 10,
}
Le JSON permet de stocker des données comme on le souhaite, sous forme de tableaux, d'objets ou de simple association clé => valeur.
Il est simple de travailler avec le format JSON :
De même pour écrire du JSON :
<?xml version="1.0" encoding="ISO-8859-1" standalone="yes"?>
<message>
<expediteur>
<identite>
<prenom>Guillaume</prenom>
<nom>Charpentier</nom>
<email>gcharpen@etudiant.univ-mlv.fr</email>
</identite>
</expediteur>
<destinataire>
<identite>
<prenom>Gérard</prenom>
<nom>Dupont</nom>
<email>gerarddupont@provider.com</email>
</identite>
</destinataire>
<message>mon message</message>
</message>
Le XML permet de stocker des données sous forme d'arbre et de nœuds. L'exemple ici représente un message avec les informations de l'expéditeur et du destinataire (ainsi que le contenu)
Le XML est une structure de stockage de données qui peut être géré en PHP, pour la création, la lecture ou la modification d'un fichier XML depuis PHP.
Réalisez une opération d'écriture et une opération de lecture d'un fichier XML ou JSON, à votre convenance. Avec quelques données de tests, pas la peine de faire une grosse structure.
De même, vous pouvez choisir la technologie.
Vous avez 20 minutes, ce sera normalement largement suffisant.
Envoyez moi les exercices tout au long de la formation, j'utiliserai ces éléments pour la notation.
Structure permettant d'agencer et de sauvegarder des données relatives à un ou plusieurs projets
Cette structure peut être relationnelle ou non (SGBD =/= SGBDR)
Les données doivent être protégées mais accessibles en continue
Nous allons utiliser SQL Server (T-SQL).
BDD
Base de données
Table
Table
Champ 1 | Champ2 | Champ2 | |
---|---|---|---|
Enregistrement 1 | data_c1_e1 | data_c2_e1 | data_c3_e1 |
Enregistrement 2 | data_c1_e2 | data_c2_e2 | data_c3_e2 |
Enregistrement 3 | data_c1_e3 | data_c3_e3 | data_c3_e3 |
Champ 1 | Champ2 | Champ2 | |
---|---|---|---|
Enregistrement 1 | data_c1_e1 | data_c2_e1 | data_c3_e1 |
Enregistrement 2 | data_c1_e2 | data_c2_e2 | data_c3_e2 |
Enregistrement 3 | data_c1_e3 | data_c3_e3 | data_c3_e3 |
Les données sont liées les unes aux autres entre les tables.
Ceci permettant d'avoir des données atomiques dans les tables tout en pouvant lier ses données grâce à des jointures.
Auteur |
---|
id |
... |
Articles |
---|
id |
auteur_id |
date_parution_id |
Date |
---|
id |
jour_nom |
Pour modéliser correctement sa base de données il est fortement conseillé d'utiliser une méthode de modélisation.
! MERISE !
Ce n'est pas l'objet du cours, mais n'hésitez pas à demander des explications si ce n'est pas connu avant la formation
Object Relational Mapping : Permet de traduire les tables et relations en objets directement, ou traduire des objets et leurs liens en tables et relations.
Souvent très pratique pour le gain de temps qu'il procure.
Mais peut parfois s'avérer inadapté à certains cas d'utilisation complexe des données.
Exemple live avec Entity (.Net)
Réalisez un ORM pour réaliser des opérations CRUD sur un système :
L'objectif est de créer 3 objets :
Puis réaliser des opérations de CRUD sur ces différents objets depuis votre Program.cs. Si besoin, vous pouvez vous aider de ce tuto : https://softchris.github.io/pages/dotnet-orm.html#create-the-database (A adapter quand même un peu pour que ça fonctionne !)
Envoyez moi les exercices tout au long de la formation, j'utiliserai ces éléments pour la notation.
Le NoSQL est de plus en plus utilisé pour répondre aux enjeux du BigData (Utilisation de données massive et diverses, en continu à travers le monde : réseaux sociaux, pubs ciblées,...)
Le fonctionnement est différent d'un SGBDR, les données ne respectent plus un schéma particulier et elles sont stockées sous forme de document (ou graphes).
Ceci ayant plusieurs avantages par rapport à un SGBDR :
Plusieurs axes sont à prendre en compte afin de bien choisir la structure de données adaptée à votre besoin :
Eric A. Brewer (2000) a formalisé un théorème reposant sur 3 propriétés des bases de données (relationnelles, NoSQL et autres) :
Le théorème de CAP :
Dans toute base de données, vous ne pouvez respecter au plus que 2 propriétés parmi la cohérence, la disponibilité et la distribution.
D'abord, il faut installer l'agent SQL, puis l'interface Mangement studio.
Ou, pour une installation plus rapide, voir ici :
Agent SQL : https://www.microsoft.com/fr-fr/sql-server/sql-server-downloads (choisir SQL Server 2022 Express)
Management studio Express :
Pour MacOS :
Vous pouvez utiliser Azure Data Studio
Nous allons utiliser une base proposée par Microsoft pour avoir une base contenant quelques données et respectant les conventions SQL.
Voici le lien de téléchargement : https://docs.microsoft.com/fr-fr/sql/samples/adventureworks-install-configure?view=sql-server-ver15&tabs=ssms
Envoyer le script de création de la base de données AnimalDatabase (!! MySQL !!) que l'on utilisera plus tard dans la formation.
(également pour l'évaluation)
On fera ça plus tard...
Nous allons voir ensemble plusieurs opérations possibles sur une table.
C
R
U
D
reate : Création de nouveaux éléments dans la BDD
ead : Lecture d'éléments dans la BDD
pdate : Modification dans la BDD
elete : Suppression dans la BDD
Comme vu avec Merise, nous pouvons créer une base de données (via le script de l'outil, ou soit-même)
CREATE DATABASE DatabaseName
IF NOT EXISTS (select * from sys.databases where name = 'Test’)
BEGIN
CREATE DATABASE Test
SELECT 'Database succesfully created’ AS ReturnMsg
END
ELSE
SELECT 'Database already exists' AS ReturnMsg
Création d'une base de données sans vérification d'existance
Création avec vérification d'existance (SQL Server)
Création avec vérification d'existance (MySQL)
CREATE DATABASE IF NOT EXISTS DatabaseName
Nous pouvons également créer une table
CREATE TABLE auteurs (
id int PRIMARY KEY IDENTITY(1,1), -- On reverra après la notion de clés
nom varchar(50) NOT NULL,
prenom varchar(50) NOT NULL,
date_naissance DATE
)
Table auteur
Auteur |
---|
id |
nom |
prenom |
date_naissance |
Exemple : Employee (ou emp_employee)
Attention, le trigramme est plus utilisé pour regrouper par sous ensemble de tables (par exemples les tables liées à la commande commenceront par cmd_XXX)
Exemple : emp_id
Aujourd'hui, je ne respecte plus cette norme de nommage, sauf pour les id. Mais ça reste la norme actuelle.
(Moi, je mets name par exemple et pas aut_name)
Voici un ensemble de types numériques à connaître :
Un des type les plus impactant, car souvent le moins bien géré, est le type numérique.
Voici les informations qu'il faut savoir (pas au chiffre près) pour ces types :
Il y a plusieurs choix pour gérer les nombres à virgules, mais ils ont un objectif différent :
Mais en soit, pour l'instant, si on veut un type avec virgule, on va mettre du decimal(7,2) disant qu'on aura 7 chiffres en tout, DONT 2 derrière la virgule (précision)
Voici un ensemble de types de dates à connaître :
Voici un ensemble de types de textes à connaître :
Tips pour les dates (format) :
https://www.mssqltips.com/sqlservertip/1145/date-and-time-conversions-using-sql-server/
Exemple conversions
CONVERT(VARCHAR, dd.DayNumberOfMonth); -- On converti une date en chaîne
CONVERT(DATETIME,'04/28/2013'); -- On converti une chaîne en date
CONVERT(VARCHAR,fis.OrderDate, 101); -- On converti une date en chaîne, avec un format (101)
CAST(fis.OrderDate AS varchar); -- On cast (brutal) une date en chaîne
Pour sélectionner des données, nous utiliserons le SELECT, qui contient plusieurs options que l'on va aborder au fur et à mesure.
SELECT *
FROM Pays
SELECT p_name, p_size
FROM Pays
SELECT TOP 10 *
FROM Pays
SELECT DISTINCT *
FROM Pays
SELECT *
FROM Pays
WHERE p_name = 'France'
Exemples simples
SELECT *
FROM table
WHERE condition
GROUP BY expression
HAVING condition { UNION | INTERSECT | EXCEPT }
ORDER BY expression
LIMIT count
OFFSET start
Les différentes options
Pour sélectionner des données, nous utiliserons le SELECT, qui contient plusieurs options que l'on va aborder au fur et à mesure.
SELECT emp_lastname, CONVERT(DECIMAL(7,2), AVG(emp_salary)) AS AverageSalary
FROM Employee
WHERE emp_salary >= 25000 -- Filtre des données principales
GROUP BY emp_lastname -- Regroupement des données par emp_lastname
HAVING CONVERT(DECIMAL(7,2), AVG(emp_salary)) >= 31000 -- Filtre après aggrégation
ORDER BY AverageSalary -- Tri des données, par défaut, c'est ASC (ascendant)
Exemples simples
Pour l'insertion de données, l'opération SQL concernée est l'INSERT. Il y a plusieurs moyens de réaliser des insertions.
-- Insertion classique
INSERT INTO Pays (p_nom, p_size)
VALUES ('Italie', 200000)
-- Insertion avec colonne implicite
INSERT INTO Pays VALUES ('France', 643801)
-- Insertion multiple
INSERT INTO Pays (p_nom, p_size)
VALUES ('Italie', 200000), ('Allemagne', 400000), ('Belgique', 150000)
Exemples simples
Pour modifier des données on utilise l'opération UPDATE. Pensez à ne jamais oublier de mettre une condition avec un WHERE pour ne pas modifier toute la table.
-- Update classique
INSERT INTO Pays (p_nom, p_size)
VALUES ('Italie', 200000)
-- NE PAS OUBLIER DE METTRE UN WHERE !!!!
UPDATE Pays
SET p_size = 500000
WHERE p_nom = 'Italie'
-- Modifier plusieurs valeurs
UPDATE Pays
SET p_size = 500000, p_nom = 'Pays-Bas'
WHERE p_nom = 'Italie'
Exemples simples
Afin de supprimer des données, on utilise l'opération DELETE, pensez également à mettre un WHERE pour éviter de supprimer toutes les données.
-- Delete classique
DELETE FROM Pays
-- OU
TRUNCATE TABLE Pays
-- NE PAS OUBLIER DE METTRE UN WHERE !!!!
DELETE FROM Pays
WHERE p_nom = 'Pays-Bas'
Exemples simples
Création de base de données, création de table, insertion de données, modification de données puis suppression de données. (SIMPLE)
La clé primaire étant le champ qui permet de rendre unique chaque champ d'une table, la clé étrangère est la référence à cette clé dans une autre table. Prenons l'exemple de la table article, on souhaite lier un article à un auteur (nom, prénom, date de naissance).
CREATE TABLE auteurs (
id int PRIMARY KEY IDENTITY(1,1),
nom VARCHAR(50) NOT NULL,
prenom VARCHAR(50) NOT NULL,
date_naissance DATE
)
ALTER TABLE articles
ADD auteur_id INT;
ALTER TABLE articles ADD CONSTRAINT FOREIGN KEY (auteur_id)
REFERENCES auteur(id);
Table auteur
Clé étrangère ajoutée à articles
Auteur |
---|
id |
... |
Articles |
---|
id |
auteur_id |
... |
Nous pouvons ajouter des clés étrangères à la création de la table (lors du CREATE TABLE) ou après la création de la table (ALTER TABLE)
Clé étrangère ajoutée à articles
Auteur |
---|
id |
... |
Articles |
---|
id |
auteur_id |
... |
CREATE TABLE Article (
id int PRIMARY KEY IDENTITY(1,1),
auteur_id INT NOT NULL
CONSTRAINT FK_Article_Auteur FOREIGN KEY (auteur_id)
REFERENCES Auteur (id)
)
Un lien de clé étrangère est TOUJOURS vers une clé primaire d'une autre table
Création de base de données avec relations, création de tables avec clé primaires / étrangères, insertion de données.
Requêtes de sélections simples
Pour concaténer deux champs dans un seul, regardez l'opérateur CONCAT
Une jointure permet de lier les données d'une table à une autre grâce à la relation de clé primaire / clé étrangère.
Nous allons voir qu'une seule jointure, mais si ça vous intéresse on peut prendre le temps de voir les deux autres les plus courantes.
Jointure interne (présence de clé étrangère obligatoire pour avoir un retour (NOT NULL))
SELECT *
FROM articles ar
INNER JOIN auteurs au ON ar.auteur_id = au.id
Une jointure fonctionne TOUJOURS via clé étrangère vers clé primaire dans la condition ON (dans un sens ou l'autre, c'est une égalité...)
Un autre exemple sur la base de travail des exercices précédents.
En général, on utilise un alias pour différencier le champ id et simplifier la syntaxe :
-- Attention, il y a deux champs nommé id, un dans Employee, un dans Department
-- Donc on ne peut plus utiliser *
SELECT
Employee.firstname,
Employee.lastname,
Department.name,
Department.location
FROM Employee
INNER JOIN Department ON Employee.dep_id = Department.id
SELECT
e.id AS EmployeeId,
d.id AS DepartmentId,
e.firstname,
e.lastname,
d.name,
d.location
FROM Employee e
INNER JOIN Department d ON e.dep_id = d.id
Petite explication schématique du résultat de la requête précédente...
SELECT CONCAT(emp_firstname, ' ', emp_lastname) AS FullName,
emp_salary AS Salary,
(
SELECT MAX(emp_salary) FROM Employee
) AS MaxSalary -- On entoure la sous requête entre parenthèse pour en utiliser le résultat
FROM Employee
Il est possible de faire une requête dans une requête, c'est ce que l'on appelle une requête imbriquée.
Il en existe deux types qui ont chacun leur objectif.
Ici, on a une requête de type 1 que l'on voit plus en détails dans la slide suivante.
C'est aussi possible de faire une requête imbriquée dans une jointure (à des fins d'optimisation). Mais on ne verra pas ça tout de suite...
SELECT
firstname,
salary,
(
SELECT MAX(salary)
FROM Employee
) AS MaxSalary
FROM Employee
Type 1 est un abus de langage que j'utilise pour expliquer les deux possibilités de requêtes imbriquées, ça n'est pas une norme de nommage des requête imbriquée.
La requête imbriquée de type 1 a pour objectif d'afficher la même données pour tous les enregistrement de la requête parente.
Elle s'utilise donc dans un champ et ne doit retourner qu'un seul résultat ! (Utilisation d'agrégation, comme MAX() ou TOP 1)
SELECT COUNT(*) AS NumberOfFamilies FROM
(
SELECT lastname, AVG(Salary) AS AverageSalary
FROM Employee
GROUP BY lastname
) T
Pour la requête de type 2, c'est également un abus de langage...
La requête de type 2, cette fois-ci, c'est une sous requête, un peu comme ci on avait présélectionné un ensemble de données, pour l'utiliser dans une requête parente.
Cette fois, on ne le met pas dans un champ, mais dans le FROM (ou dans le JOIN)
GO
CREATE VIEW V_CustomerProduct
AS
SELECT fis.SalesOrderNumber, dcu.FirstName + ' ' + dcu.LastName AS FullName, fis.OrderDate
FROM FactInternetSales fis
INNER JOIN DimCustomer dcu ON dcu.CustomerKey = fis.CustomerKey
INNER JOIN DimProduct dp ON dp.ProductKey = fis.ProductKey
GO
Une vue est un objet permettant de prendre le contenu d'une requête de base afin d'en prendre le contenu et le mettre dans une vue. C'est souvent utilisé pour l'administration de base de données ou le dashboarding.
C'est en gros un copié collé de requête, permettant de simplifier la syntaxe d'appels à des éléments de cette vue.
Attention, ça n'optimise en rien votre accès aux données (sauf la syntaxe) :
SELECT * FROM V_CustomerProduct WHERE FullName = 'Cole Watson'
CASE
WHEN YEAR(hire_date) < 1995
THEN 'Vieux'
WHEN YEAR(hire_date) BETWEEN 1995 AND 1997
THEN 'Normal'
ELSE 'Récent'
END
AS LabelDate
FROM Employee
C'est un opérateur permettant de réaliser une opération comme un switch. Voici un exemple :
En fonction de la date, on affiche une chaîne de caractère différente dans le champ "LabelDate" qui sera retourné par la requête.
On a vu avec Merise que l'on peut faire de la récursivité.
Voici comment ça fonctionne avec un exemple :
SELECT daEnfant.AccountDescription, daParent.AccountDescription
FROM DimAccount daEnfant
INNER JOIN DimAccount daParent ON daEnfant.ParentAccountKey = daParent.AccountKey
Exemple (fonctionnel)
Les jointures... Ici, nous aurons besoin de la base proposée par Microsoft
Voici le lien de téléchargement : https://docs.microsoft.com/fr-fr/sql/samples/adventureworks-install-configure?view=sql-server-ver15&tabs=ssms
Envoyer le script de création de la base de données AnimalDatabase (!! MySQL !!) que l'on utilisera plus tard dans la formation.
(également pour l'évaluation)
Comme pour PHP, vous pouvez vous référer à la documentation SQL afin de trouver des exemples ou la syntaxe pour réaliser les opérations voulues. Nous en avons vu quelques exemples, mais les cas de requêtage en SQL sont presque illimités.
Voici le lien de la documentation : https://sql.sh/
LA DOC ?
C'est réellement ce qui peut vous sauver des heures de recherches inutiles, prenez le reflexe également pour le SQL !
INSERT INTO matable (colonnes, ...)
SELECT valeurs, ....
FROM ...
INNER JOIN ...
Comme on l'a vu, une base de données relationnelle s'assure de l'intégrité des données, c'est encore plus important concernant les clés primaires / clés étrangères. Donc quand on fait une insertion, on s'assure d'insérer une donnée qui est liable par la clé.
Première façon de faire, avec un INSERT utilisant le retour d'un SELECT, pensez bien à vous assurer que le nombre de champs de votre sélection correspond au nombre de champs de votre INSERT (ici, on peut même faire de l'insertion multiple)
INSERT INTO Employee
VALUES (15, 'JOHN', 'LAM', 'ADMIN', '12-17-1990', 18000, NULL,
(
SELECT TOP 1 dep_id
FROM Department
WHERE dep_location = 'SEATTLE’
)
)
Comme on l'a vu, une base de données relationnelle s'assure de l'intégrité des données, c'est encore plus important concernant les clés primaires / clés étrangères. Donc quand on fait une insertion, on s'assure d'insérer une donnée qui est liable par la clé.
Seconde façon, avec une sous requête permettant d'aller récupérer l'identifiant de ce que l'on cherchait à insérer comme clé étrangère.
Notez la présence du TOP 1 permettant de s'assurer de n'avoir qu'un identifiant de clé étrangère dans le retour de la sous requête
UPDATE Table1
SET Table1.col1 = Table2.col1
FROM Table1 INNER JOIN Table2 ON Table1.Id = Table2.Id
Il en est de même avec les update respectant l'intégrité des clés primaires / étrangères. Ca peut se faire de deux façons.
Première façon de faire, avec un UPDATE avec une table de départ et une jointure dans le FROM sous le SET.
UPDATE Table
SET cle_etrangere = (
SELECT TOP 1 id
FROM TableLiee
WHERE champ_table_liee = 'Condition'
)
WHERE champ_condition_Table_modifiee = '...'
Il en est de même avec les update respectant l'intégrité des clés primaires / étrangères. Ca peut se faire de deux façons.
Seconde façon, avec une sous requête permettant d'aller récupérer l'identifiant de ce que l'on cherchait à modifier comme clé étrangère.
Notez la présence du TOP 1 permettant de s'assurer de n'avoir qu'un identifiant de clé étrangère dans le retour de la sous requête
UPDATE Employee
SET Employee.dep_id = (
SELECT TOP 1 dep_id
FROM Department
WHERE dep_location = 'JERSEY'
)
FROM Department INNER JOIN Employee ON Employee.dep_id = Department.dep_id
WHERE dep_location = 'NEW YORK'
Exemple mixant les deux possibilités...
DELETE
t1
FROM
Table2 t2
INNER JOIN Table1 t1 ON (t1. _id = t2._id)
WHERE (…)
De même pour la suppression avec intégrité des clés primaires / étangères
A noter qu'il existe aussi la suppression en cascade, qui permet de supprimer les données liées (par exemple, si on supprime un département, le SGBD va supprimer tous les employés associés à ce département. Ca se défini au moment de la création de clé étrangère.
MAIS C'EST RISQUE !
ALTER TABLE Department
ADD CONSTRAINT FK_Department_Employee_Cascade
FOREIGN KEY (dep_id) REFERENCES Employee(dep_id) ON DELETE CASCADE
(Sur la base des exercices précédents)
Sur la base Formation_RIL
Requête d’insertion avec jointures, update avec jointure, delete avec jointure (« Jointure » = Abus de langage, on parle plutôt de suppression propre en fonction des relations existantes)
GO
CREATE FUNCTION GET_EMPLOYEE_SALARY_BY_NAME
(
@emp_firstname varchar(50), -- paramètre d'entrée de la fonction
@emp_lastname varchar(50)
)
RETURNS numeric(7,2) -- type de retour
AS
BEGIN
RETURN -- Retour via un SELECT (généralement une seule donnée)
(
SELECT emp_salary
FROM Employee
WHERE emp_firstname = @emp_firstname AND emp_lastname = @emp_lastname
)
END
GO
Il est possible de créer ses propres fonctions avec SQL. A noter que celles-ci doivent toujours avoir un type de retour.
Attention aux contre performances, appeler une fonction dans un champ, c'est exécuter cette fonction pour tous les enregistrements !
(Utilisation de table temporaire recommandé dans ce cas)
SELECT dbo.GET_EMPLOYEE_SALARY_BY_NAME('ARTHUR', 'PARISOT') AS Salary
-- dbo, c'est le schéma de base.
Il est possible de créer ses propres fonctions avec SQL. A noter que celles-ci doivent toujours avoir un type de retour.
Appel de fonction direct
DECLARE @Salary numeric(7,2) = dbo.GET_EMPLOYEE_SALARY_BY_NAME('ARTHUR', 'PARISOT')
SELECT @Salary
-- Oui, on peut créer des variabls en SQL :)
Appel de fonction via variable
Seulement la partie fonction ! Les procédures, on les voit juste après.
Création de fonctions
Reprise des bases & workshop complémentaires
Une procédure stockée est un objet de base de données, qui peut être appelé pour réaliser une suite d'instructions ayant un but particulier. Par exemple, une procédure de pagination pour l'affichage d'une liste de composants.
CREATE PROCEDURE SP_ProcedureName
(
@ComponentName varchar(50) = NULL,
@ComponentVersion varchar(10)
)
WITH RECOMPILE -- Optionnel mais permet généralement de meilleurs performances
AS
BEGIN
-- Instructions à exécuter sur la procédure,
-- @ComponentName & @ComponentVersion sont utilisables comme des variables
END
EXEC SP_ProcedureName 'QUAL', '3.4'
Les paramètres de procédures stockées sont disponible en entrée, dans le corps de la procédure ainsi qu'être rendu disponible à la sortie de la procédure.
CREATE PROCEDURE SP_ProcedureName
(
@ComponentName varchar(50) = NULL, -- valeur par défaut : NULL
@ComponentVersion varchar(10) NOT NULL -- Pas de NULL possible
)
AS
BEGIN
SELECT ComponentDate AS CompDate,
ComponentTeam AS CompTeam,
@ComponentName AS CompName,
@ComponentVersion AS CompVersion
FROM Components
WHERE ComponentName = @ComponentName AND ComponentVersion = @ComponentVersion
END
GO
EXEC SP_ProcedureName ‘test’, ‘test’;
Quelques exemples de procédures stockées...
Exercice de création de procédures stockées.
Je vais vous envoyer le sujet, sinon, le voici :
Envoyez moi les exercices tout au long de la formation, j'utiliserai ces éléments pour la notation.
-- Sur la base AdventureWorksDW : USE AdventureWorksDW
-- 1. Créez une procédure stockée permettant de récupérer
-- l'emplacement géographique (DimGeography.FrenchCountryRegionName)
-- d'un client par son nom complet (FirstName + LastName)
-- 2. Créez une procédure stockée permettant d'insérer un nouvel enregistrement dans la table
-- DimProductCategory avec des entrées correspondant aux noms des champs de la table
-- ProductCategoryAlternateKey (int), EnglishProductCategoryName (varchar(50)),
-- SpanishProductCategoryName (varchar(50)), FrenchProductCategoryName (varchar(50))
Gérer les erreurs est également possible en SQL, c'est d'ailleurs recommandé dans les procédures stockées.
BEGIN TRY
SELECT 1/0;
END TRY
BEGIN CATCH
SELECT
ERROR_NUMBER() AS NumeroErreur,
ERROR_SEVERITY() AS SeveriteErreur,
ERROR_STATE() AS EtatErreur,
ERROR_PROCEDURE() AS ProcedureErreur,
ERROR_LINE() AS LigneErreur,
ERROR_MESSAGE() AS MessageErreur;
END CATCH;
Un bloc try, un bloc catch et on peut retourner les informations d'erreurs dans une requête.
Il est également possible de générer soit même son erreur avec l'instruction THROW numError (min 50000), 'message', severité. Ceci permettant d'anticiper les erreurs dans vos scripts (validation d'entrée utilisateur par ex)
BEGIN TRY
DECLARE @a smallint = 0
--Instructions
IF @a = 0
BEGIN
RAISERROR('@a ne doit pas être égal à zéro', 1, 1);
THROW 50001, '@a ne doit pas être égal à zéro’, 1; -- On va directement dans le catch
END
END TRY
BEGIN CATCH
SELECT ERROR_MESSAGE(), ERROR_NUMBER(), ERROR_SEVERITY(), ERROR_STATE()
END CATCH
Ou uniquement notifier l'erreur avec RAISERROR('message', sévérité, state). Une sévérité entre 0 et 10 ne stop pas l'exécution et notifie d'une erreur (warning), au delà, l'erreur mène directement au bloc catch ! (Entre 11 et 25)
Exercice de création de procédures stockées avec gestion des erreurs.
Envoyez moi les exercices tout au long de la formation, j'utiliserai ces éléments pour la notation.
Exercice de création de procédures stockées avec gestion des erreurs.
SAUF POUR LE NUMERO DE TELEPHONE.
Envoyez moi les exercices tout au long de la formation, j'utiliserai ces éléments pour la notation.
En complément de la gestion des erreurs il est possible de gérer un état de base de données afin de pouvoir valider un ensemble de requêtes, ou les invalider en cas d'erreurs.
BEGIN
BEGIN TRAN TransacName
BEGIN TRY
-- Requêtes à effectuer en un bloc sûr
COMMIT TRANSACTION TransacName -- On valide la transaction
PRINT 'Transaction OK'
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION TransacName --On annule la transaction
SELECT ERROR_MESSAGE()
END CATCH
END
Un commit de transaction provoque la validation.
Un rollback de transaction provoque l'annulation.
Un exemple de transaction, avant l'exercice...
(Les 5 clients peuvent êtres choisis arbitrairement)
Envoyez moi les exercices tout au long de la formation, j'utiliserai ces éléments pour la notation.
Une opération de merge de données est plus souvent plus rapide pour de la modification massive de données.
MERGE INTO table1
USING table_reference
ON (conditions)
WHEN MATCHED THEN
UPDATE SET table1.colonne1 = valeur1, table1.colonne2 = valeur2
WHEN NOT MATCHED THEN
INSERT (colonnes1, colonne3)
VALUES (valeur1, valeur3)
Une table de base, une table de référence et une condition à analyser. Selon le match, on réalise une opération ou une autre.
Deux exemples de merge, le merge simple et un merge plus complexe...
Exercice de manipulation du merge
Je vais vous envoyer le sujet, sinon, le voici :
Envoyez moi les exercices tout au long de la formation, j'utiliserai ces éléments pour la notation.
-- Sur la base Formation_RIL, créez une table EmployeeMerged
-- avec les mêmes champs que la table Employee
-- Grâce à une opération de MERGE, inserez tous les employés
-- des départements 1,2 et 3 dans la table EmployeeMerged.
-- ATTENTION, Insert n'est utilisable qu'en cas de WHEN NOT MATCHED.
-- Insérez un Employee sans département ('Test', 'Test', 'Test', '01-01-1999', 0, 0, NULL)
-- Grâce à une opération de MERGE, modifiez le département
-- des employés sans département vers un département existant (Ex : 4)
C’est un objet à part entière en base de données, donc comme pour tous les objets on peut faire ce type d’opérations dessus :
Un trigger représente une succession d’instructions qui sont déclenchées automatiquement lorsqu’une autre action (précise et définie lors de la création du trigger) est levée sur le SGBDR.
Les triggers sont utilisés essentiellement pour assurer l’intégrité des données dans une base de données relationnelle.
Il existe deux classes de triggers :
Pour deux types :
La syntaxe de création ou modification d'un trigger se présente comme suit :
CREATE OR ALTER TRIGGER triggerName
ON tableName –- Ou vue
AFTER INSERT, UPDATE
AS
-- Instructions à effectuer après l'exécution de la requête déclenchant le trigger
GO
CREATE OR ALTER TRIGGER triggerName
ON tableName –- Ou vue
INSTEAD OF INSERT, UPDATE, DELETE
AS
-- Instructions à effectuer de l'instruction de base
GO
On remarque que soit on réalise les opérations après exécution de la requête déclencheuse, soit on remplace.
Un exemple très simple en utilisant une procédure MSSQL d'envoi de mail :
CREATE TRIGGER TRIG_Mail_Reminder_NewVersion
ON dbo.AdventureWorksDWBuildVersion
AFTER INSERT, UPDATE, DELETE
AS
EXEC msdb.dbo.sp_send_dbmail
@recipients = 'email@address.com',
@body = 'Modification de la table de version',
@subject = 'TRIG_Mail_Reminder_NewVersion : Information action sur version';
GO
Juste pour pratiquer rapidement, réalisez un trigger sur la table T_Client.
A chaque UPDATE, DELETE, INSERT sur la table T_Client, réaliser un trigger INSTEAD OF permettant de remplacer la requête par un
SELECT 'Ces opérations sont interdites et ont été désactivées';
Testez en faisant des opérations sur la table.
Pensez absolument à supprimer ce trigger (soit avec l'instruction DROP TRIGGER triggerName, soit depuis l'interface dans "déclencheurs", après avoir cliqué sur le petit + à côté de la table T_Client)
Envoyez moi les exercices tout au long de la formation, j'utiliserai ces éléments pour la notation.
Tables INSERTED / DELETED
Les triggers génèrent des données dans des tables temporaires liées uniquement à la requête déclenchant le trigger. (Les données sont mises en cache)
Ces tables sont les suivantes :
- INSERTED : Contient les données insérées et mises à jour
- DELETED : Contient les données supprimées
Utile pour gérer ce qui s’est passé sur l’action déclenchant le trigger.
Tables INSERTED / DELETED
Un exemple pour mieux comprendre avant l'exercice :
CREATE TRIGGER inserted_client
ON T_Client
FOR INSERT, UPDATE
AS
-- requête de contrôle avec table d'insertion
SELECT *
FROM INSERTED I
INNER JOIN T_Client tc ON I.cli_id= tc.cli_id
IF @@Error <> 0
ROLLBACK TRANSACTION
GO
CREATE TRIGGER deleted_client
ON T_Client
FOR DELETE
AS
-- requête de contrôle avec table de suppression
SELECT *
FROM DELETED
-- rollback en cas d'erreur
IF @@Error <> 0
ROLLBACK TRANSACTION
GO
INSERT INTO T_Client VALUES ('Tri','Gger', '1990-01-07','XL','01 02 03 04 05')
UPDATE T_Client SET cli_prenom = 'Tri2' WHERE cli_id = @@IDENTITY
DELETE FROM T_Client WHERE cli_id = @@IDENTITY
Reprenez la base « ClientDatabase», créez un trigger permettant de contrôler la validité des données lors d’une insertion d’un nouveau client.
1. Assurez-vous à l’aide d’un trigger que le numéro donné pour chaque insertion de client est bien composé qu’avec des chiffres et des points, ou sans espace.
Info : (Replace ‘ ‘, ‘.’) Cast decimal.
Pensez au retour arrière en cas d’erreur.
2. Ajoutez ce fonctionnement pour tout update sur le champ Num_client.
3. Ici, l'exercice consiste maintenant à corriger à la volée les saisies incorrectes. Tous les caractères de séparation d’un numéro de téléphone, tels que les tirets ou les espaces ou les points devront être supprimés pour que les données passent au format « 0102030405 » ou alors au format « 01.02.03.04.05 » si vous préférez.
==> Indice : Utilisez la jointure entre la table T_Client et la table de INSERTED
Envoyez moi les exercices tout au long de la formation, j'utiliserai ces éléments pour la notation.
Quand utiliser un trigger
Prenons le MLD suivant :
Créez la table « Facture ». Cli_id et fac_id seront des clés primaires auto incrémentées (IDENTITY(1,1)) et n’ajoutez PAS la clé étrangère sur cli_id pour l’instant, on veut que le delete cascade soit géré directement dans la contrainte de clé étrangère (non conseillé sauf cas particuliers)
Ajoutez des données fictives dans les deux tables. A minima, un client et deux factures associées à ce client. (Avec des id de clients existants pour T_Facture (cli_id))
Suppression en cascade. Grâce à un trigger, veuillez vous assurer que si un client est supprimé, toutes les factures associées le sont aussi.
Envoyez moi les exercices tout au long de la formation, j'utiliserai ces éléments pour la notation.
Créez les tables associées au MLD suivant (sans clé primaire ni étrangère):
On remarque un lien d’héritage, sa gestion suppose souvent une exclusion entre les fils. Une valeur de clef présente dans T_VEHICULE peut donc se retrouver soit dans T_BATEAU soit dans T_AVION mais pas dans les deux tables.
Créez le trigger permettant de vous assurer que la clé insérée de T_AVION existe bien dans la table T_VEHICULE ainsi que la clé n’est pas déjà présente dans la table T_BATEAU.
A noter également qu’un trigger équivalent est nécessaire sur la table T_BATEAU.
Envoyez moi les exercices tout au long de la formation, j'utiliserai ces éléments pour la notation.
Qu’est-ce qu’un curseur ?
Un curseur permet d'itérer sur tous les résultats d'une sélection prédéfinie.
DECLARE C_Nom CURSOR
FOR (SELECT nom, prenom FROM T_Client)
-- Requête de sélection sur laquelle on veut parcourir les données
OPEN C_Nom -- Ouverture du curseur
-- Déclaration de(s) variable(s)
DECLARE @nom varchar(50) = ‘’
DECLARE @prenom varchar(50) = ‘’
FETCH C_Nom INTO @nom, @prenom -- Ouverture du curseur
WHILE @@FETCH_STATUS = 0 -- Jusqu’à ce qu’il n’y ai plus de ligne
BEGIN
-- Opérations en fct de @nom, toujours sur la requête du départ
FETCH C_Nom INTO @nom , @prenom -- On passe à l’enregistrement suivant
END
CLOSE C_Nom -- /!\ Ne jamais oublier ces deux lignes /!\
DEALLOCATE C_Nom
Honnêtement, ce n'est pas performant et je déconseille l'utilisation de curseur en toutes circonstances.
Un exemple concret
GO
DECLARE C_auteurs CURSOR
FOR
SELECT cli_nom FROM T_Client
OPEN C_auteurs
DECLARE @all_noms VARCHAR(3000) = ''
DECLARE @nom VARCHAR(50)
FETCH C_auteurs INTO @nom
WHILE @@FETCH_STATUS = 0
BEGIN
IF @all_noms = ''
SET @all_noms = @nom
ELSE
SET @all_noms = @all_noms + ', ' + @nom
PRINT @all_noms
FETCH C_auteurs INTO @nom
END
CLOSE C_auteurs
DEALLOCATE C_auteurs
Un exemple pour mieux comprendre
Les possibilités de l’instruction FETCH :
Fonctions de curseurs :
Un exemple de curseur...
-- Exercice curseur : CREATE TABLE T_Joueur (id int NOT NULL PRIMARY KEY IDENTITY(1,1), prenom varchar(30)).
-- Insérez 7 joueurs, dont un avec le nom = NULL
-- Récupérez la liste de tous les prénoms concaténés grâce à un curseur. Retour voulu : ‘Prenom1’, ‘Prenom2’, …, ‘Prenom7’
-------------------------------------------
-- Obtenir le même résultat sans curseur --
-------------------------------------------
* Pour éviter les NULL, vous pouvez utiliser COALESCE(jou_prenom, ‘, ‘, ‘’)
Envoyez moi les exercices tout au long de la formation, j'utiliserai ces éléments pour la notation.
-- Réalisez un curseur qui cumule les scores par joueur, au tour par tour; avec un curseur. Il y’a 3 joueurs,
-- C'est à dire, retourner une ligne par joueur et par tour. Ainsi qu'un score.
-- Utilisation de table temporaire souhaité
-- CREATE TABLE #CUMULS (sco_tour int, jou_id int, sco_value int)
-- Premier enregistrement: j1, t1, score = score t1j1
-- Second enregistrement: j1, t2, score = score t1j1 + score t2j1
-- Troisième enregistrement : j1, t3, score = score t1j1 + score t2j1 + score t3j1
-- Quatrième enregistrement : j2, t1, score = score t1j2
-- Cinquième enregistrement : j2, t2, score = score t1j2 + score t2j2
-- ...
-- Dernier enregistrement: j3, t3, score = score t1j3 + score t2j3 + score t3j3
-------------------------------------------
-- Obtenir le même résultat sans curseur --
-------------------------------------------
Envoyez moi les exercices tout au long de la formation, j'utiliserai ces éléments pour la notation.
SELECT classique
Curseurs
Il existe plusieurs types de données afin de les stocker. Ce qu'il faut comprendre c'est que le type de données défini un espace mémoire pour le stockage de cette donnée.
Quelques conseils :
Types
Un des type les plus impactant, car souvent le moins bien géré, est le type numérique.
Voici les informations qu'il faut savoir (pas au chiffre près) pour ces types :
D'autres éléments sont à prendre en compte pour optimiser votre stockage de données, tels que le choix du moteur de stockage ainsi que les capacités du serveur de base de données.
Pour votre serveur : Avoir suffisamment de RAM, monitoring, utilisation de socket Unix (si possible, sur la même machine que le code applicatif), mise en place et utilisation d'un système de mise en cache.
Pour le choix du moteur de stockage :
Qu’est-ce qu’un index concrètement ?
Un exemple pour mieux comprendre
Si l'on créer un index suivant ces colonnes :
Pour cet index composé d'un nom d'un prénom et d'une date de naissance, la recherche sera efficace pour les informations suivantes :
Un exemple pour mieux comprendre
Si l'on créer un index suivant ces colonnes :
Pourquoi pas sur CLI_PRENOM + CLI_DATE_NAISSANCE ?
Types et différences
Clusterisé
Non clusterisé
Couvrant
Les données sont triées en lignes selon la clé fournie à l’index.
Les données sont vectorisées selon l'ordre fourni.
Les données sont vectorisées selon l'ordre en excluant des colonnes inutiles :
Ceci permet de ne pas avoir besoin d’aller rechercher les données incluses dans la table.
Attention cependant à l’espace mémoire que ça peut impliquer.
Syntaxe de création
-- Créer un index non clusterisé
CREATE INDEX i1 ON t1 (col1);
-- Créer un index clusterisé (en general automatique via PK)
CREATE CLUSTERED INDEX i1 ON t1(col1);
-- Syntaxe SQL Server
CREATE UNIQUE INDEX i1 ON t1 (col1 DESC, col2 ASC, col3 DESC);
-- Index non clusterisé avec colonnes incluses
CREATE NONCLUSTERED INDEX IX_Address_PostalCode
ON Address (PostalCode)
INCLUDE (AddressLine1, AddressLine2, City, StateProvinceID);
Un exemple sur SQL Server
CREATE UNIQUE CLUSTERED INDEX X_Cli_prenom_tel
ON T_CLIENT (cli_nom ASC,
cli_prenom,
cli_date_naissance DESC)
WITH (FILLFACTOR = 80,
SORT_IN_TEMPDB = ON,
ALLOW_ROW_LOCKS = ON,
MAXDOP = 3)
ON STORAGE
Un exercice pour vous
Créer trois indexes :
Quand devons nous absolument créer un index ?
La plupart des SGBDR s’en chargent pour chaque clé primaires et étrangères, en majeur partie des cas.
Il faut donc en créer un soi-même !
Quand devons nous absolument créer un index ?
create table T_COMMANDE_CMD (
CMD_NUM int not null PRIMARY KEY,
CMD_DATE datetime not null
)
create table T_PRODUIT_PRD (
PRD_REFERENCE char(8)not null PRIMARY KEY,
PRD_DESIGNATION varchar(64)not null
)
create table T_J_COMPOSEE_CPS (
CMD_NUM int not null foreign key (CMD_NUM) references T_COMMANDE_CMD (CMD_NUM),
PRD_REFERENCE char(8) not null foreign key (PRD_REFERENCE)
references T_PRODUIT_PRD (PRD_REFERENCE),
CPS_QUANTITE float not null,
constraint PK_T_J_COMPOSEE_CPS primary key (CMD_NUM, PRD_REFERENCE)
)
Voici le code SQL associé à cet index :
Quand devons nous absolument créer un index ?
Les index créés automatiquement à cause des PRIMARY KEY
Les index à créer pour les FOREIGN KEY :
Le second est redondant car déjà présent avec une vectorisation supplémentaire.
Quand devons nous potentiellement créer un index ?
Utilisation de requêtes prédéfinies afin de détecter le top X des requêtes les plus consommatrices.
Je vous enverrai un script contenant plusieurs requêtes d'analyse de votre base de données
-- Afficher les 50 dernières requêtes les plus consommatrices de CPU
-- Afficher les 50 dernières requêtes exécutées
-- Afficher les 50 requêtes les + fréquemment exécutées
-- Création automatisée d'index
-- Mesurer les volumes stockées par partition, index et tables d’une base
-- Index trop lourds
-- Vérification des index dont les clés contiennent trop de colonnes
Conseils et informations
Le conditionnement sur index
Utilisés sur des données calculables. Exemple… Les dates.
Les clients nés avant 1900 ne nous intéressent surement moins aujourd’hui dans un index.
On peut donc créer un index avec une clause where comme suit :
Ceci permet une double optimisation, sur la mémoire utilisée par l’index et sur le temps de traitement des requêtes utilisant cet index. Moins de données accédées impliquent une recherche (je le rappel, souvent dichotomique) moins longue.
CREATE INDEX X_CLIENT ON T_Client (cli_nom, cli_prenom)
WHERE (cli_date_naissance > '1900-01-01'
Chaque icone a sa propre signification, la couleur des icônes classe par famille :
Beaucoup d’informations importantes disponibles au survol.
Ex : Type d’index utilisé, nombre de lignes de codes sur laquelle l’opération de jointure est faite,…
Des outils permettent de monitorer en continue les requêtes vous informer sur les optimisations possibles.
En cycle de développement, l’outil tourne en tâche de fond et analyse les plans d’exécution pour proposer des optimisations. Soit par des ajouts index, des analyses de requêtes, ou des analyses des statistiques utilisés par les index.
Exemple concret rencontré récemment sur un problème SQL :
Je vous envoie une procédure que l’on va analyser rapidement.
Note : Les plans d’exécutions sont parfois radicalement différents entre SQL Server 2012 et SQL Server 2016 ou SQL Server 2019, ou … Vous avez compris l’idée.
Faites donc extrêmement attention lorsque vous montez de version, il est nécessaire de recetter l’application complètement afin de vous assurer que la montée de version n’a pas « cassé » des index.
Sans rentrer dans le détail car ça ne fait pas parti du plan de formation :
le Big Data est une forte volumétrie, haute Vélocité et grande Variété de données qui exigent des techniques innovantes et rentables de traitement d’information pour une meilleure efficacité
Volume
Vélocité
Variété
Le NoSQL est de plus en plus utilisé pour répondre aux enjeux du BigData
Le fonctionnement est différent d'un SGBDR, les données ne respectent plus un schéma particulier et elles sont stockées sous forme de document (ou parfois de graphes).
Ceci ayant plusieurs avantages par rapport à un SGBDR :
Un exemple très rapide pour montrer la syntaxe mongoDB
Un exemple de projet avec GraphQL
Envoyer fichier sql_theory
Envoyer le fichier .sql
BON COURAGE !
(après c'est fini)
Workshop Entity : Envoyer le fichier workshop_entity.docx
Voir workshop_sql_subject.sql
Voir workshop_nosql_subject.sql