REGExpr Testeur est bientôt disponible...
Cette application permet : de mettre au point, tester, et surtout comprendre une expression régulière
Dans cet écran : un édite et on teste l'expression régulière.
L'onglet Information décrypte la composition de l'expression.
12 décembre 2018
SQL - Concaténer plusieurs lignes dans une colonne
Ça arrive de temps en en temps : j'ai besoin de remonter dans une colonne une liste de pleins d'éléments présent dans plusieurs lignes d'une sous table.
Pour être concret :
On a une table activité : dbo.activite qui contient les colonnes: actId; actNom
On a une table participant d'une activité : dbo.participant qui contient les colonnes : parId, parNom, actId
J'ai besoin de remonter sur une ligne les activités avec une colonne contenant les noms de tous les participants de chaque activité !
Ma proposition :
SELECT a.actId, a.actNom
, (SELECT p.parNom + ', ' as [text()]
FROM dbo.participant p
WHERE p.actId = a.actId
ORDER BY p.parId
FOR XML PATH('')) AS participants
FROM dbo.activite a
Cette requête remonte les infos de la table activité et une colonne participants qui contient les noms séparés par une virgule des participants de l'activité.
Les points à noter :
La colonne participants est construite par une sous requête qui remonte en XML (FOR XML PATH('')) les noms des participants en y ajoutant un séparateur (la virgule + espace ici),
Cette sous requête, remontée dans une colonne de résultat, a pour effet de concaténer les lignes de XML en une seule valeur.
La colonne remontée est renommée AS [text()], fonction de conversion XML qui permet à SQL Server de retirer les balises XML ! (Tout est la !!) . Cela nettoie la chaîne concaténée des balises xml inutiles ici (Attention text() est sensible à la casse : il doit être écrit en minuscule, n'oubliez pas non plus les parenthèses pour que la méthode soit invoquée, le tout entre crochet ou apostrophes pour respecter la syntaxe SQL ).
A noter la clause Order By qui s'assure de l'ordre de présentation des participants.
Reste un petite erreur dans ce qui est remonté : La liste des noms se termine par un séparateur (la virgule + espace ici), il faudra filtrer cela dans l'applicatif ou complexifier la requête pour retirer les 2 derniers caractères inutiles.
Maj 05/2021 : Attention aussi au contenu remonté, s'il y a des caractères spéciaux (pour l'XML) ils seront XML encodé, il faudra donc les décoder avant de les utiliser. Avec par exemple CONVERT(NVARCHAR(MAX), CONVERT(XML, participants)dans l'exemple ci dessus.
Enjoy !
Pour être concret :
On a une table activité : dbo.activite qui contient les colonnes: actId; actNom
On a une table participant d'une activité : dbo.participant qui contient les colonnes : parId, parNom, actId
J'ai besoin de remonter sur une ligne les activités avec une colonne contenant les noms de tous les participants de chaque activité !
Ma proposition :
SELECT a.actId, a.actNom
, (SELECT p.parNom + ', ' as [text()]
FROM dbo.participant p
WHERE p.actId = a.actId
ORDER BY p.parId
FOR XML PATH('')) AS participants
FROM dbo.activite a
Les points à noter :
La colonne participants est construite par une sous requête qui remonte en XML (FOR XML PATH('')) les noms des participants en y ajoutant un séparateur (la virgule + espace ici),
Cette sous requête, remontée dans une colonne de résultat, a pour effet de concaténer les lignes de XML en une seule valeur.
La colonne remontée est renommée AS [text()], fonction de conversion XML qui permet à SQL Server de retirer les balises XML ! (Tout est la !!) . Cela nettoie la chaîne concaténée des balises xml inutiles ici (Attention text() est sensible à la casse : il doit être écrit en minuscule, n'oubliez pas non plus les parenthèses pour que la méthode soit invoquée, le tout entre crochet ou apostrophes pour respecter la syntaxe SQL ).
A noter la clause Order By qui s'assure de l'ordre de présentation des participants.
Reste un petite erreur dans ce qui est remonté : La liste des noms se termine par un séparateur (la virgule + espace ici), il faudra filtrer cela dans l'applicatif ou complexifier la requête pour retirer les 2 derniers caractères inutiles.
Maj 05/2021 : Attention aussi au contenu remonté, s'il y a des caractères spéciaux (pour l'XML) ils seront XML encodé, il faudra donc les décoder avant de les utiliser. Avec par exemple CONVERT(NVARCHAR(MAX), CONVERT(XML, participants)dans l'exemple ci dessus.
Enjoy !
09 novembre 2018
SQL - Les Formats de date et heure
Vous galérez pas à trouver le bon format pour convertir les dates en texte dans SQL Server vous ?
Moi Oui !!
Du coup j'utilise la requête ci-dessous pour trouver le bon format en fonction de mes besoins :
DECLARE @date DATETIME
SET @date = GETDATE() --- la date que vous voulez !!
SELECT format, CONVERT(NVARCHAR(MAX), @date, format)
FROM (SELECT ROW_NUMBER() OVER(ORDER BY object_id) - 1 AS format
FROM sys.all_objects) x
WHERE format < 132
AND format NOT IN (15, 16, 17, 18, 19, 115, 116, 117, 118, 119, 122, 123, 124, 125, 128, 129)
AND (format < 26 OR format > 99 )
On compte le nombre de ligne de sys.all_objets ou de n'importe quelle table qui a plus de 132 lignes !!
Puis on exclus les ID non valides de la fonction convert
Puis on affiche toutes les conversions possibles.
La doc SQL SERVER de la fonction CONVERT est ici
Plus sexy avec une CTE récursive :
DECLARE @date DATETIME
SET @date = GETDATE()
;
WITH nombre (idx)
AS (SELECT 0 AS idx
UNION ALL SELECT idx + 1 FROM nombre WHERE idx < 131)
SELECT idx AS format, CONVERT(NVARCHAR(MAX), @date, idx) FROM nombre
WHERE idx NOT IN (15, 16, 17, 18, 19, 115, 116, 117, 118, 119, 122, 123, 124, 125, 128, 129)
AND (idx < 26 OR idx> 99 )
OPTION(maxrecursion 132)
A noter le ; avant le WITH De la CTE
A noter aussi on utilise la clause OPTION pour augmenter la profondeur de récursion car ici on a besoin d'une profondeur connue de 132
Enjoy !
Moi Oui !!
Du coup j'utilise la requête ci-dessous pour trouver le bon format en fonction de mes besoins :
DECLARE @date DATETIME
SET @date = GETDATE() --- la date que vous voulez !!
SELECT format, CONVERT(NVARCHAR(MAX), @date, format)
FROM (SELECT ROW_NUMBER() OVER(ORDER BY object_id) - 1 AS format
FROM sys.all_objects) x
WHERE format < 132
AND format NOT IN (15, 16, 17, 18, 19, 115, 116, 117, 118, 119, 122, 123, 124, 125, 128, 129)
AND (format < 26 OR format > 99 )
On compte le nombre de ligne de sys.all_objets ou de n'importe quelle table qui a plus de 132 lignes !!
Puis on exclus les ID non valides de la fonction convert
Puis on affiche toutes les conversions possibles.
La doc SQL SERVER de la fonction CONVERT est ici
Plus sexy avec une CTE récursive :
DECLARE @date DATETIME
SET @date = GETDATE()
;
WITH nombre (idx)
AS (SELECT 0 AS idx
UNION ALL SELECT idx + 1 FROM nombre WHERE idx < 131)
SELECT idx AS format, CONVERT(NVARCHAR(MAX), @date, idx) FROM nombre
WHERE idx NOT IN (15, 16, 17, 18, 19, 115, 116, 117, 118, 119, 122, 123, 124, 125, 128, 129)
AND (idx < 26 OR idx> 99 )
OPTION(maxrecursion 132)
A noter le ; avant le WITH De la CTE
A noter aussi on utilise la clause OPTION pour augmenter la profondeur de récursion car ici on a besoin d'une profondeur connue de 132
Enjoy !
24 octobre 2018
SQL Pagination
Y-a-t-il des "Best Practices" pour paginer le résultat d'une requête ?
Si on a une requête du style :
SELECT xxx
FROM -- [... suite du FROM + WHERE très complexe ]
Et qu'on veut la paginer...
...Que faut il pour le faire correctement ?
en entrée : la page à afficher, la taille des page et l'information de tri
en sortie : il nous faut : le nombre total de pages, et ou le nombre total d'éléments et les X éléments seulement.
Le but aussi est de ne pas faire deux fois la requête : une pour compter et une pour renvoyer les X éléments demandés !!
Voila ce que je propose :
1) variables de pagination : @page est le numéro de la page demandée (commence à 1 !!)
@pageSize est le nombre de lignes dans la page (ici 20)
DECLARE @page INT = 1, @pageSize INT = 20
2) Entourer la requête de :
SELECT *, CEILING(x.nb_results / @pageSize) AS nb_pages
FROM
(
SELECT xxx
...
La colonne remontée nb_pages contiendra un entier avec le nombre total de pages possibles
3) Dans le SELECT ajouter les colonnes :
, ROW_NUMBER() OVER(ORDER BY --- critère de tri ) AS rnk
, COUNT(*) OVER() AS nb_results
FROM -- [... suite du FROM + WHERE très complexe ]
La colonne rnk avec le ROW_NUMBER() permet d'attribuer un numéro à chaque ligne remontée
Pour ce faire il faut définir la ou les colonnes sur lesquelles trier
Remarque il n'est pas nécessaire de remonter cette colonne dans le résultat final.
La colonne nb_results avec le COUNT(*) OVER() permet de calculer le nombre total de ligne, même si on filtres celle ci : Magique ! Merci SQL Server
4) à la fin de la requête on ajoute
) AS newTable
WHERE rnk BETWEEN ((@page - 1) * @pageSize + 1) AND (@page * @pageSize)
C'est ici qu'on prélève les lignes qui correspondent à la page à visualiser.
Enjoy !
Si on a une requête du style :
SELECT xxx
FROM -- [... suite du FROM + WHERE très complexe ]
Et qu'on veut la paginer...
...Que faut il pour le faire correctement ?
en entrée : la page à afficher, la taille des page et l'information de tri
en sortie : il nous faut : le nombre total de pages, et ou le nombre total d'éléments et les X éléments seulement.
Le but aussi est de ne pas faire deux fois la requête : une pour compter et une pour renvoyer les X éléments demandés !!
Voila ce que je propose :
1) variables de pagination : @page est le numéro de la page demandée (commence à 1 !!)
@pageSize est le nombre de lignes dans la page (ici 20)
DECLARE @page INT = 1, @pageSize INT = 20
2) Entourer la requête de :
SELECT *, CEILING(x.nb_results / @pageSize) AS nb_pages
FROM
(
SELECT xxx
...
La colonne remontée nb_pages contiendra un entier avec le nombre total de pages possibles
3) Dans le SELECT ajouter les colonnes :
, ROW_NUMBER() OVER(ORDER BY --- critère de tri ) AS rnk
, COUNT(*) OVER() AS nb_results
FROM -- [... suite du FROM + WHERE très complexe ]
La colonne rnk avec le ROW_NUMBER() permet d'attribuer un numéro à chaque ligne remontée
Pour ce faire il faut définir la ou les colonnes sur lesquelles trier
Remarque il n'est pas nécessaire de remonter cette colonne dans le résultat final.
La colonne nb_results avec le COUNT(*) OVER() permet de calculer le nombre total de ligne, même si on filtres celle ci : Magique ! Merci SQL Server
4) à la fin de la requête on ajoute
) AS newTable
WHERE rnk BETWEEN ((@page - 1) * @pageSize + 1) AND (@page * @pageSize)
C'est ici qu'on prélève les lignes qui correspondent à la page à visualiser.
Enjoy !
29 juillet 2018
SQL : Les propriétés étendues
SQL Server permet d'ajouter des "méta données" dans une base de données : Les "extended properties".
Les propriétés étendues sont des éléments "clés / valeurs" associé à un élément de la base de données.
A rendre dynamique des interfaces : on peut imaginer ajouter des données qui permettent de générer l'IHM d'une saisie de table, ou d'avoir des message d'erreurs, de validation personnalisable...
Pour gérer cela trois procédures stockées :
les 6 derniers paramètres de ces procédures, permettent d'indiquer où associer l'information.
Level0type = 'SCHEMA'
Level0Name = 'dbo'
Level1Type = 'TABLE'
Level1Name = 'utilisateur'
Level2Type = 'COLUMN'
Level2Name = 'util_nom'
==> Indique la colonne 'util_nom' de la table 'utilisateur' du schéma 'dbo'
Pour associer une propriété à une table : ne pas préciser le 'level2'
Pour associer une propriété à une vue, fonction, procédure ==> changer les infos dans le 'level1' en conséquence !
Pour associer une propriété à la base elle même : ne pas préciser de niveaux !
Comment on retrouve les infos ?
Avec SSMS : Dans la fenêtre propriétés de l'objet concerné il y a un onglet "propriétés étendues" qui les liste aisément.
Pour les interroger, SQL Server fournit une vue sys.fn_listextendedproperty censée remonter une ou plusieurs infos en fonction des filtre passés : j'ai pas réussi à l'utiliser dans les scénarios suivants : donne moi toutes les propriétés étendues, ou donne moi toutes les propriétés de toutes les tables...
... a creuser ?!??
Heureusement en cherchant un peu on trouve que c'est dans la table : sys.extended_properties que sont stockées les informations.
Voici donc 3 requêtes pour retrouver les informations associées à :
SELECT name, value
FROM sys.extended_properties
WHERE major_id = 0 AND minor_id = 0 AND class = 0
SELECT s.name AS [schema_name], o.name AS [table_name] , p.name, p.value
FROM sys.extended_properties p
INNER JOIN sys.tables t ON p.major_id = t.object_id AND p.minor_id = 0 AND p.class = 1
INNER JOIN sys.schemas s ON t.schema_id = s.schema_id
WHERE t.name = 'utilisateur'
SELECT s.name AS [schema_name], o.name AS [table_name], c.name AS [column_name], p.name, p.value
FROM sys.extended_properties p
INNER JOIN sys.tables t ON p.major_id = t.object_id
INNER JOIN sys.columns c ON c.object_id = t.object_id AND c.column_id = p.minor_id AND p.class = 1
INNER JOIN sys.schemas s ON t.schema_id = s.schema_id
WHERE t.name = 'utilisateur'
Enjoy
Add : Avec la suppression de l'objet de la base de données (Ex : suppression d'une colonne) les propriétés étendues sont elles aussi supprimées automatiquement (sans bloquer la suppression). Merci SQL Server !
Les propriétés étendues sont des éléments "clés / valeurs" associé à un élément de la base de données.
Ca sert à quoi ?
A qualifier un élément : documentation interne de la bddA rendre dynamique des interfaces : on peut imaginer ajouter des données qui permettent de générer l'IHM d'une saisie de table, ou d'avoir des message d'erreurs, de validation personnalisable...
Pour gérer cela trois procédures stockées :
- sp_addextendedproperty
- sp_updateextendedproperty
- sp_dropextendedproperty
les 6 derniers paramètres de ces procédures, permettent d'indiquer où associer l'information.
Level0type = 'SCHEMA'
Level0Name = 'dbo'
Level1Type = 'TABLE'
Level1Name = 'utilisateur'
Level2Type = 'COLUMN'
Level2Name = 'util_nom'
==> Indique la colonne 'util_nom' de la table 'utilisateur' du schéma 'dbo'
Pour associer une propriété à une table : ne pas préciser le 'level2'
Pour associer une propriété à une vue, fonction, procédure ==> changer les infos dans le 'level1' en conséquence !
Pour associer une propriété à la base elle même : ne pas préciser de niveaux !
Comment on retrouve les infos ?
Avec SSMS : Dans la fenêtre propriétés de l'objet concerné il y a un onglet "propriétés étendues" qui les liste aisément.
Pour les interroger, SQL Server fournit une vue sys.fn_listextendedproperty censée remonter une ou plusieurs infos en fonction des filtre passés : j'ai pas réussi à l'utiliser dans les scénarios suivants : donne moi toutes les propriétés étendues, ou donne moi toutes les propriétés de toutes les tables...
... a creuser ?!??
Heureusement en cherchant un peu on trouve que c'est dans la table : sys.extended_properties que sont stockées les informations.
Voici donc 3 requêtes pour retrouver les informations associées à :
- La base de données :
SELECT name, value
FROM sys.extended_properties
WHERE major_id = 0 AND minor_id = 0 AND class = 0
- Une Table :
SELECT s.name AS [schema_name], o.name AS [table_name] , p.name, p.value
FROM sys.extended_properties p
INNER JOIN sys.tables t ON p.major_id = t.object_id AND p.minor_id = 0 AND p.class = 1
INNER JOIN sys.schemas s ON t.schema_id = s.schema_id
WHERE t.name = 'utilisateur'
- Toutes les colonnes d'une table :
SELECT s.name AS [schema_name], o.name AS [table_name], c.name AS [column_name], p.name, p.value
FROM sys.extended_properties p
INNER JOIN sys.tables t ON p.major_id = t.object_id
INNER JOIN sys.columns c ON c.object_id = t.object_id AND c.column_id = p.minor_id AND p.class = 1
INNER JOIN sys.schemas s ON t.schema_id = s.schema_id
WHERE t.name = 'utilisateur'
.. reste à adapter en fonction des besoins.
Enjoy
Add : Avec la suppression de l'objet de la base de données (Ex : suppression d'une colonne) les propriétés étendues sont elles aussi supprimées automatiquement (sans bloquer la suppression). Merci SQL Server !
19 juin 2018
SQL Server Ecrire un message pendant une procédure
Afin d'écrire un message durant l'exécution d'une requête longue on peut utiliser :
RAISERROR (@msg, 0, 1) WITH NOWAIT
Cette instruction déclenche un message de sévérité = 0 (autant dire pas grave du tout !)
et d'un statut de 1 (qui sera ignoré dans ce cas)
l'activateur NOWAIT fait le boulot attendu ==> afficher l'information immédiatement au client
ce que l'instruction PRINT permet pas !
Enjoy !
RAISERROR (@msg, 0, 1) WITH NOWAIT
Cette instruction déclenche un message de sévérité = 0 (autant dire pas grave du tout !)
et d'un statut de 1 (qui sera ignoré dans ce cas)
l'activateur NOWAIT fait le boulot attendu ==> afficher l'information immédiatement au client
ce que l'instruction PRINT permet pas !
Enjoy !
31 octobre 2017
Les tests en C#
Après quelques mise en place de tests unitaires sur des solutions, voici un compte rendu de mes points de réflexions sur la mise en place de les tests unitaire en c#
J'ai utilise les outils de base de visual studio
1) Une classe de test par classe testée : le nom de la classe de tests doit être d'une forme dérivée de la classe testée. Par exemple : Test[Nom de la classe testée]
Cela permet de s'y retrouver.
2) Une méthode de test (au moins) par méthode testée. Le nom de la méthode doit être d'une forme dérivée de la méthode testée. Par exemple : Test[Nom de la méthode testée]
Cela permet de s'y retrouver.
3) Chaque appel à Assert.xxx doit renvoyer un message personnalisé.
Le message doit être clair sur : la classe testée, la méthode testé, le contexte du test et ce qui n'a pas marché.
J'ai utilise les outils de base de visual studio
1) Une classe de test par classe testée : le nom de la classe de tests doit être d'une forme dérivée de la classe testée. Par exemple : Test[Nom de la classe testée]
Cela permet de s'y retrouver.
2) Une méthode de test (au moins) par méthode testée. Le nom de la méthode doit être d'une forme dérivée de la méthode testée. Par exemple : Test[Nom de la méthode testée]
Cela permet de s'y retrouver.
3) Chaque appel à Assert.xxx doit renvoyer un message personnalisé.
Le message doit être clair sur : la classe testée, la méthode testé, le contexte du test et ce qui n'a pas marché.
Inscription à :
Articles (Atom)