Lors d’un chargement massif de données et/ou d’une migration, il est possible que la qualité des données soient altérées. Il faut alors tester leur qualité et valider chaque écart. Parmi ceux-ci, on peut trouver les numéro SIRET, qui permet l’identification d’un établissement ou d’une entreprise Française.
Cet identifiant numérique est constitué de 14 chiffres, et est articulé en trois parties :
- le SIREN
- le Numéro Interne de Classement, à quatre chiffres : c’est l’ordre de création de l’établissement par l’entreprise Française
- une clé de contrôle, constituée d’un seul chiffre
Ce dernier est calculé à l’aide de l’algorithme de Luhn. Cet algorithme est aussi utilisé pour valider les numéros de carte bleue, et de nombreux identifiants distribués par les administrations de beaucoup de pays. Nous allons nous en servir pour vérifier la validité de SIRETs en l’implémentant spécifiquement pour ce type d’identifiant.
L’algorithme est descriptible comme suit :
- Pour chaque chiffre dont l’index dans la chaîne est impair, on le multiplie par 2; si le résultat est supérieur à 9, on additionne les chiffes qui constituent ce résultat. Ceci revient à multiplier par 2 le chiffre, diviser le résultat par 10, puis ajouter la multiplication par 2 de ce même chiffre modulo 10.
- On additionne ensuite les chiffres du résultat un à un, avec les chiffres de l’identifiant qui n’ont pas été traités, c’est à dire ceux dont l’index dans la chaîne est pair. Ceci inclut le chiffre de l’identifiant qui est clé.
- Si le résultat obtenu est un multiple de 10, alors l’identifiant est valide. Ceci revient à vérifier que le résultat modulo 10 vaut zéro.
La traduction en SQL donne :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | CASE WHEN laColonneSIRET IS NULL THEN NULL WHEN LEN(laColonneSIRET) < 14 THEN CAST(0 AS bit) WHEN laColonneSIRET LIKE'%[^0-9]%' THEN CAST(0 AS bit) WHEN ( 2 * CAST(SUBSTRING(laColonneSIRET, 1, 1) AS tinyint) / 10 + 2 * CAST(SUBSTRING(laColonneSIRET, 1, 1) AS tinyint) % 10 + 2 * CAST(SUBSTRING(laColonneSIRET, 3, 1) AS tinyint) / 10 + 2 * CAST(SUBSTRING(laColonneSIRET, 3, 1) AS tinyint) % 10 + 2 * CAST(SUBSTRING(laColonneSIRET, 5, 1) AS tinyint) / 10 + 2 * CAST(SUBSTRING(laColonneSIRET, 5, 1) AS tinyint) % 10 + 2 * CAST(SUBSTRING(laColonneSIRET, 7, 1) AS tinyint) / 10 + 2 * CAST(SUBSTRING(laColonneSIRET, 7, 1) AS tinyint) % 10 + 2 * CAST(SUBSTRING(laColonneSIRET, 9, 1) AS tinyint) / 10 + 2 * CAST(SUBSTRING(laColonneSIRET, 9, 1) AS tinyint) % 10 + 2 * CAST(SUBSTRING(laColonneSIRET, 11, 1) AS tinyint) / 10 + 2 * CAST(SUBSTRING(laColonneSIRET, 11, 1) AS tinyint) % 10 + 2 * CAST(SUBSTRING(laColonneSIRET, 13, 1) AS tinyint) / 10 + 2 * CAST(SUBSTRING(laColonneSIRET, 13, 1) AS tinyint) % 10 --- + CAST(SUBSTRING(laColonneSIRET, 2, 1) AS tinyint) + CAST(SUBSTRING(laColonneSIRET, 4, 1) AS tinyint) + CAST(SUBSTRING(laColonneSIRET, 6, 1) AS tinyint) + CAST(SUBSTRING(laColonneSIRET, 8, 1) AS tinyint) + CAST(SUBSTRING(laColonneSIRET, 10, 1) AS tinyint) + CAST(SUBSTRING(laColonneSIRET, 12, 1) AS tinyint) + CAST(SUBSTRING(laColonneSIRET, 14, 1) AS tinyint) ) % 10 = 0 THEN CAST(1 AS bit) ELSE CAST(0 AS bit) END AS is_valid_SIRET |
On peut l'utiliser directement dans une vue, comme dans le pseudo code suivant :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | CREATE VIEW maVue AS SELECT desColonnes , CASE WHEN laColonneSIRET IS NULL THEN NULL WHEN LEN(laColonneSIRET) < 14 THEN CAST(0 AS bit) WHEN laColonneSIRET LIKE'%[^0-9]%' THEN CAST(0 AS bit) WHEN ( 2 * CAST(SUBSTRING(laColonneSIRET, 1, 1) AS tinyint) / 10 + 2 * CAST(SUBSTRING(laColonneSIRET, 1, 1) AS tinyint) % 10 + 2 * CAST(SUBSTRING(laColonneSIRET, 3, 1) AS tinyint) / 10 + 2 * CAST(SUBSTRING(laColonneSIRET, 3, 1) AS tinyint) % 10 + 2 * CAST(SUBSTRING(laColonneSIRET, 5, 1) AS tinyint) / 10 + 2 * CAST(SUBSTRING(laColonneSIRET, 5, 1) AS tinyint) % 10 + 2 * CAST(SUBSTRING(laColonneSIRET, 7, 1) AS tinyint) / 10 + 2 * CAST(SUBSTRING(laColonneSIRET, 7, 1) AS tinyint) % 10 + 2 * CAST(SUBSTRING(laColonneSIRET, 9, 1) AS tinyint) / 10 + 2 * CAST(SUBSTRING(laColonneSIRET, 9, 1) AS tinyint) % 10 + 2 * CAST(SUBSTRING(laColonneSIRET, 11, 1) AS tinyint) / 10 + 2 * CAST(SUBSTRING(laColonneSIRET, 11, 1) AS tinyint) % 10 + 2 * CAST(SUBSTRING(laColonneSIRET, 13, 1) AS tinyint) / 10 + 2 * CAST(SUBSTRING(laColonneSIRET, 13, 1) AS tinyint) % 10 --- + CAST(SUBSTRING(laColonneSIRET, 2, 1) AS tinyint) + CAST(SUBSTRING(laColonneSIRET, 4, 1) AS tinyint) + CAST(SUBSTRING(laColonneSIRET, 6, 1) AS tinyint) + CAST(SUBSTRING(laColonneSIRET, 8, 1) AS tinyint) + CAST(SUBSTRING(laColonneSIRET, 10, 1) AS tinyint) + CAST(SUBSTRING(laColonneSIRET, 12, 1) AS tinyint) + CAST(SUBSTRING(laColonneSIRET, 14, 1) AS tinyint) ) % 10 = 0 THEN CAST(1 AS bit) ELSE CAST(0 AS bit) END AS is_valid_SIRET FROM dbo.maTable |
Si l'on souhaite l'utiliser en colonne calculée, on peut utiliser ce CASE directement dans la définition de ladite colonne, ou bien définir une fonction scalaire comme suit :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | CREATE FUNCTION fn_scl_is_valid_SIRET ( @_siret char(14) ) RETURNS bit WITH SCHEMABINDING AS BEGIN DECLARE @ok bit SELECT @ok = CASE WHEN @_siret IS NULL THEN NULL WHEN LEN(@_siret) < 14 THEN CAST(0 AS bit) WHEN @_siret LIKE'%[^0-9]%' THEN CAST(0 AS bit) WHEN ( 2 * CAST(SUBSTRING(@_siret, 1, 1) AS tinyint) / 10 + 2 * CAST(SUBSTRING(@_siret, 1, 1) AS tinyint) % 10 + 2 * CAST(SUBSTRING(@_siret, 3, 1) AS tinyint) / 10 + 2 * CAST(SUBSTRING(@_siret, 3, 1) AS tinyint) % 10 + 2 * CAST(SUBSTRING(@_siret, 5, 1) AS tinyint) / 10 + 2 * CAST(SUBSTRING(@_siret, 5, 1) AS tinyint) % 10 + 2 * CAST(SUBSTRING(@_siret, 7, 1) AS tinyint) / 10 + 2 * CAST(SUBSTRING(@_siret, 7, 1) AS tinyint) % 10 + 2 * CAST(SUBSTRING(@_siret, 9, 1) AS tinyint) / 10 + 2 * CAST(SUBSTRING(@_siret, 9, 1) AS tinyint) % 10 + 2 * CAST(SUBSTRING(@_siret, 11, 1) AS tinyint) / 10 + 2 * CAST(SUBSTRING(@_siret, 11, 1) AS tinyint) % 10 + 2 * CAST(SUBSTRING(@_siret, 13, 1) AS tinyint) / 10 + 2 * CAST(SUBSTRING(@_siret, 13, 1) AS tinyint) % 10 --- + CAST(SUBSTRING(@_siret, 2, 1) AS tinyint) + CAST(SUBSTRING(@_siret, 4, 1) AS tinyint) + CAST(SUBSTRING(@_siret, 6, 1) AS tinyint) + CAST(SUBSTRING(@_siret, 8, 1) AS tinyint) + CAST(SUBSTRING(@_siret, 10, 1) AS tinyint) + CAST(SUBSTRING(@_siret, 12, 1) AS tinyint) + CAST(SUBSTRING(@_siret, 14, 1) AS tinyint) ) % 10 = 0 THEN CAST(1 AS bit) ELSE CAST(0 AS bit) END RETURN @ok END |
Et en ce qui concerne la création de la colonne calculée, on peut l'ajouter à l'aide du pseudo-T-SQL suivant :
1 2 | ALTER TABLE maTable ADD is_valid_SIRET AS(dbo.fn_scl_is_valid_SIRET(laColonneSIRET)) |
Enfin, si l'on souhaite exécuter cette vérification au sein d'une requête, on peut s'en remettre à une fonction de table en ligne. Ce type de fonction n'étant appelé qu'une seule fois, il s'avérera plus performant à l'exécution sur un grand nombre de lignes que l'appel à une fonction scalaire : ces dernières sont appelées autant de fois qu'il y a de lignes de résultat. Ce qui donne :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | CREATE FUNCTION fn_it_is_valid_SIRET ( @siret char(14) ) RETURNS TABLE WITH SCHEMABINDING AS RETURN ( SELECT CASE WHEN LEN(COALESCE(@siret,'')) < 14 THEN CAST(0 AS bit) WHEN @siret LIKE'%[^0-9]%' THEN CAST(0 AS bit) WHEN @siret IS NULL THEN CAST(0 AS bit) WHEN ( 2 * CAST(SUBSTRING(@siret, 1, 1) AS tinyint) / 10 + 2 * CAST(SUBSTRING(@siret, 1, 1) AS tinyint) % 10 + 2 * CAST(SUBSTRING(@siret, 3, 1) AS tinyint) / 10 + 2 * CAST(SUBSTRING(@siret, 3, 1) AS tinyint) % 10 + 2 * CAST(SUBSTRING(@siret, 5, 1) AS tinyint) / 10 + 2 * CAST(SUBSTRING(@siret, 5, 1) AS tinyint) % 10 + 2 * CAST(SUBSTRING(@siret, 7, 1) AS tinyint) / 10 + 2 * CAST(SUBSTRING(@siret, 7, 1) AS tinyint) % 10 + 2 * CAST(SUBSTRING(@siret, 9, 1) AS tinyint) / 10 + 2 * CAST(SUBSTRING(@siret, 9, 1) AS tinyint) % 10 + 2 * CAST(SUBSTRING(@siret, 11, 1) AS tinyint) / 10 + 2 * CAST(SUBSTRING(@siret, 11, 1) AS tinyint) % 10 + 2 * CAST(SUBSTRING(@siret, 13, 1) AS tinyint) / 10 + 2 * CAST(SUBSTRING(@siret, 13, 1) AS tinyint) % 10 --- + CAST(SUBSTRING(@siret, 2, 1) AS tinyint) + CAST(SUBSTRING(@siret, 4, 1) AS tinyint) + CAST(SUBSTRING(@siret, 6, 1) AS tinyint) + CAST(SUBSTRING(@siret, 8, 1) AS tinyint) + CAST(SUBSTRING(@siret, 10, 1) AS tinyint) + CAST(SUBSTRING(@siret, 12, 1) AS tinyint) + CAST(SUBSTRING(@siret, 14, 1) AS tinyint) ) % 10 = 0 THEN CAST(1 AS bit) ELSE CAST(0 AS bit) END AS is_valid_SIRET ) |
On peut alors l'utiliser à l'aide de l'opérateur APPLY, comme dans l'exemple suivant :
1 2 3 4 | SELECT S.* , F.is_valid_SIRET FROM dbo.maTableAvecUneColonneSIRET AS S CROSS APPLY dbo.fn_it_is_valid_SIRET(S.laColonneSIRET) AS F |
Bonne validation de SIRETs !