SAS9

Comment croiser deux tables sans clé commune

Simon 7 vistas

Il arrive souvent en programmation SAS© que l'on doive croiser deux jeux de données. Habituellement, nous utilisons une instruction MERGE dans une étape DATA ou une jointure LEFT/INNER JOIN en SQL via une clé commune (par exemple, un ID client).

Mais comment faire lorsque les deux tables n'ont aucune variable en commun et que la jointure dépend de conditions complexes, comme des valeurs comprises dans des intervalles ? C'est ce qu'on appelle une jointure non équitable (Non-Equi Join).

Examinons un cas pratique issu d'une discussion entre utilisateurs pour comprendre comment résoudre ce problème élégamment avec PROC SQL.

La fausse bonne idée : La boucle dans l'étape DATA

Face à ce problème, la première intuition est souvent de tenter une approche itérative via une étape DATA. L'idée serait de lire une ligne de la table accounts, puis de boucler sur toute la table intervals pour trouver une correspondance.

Bien que techniquement possible, cette approche est complexe à coder en SAS© :

  • Elle nécessite de manipuler deux pointeurs de lecture.

  • Il faut gérer la réinitialisation de la lecture de la table intervals pour chaque compte (ce qui peut nécessiter l'option POINT= ou de multiples instructions SET).

  • Le code devient rapidement lourd et difficile à maintenir.

Comme le souligne un contributeur dans la discussion : "Je ne perdrais pas de temps sur une méthodologie de boucle."

La Solution Élégante : PROC SQL et le Produit Cartésien


La solution la plus efficace et la plus lisible réside dans l'utilisation de la PROC SQL. Contrairement à l'étape DATA, le SQL est conçu pour gérer naturellement les relations complexes entre les ensembles sans se soucier de l'ordre de tri ou de boucles explicites.

L'astuce consiste à effectuer une jointure cartésienne (toutes les lignes de A combinées à toutes les lignes de B) et à filtrer immédiatement le résultat.


Note :
Voici commen résoudre le problème en quelques lignes :
1PROC SQL;
2 CREATE TABLE DATA3 AS
3 SELECT
4 t1.grpnm,
5 t2.acct,
6 t2.bal1,
7 t2.bal2
8 FROM
9 intervals t1, /* Table 1 */
10 accounts t2 /* Table 2 */
11 WHERE
12 /* Condition pour le premier solde */
13 t2.bal1 BETWEEN t1.min1 AND t1.max1
14 AND
15 /* Condition pour le second solde */
16 t2.bal2 BETWEEN t1.min2 AND t1.max2;
17QUIT;
Pourquoi ça marche ?
FROM data1, data2 : En listant les deux tables séparées par une virgule sans clause JOIN explicite (comme LEFT JOIN), SAS© prépare implicitement un produit cartésien. Il compare virtuellement chaque ligne de la table intervals avec chaque ligne de la table accounts.

WHERE ... BETWEEN : C'est ici que la magie opère. Au lieu d'utiliser une égalité (ON t1.id = t2.id), nous utilisons des opérateurs logiques (BETWEEN, >, <). Le SQL ne retient que les combinaisons où les chiffres "tombent" dans les bonnes cases.

Si vous vous retrouvez bloqué à essayer de faire une boucle complexe dans une étape DATA parce que vous n'avez pas de clé commune : pensez SQL.

Ne vous laissez pas enfermer dans l'idée qu'une jointure nécessite une colonne identique. Tant que vous pouvez définir une règle logique (comme "être compris entre X et Y"), la PROC SQL peut faire le travail de manière beaucoup plus concise.