L'un de ces scénarios critiques survient lors de jointures impliquant des conditions complexes, notamment l'utilisation de OR combinée à des transformations de variables à la volée dans la clause ON.
Cet article explore comment transformer une requête SQL inefficace en une étape Data ultra-rapide grâce à l'utilisation des Tables de Hachage (Hash Objects).
Imaginons le scénario suivant, fréquent en traitement de données bancaires ou administratives :
Vous devez joindre deux tables de taille moyenne (par exemple, 500 000 lignes et 300 000 lignes). Le défi réside dans le fait que les clés de jointure ne sont pas directes. Les identifiants sont stockés sous forme numérique, et la correspondance nécessite de convertir ces nombres en chaînes de caractères, puis d'extraire des sous-chaînes spécifiques. Pire encore, la condition de jointure contient un OR, signifiant qu'une correspondance peut survenir de deux manières différentes.
Voici à quoi ressemble le goulot d'étranglement en SQL :
PROC SQL noprint;
CREATE TABLE Resultat AS
SELECT DISTINCT
T1.ID_Parent_L,
T2.CodeCedant,
T2.ID_Client,
T1.Nom
FROM TABLE_PRINCIPALE T1
INNER JOIN TABLE_SECONDAIRE T2
/* La source du problème : conversions et condition OR */
ON (substr(put(T1.ID_Parent_L,14.),7,8) = substr(put(T2.ID_Client,14.),1,8)
OR substr(put(T1.ID_Parent_L,14.),1,8) = substr(put(T2.ID_Client,14.),1,8))
;
QUIT;
1
PROC SQL noprint;
2
CREATE TABLE Resultat AS
3
SELECT DISTINCT
4
T1.ID_Parent_L,
5
T2.CodeCedant,
6
T2.ID_Client,
7
T1.Nom
8
FROM TABLE_PRINCIPALE T1
9
INNER JOIN TABLE_SECONDAIRE T2
10
/* La source du problème : conversions et condition OR */
11
ON (substr(put(T1.ID_Parent_L,14.),7,8) = substr(put(T2.ID_Client,14.),1,8)
12
OR substr(put(T1.ID_Parent_L,14.),1,8) = substr(put(T2.ID_Client,14.),1,8))
La clause OR : L'utilisation de OR dans une jointure empêche souvent l'optimiseur SQL d'utiliser efficacement les index. Cela force souvent le système à effectuer un "produit cartésien" partiel ou total, comparant chaque ligne de la table T1 avec une grande partie des lignes de T2.
Absence de clés uniques : Si les clés de jointure ne sont pas uniques dans les deux tables, la complexité augmente exponentiellement.
Dans ce genre de situation, une jointure MERGE classique en étape Data est également difficile à mettre en œuvre car elle nécessite un tri préalable sur des clés transformées. C'est ici qu'interviennent les objets de hachage.
La Solution : L'étape Data et les Hash Objects
Les objets de hachage (Hash Objects) permettent de charger une table entière (généralement la plus petite des deux) en mémoire vive (RAM). Cela crée une structure de recherche ultra-rapide où l'on peut trouver une correspondance instantanément sans avoir besoin de trier les données.
La stratégie consiste à :
Préparer la table de référence (la plus petite) en créant une clé de recherche propre.
Charger cette table dans un objet de hachage en mémoire.
Lire la grande table ligne par ligne, calculer la clé de recherche à la volée, et interroger l'objet de hachage.
Étape 1 : Préparation des données de référence
Au lieu de faire des calculs complexes dans la jointure, nous préparons la table secondaire (TABLE_SECONDAIRE) pour qu'elle contienne une clé de recherche (KEY) prête à l'emploi correspondant à la partie de l'identifiant que nous cherchons.
PROC SQL;
CREATE TABLE HashData AS
SELECT UNIQUE
CodeCedant,
ID_Client,
/* Création de la clé de jointure standardisée */
put(put(ID_Client,14.),$8.) AS KEY
FROM TABLE_SECONDAIRE;
QUIT;
Voici le code qui remplace la jointure SQL complexe.
DATA Resultat_Hash;
/* On lit la table principale ligne par ligne */
SET TABLE_PRINCIPALE (keep=ID_Parent_L Nom);
/* Initialisation de l'objet de hachage (une seule fois au début) */
if _N_=1 then do;
/* Déclaration : on charge la table préparée en mémoire */
dcl hash H(dataset:'HashData');
/* Définition de la clé de recherche */
H.definekey('KEY');
/* Définition des données à récupérer en cas de succès */
H.definedata('CodeCedant','ID_Client');
H.definedone();
/* Astuce pour éviter de lire HashData ici, mais initialiser les variables */
if 0 then set HashData;
end;
/* --- Cœur du traitement --- */
/* Tentative 1 : On calcule la clé potentielle selon la première logique */
RC = H.find(key: put(put(ID_Parent_L,14.),$8.));
/* Si RC (Code Retour) = 0, la recherche a réussi.
Les variables CodeCedant et ID_Client sont remplies automatiquement. */
if RC=0 then output;
else do;
/* Optionnel : Si votre logique SQL initiale avait un "OR",
vous pouvez tenter une deuxième recherche ici si la première échoue.
Exemple :
RC = H.find(key: substr(put(ID_Parent_L,14.),7,8));
if RC=0 then output;
*/
end;
drop RC;
run;
1
DATA Resultat_Hash;
2
/* On lit la table principale ligne par ligne */
3
SET TABLE_PRINCIPALE (keep=ID_Parent_L Nom);
4
5
/* Initialisation de l'objet de hachage (une seule fois au début) */
6
IF _N_=1THENDO;
7
/* Déclaration : on charge la table préparée en mémoire */
8
dcl hash H(dataset:'HashData');
9
/* Définition de la clé de recherche */
10
H.definekey('KEY');
11
/* Définition des données à récupérer en cas de succès */
12
H.definedata('CodeCedant','ID_Client');
13
H.definedone();
14
/* Astuce pour éviter de lire HashData ici, mais initialiser les variables */
15
IF 0 THENSET HashData;
16
END;
17
18
/* --- Cœur du traitement --- */
19
20
/* Tentative 1 : On calcule la clé potentielle selon la première logique */
21
RC = H.find(key: put(put(ID_Parent_L,14.),$8.));
22
23
/* Si RC (Code Retour) = 0, la recherche a réussi.
24
Les variables CodeCedant et ID_Client sont remplies automatiquement. */
25
IF RC=0 THENOUTPUT;
26
ELSEDO;
27
/* Optionnel : Si votre logique SQL initiale avait un "OR",
28
vous pouvez tenter une deuxième recherche ici si la première échoue.
Le gain de performance lors du passage d'une jointure SQL complexe avec des OR et des conversions vers une solution basée sur des Hash Objects est souvent spectaculaire. Des traitements qui prenaient des heures ou plantaient le serveur peuvent s'exécuter en quelques secondes.
The codes and examples provided on WeAreCAS.eu are for educational purposes. It is imperative not to blindly copy-paste them into your production environments. The best approach is to understand the logic before applying it. We strongly recommend testing these scripts in a test environment (Sandbox/Dev). WeAreCAS accepts no responsibility for any impact or data loss on your systems.
SAS and all other SAS Institute Inc. product or service names are registered trademarks or trademarks of SAS Institute Inc. in the USA and other countries. ® indicates USA registration. WeAreCAS is an independent community site and is not affiliated with SAS Institute Inc.
This site uses technical and analytical cookies to improve your experience.
Read more.