SAS → Noria
Philosophie
Le code SAS se réécrit principalement en SQL pur dans Noria. Les DATA step deviennent des SELECT, les PROC ont leurs équivalents SQL directs. Les macro-variables SAS (¶m) deviennent des paramètres Jinja2 ({{ params.nom }}).
Correspondances
Gestion des bibliothèques
| SAS |
Noria |
LIBNAME dwh ... |
{{ params.schema }} dans le SQL |
dwh.ma_table |
{{ params.schema }}.ma_table |
WORK.temp |
Table temporaire sans target: (SELECT only) |
DATA step
| SAS |
Noria (SQL) |
DATA out; SET source; |
SELECT * FROM source |
WHERE condition; |
WHERE condition |
KEEP col1 col2; |
SELECT col1, col2 |
DROP col; |
Lister les colonnes sans col |
RENAME old=new; |
col AS new |
IF ... THEN ...; |
CASE WHEN ... THEN ... END |
IF ... THEN DELETE; |
WHERE NOT (condition) |
new_col = expr; |
expr AS new_col dans SELECT |
FORMAT col DDMMYY10.; |
STRFTIME(col, '%d/%m/%Y') (DuckDB) |
RETAIN cumul 0; cumul + val; |
SUM(val) OVER (ORDER BY ...) |
BY group; FIRST.group; |
ROW_NUMBER() OVER (PARTITION BY group ORDER BY ...) |
LAG(col) |
LAG(col) OVER (ORDER BY ...) |
PROC SQL
PROC SQL se réécrit directement — la syntaxe est très proche. Les principales différences :
| SAS PROC SQL |
Noria |
INTO :var (macro variable) |
{{ params.nom }} (résolu avant exécution) |
CONNECT TO (pass-through) |
Connexion native via database: |
%SYSFUNC dans le SQL |
Macro Jinja2 |
PROC utilitaires
| SAS |
Noria |
PROC SORT DATA=t BY col; |
SELECT * FROM t ORDER BY col |
PROC MEANS DATA=t; |
SELECT AVG(col), MIN(col), MAX(col) FROM t GROUP BY ... |
PROC FREQ DATA=t; TABLES col; |
SELECT col, COUNT(*) AS n FROM t GROUP BY col ORDER BY n DESC |
PROC SUMMARY NWAY; |
SELECT ... FROM t GROUP BY ... |
PROC TRANSPOSE DATA=t; |
UNPIVOT (DuckDB) |
PROC APPEND BASE=b DATA=d; |
output_write: append |
PROC DELETE DATA=t; |
output_write: replace (recrée la table) |
PROC PRINT; |
noria compile --task nom (voir le SQL) |
Macro SAS
| SAS |
Noria |
%LET param = valeur; |
params: {param: valeur} dans noria.yml |
¶m |
{{ params.param }} |
%GLOBAL param; |
params: au niveau pipeline |
%MACRO nom; ... %MEND; |
Macro Jinja2 dans sql/macros/nom.sql.j2 |
%nom(args) |
{{ nom(args) }} dans le SQL |
%DO %TO %END; |
loop: (liste statique) ou loop_from_query: |
%INCLUDE 'fichier.sas'; |
{% include 'fichier.sql' %} Jinja2 |
%IF &env = PROD %THEN ...; |
{% if params.env == 'prod' %} ... {% endif %} |
Exemple complet
Code SAS (DATA step + PROC SQL)
%LET schema = DWH;
%LET annee = 2024;
/* Étape 1 : sélection et calcul */
PROC SQL;
CREATE TABLE WORK.commandes_calc AS
SELECT
client_id,
MONTH(date_commande) AS mois,
montant_ht,
montant_ht * 1.20 AS montant_ttc
FROM &schema..commandes
WHERE YEAR(date_commande) = &annee
AND statut = 'V';
QUIT;
/* Étape 2 : agrégation */
PROC SQL;
CREATE TABLE &schema..stats_client AS
SELECT
client_id,
mois,
SUM(montant_ttc) AS ca_ttc,
COUNT(*) AS nb_commandes
FROM WORK.commandes_calc
GROUP BY client_id, mois;
QUIT;
Réécriture Noria
params:
schema: DWH
annee: 2024
nodes:
commandes_calc:
sql: sql/commandes_calc.sql
target: "{{ params.schema }}.commandes_calc"
output_write: replace
description: "Commandes validées avec montant TTC — {{ params.annee }}"
stats_client:
sql: sql/stats_client.sql
target: "{{ params.schema }}.stats_client"
output_write: replace
description: "Statistiques mensuelles par client"
needs: [commandes_calc]
-- sql/commandes_calc.sql
SELECT
client_id,
EXTRACT(MONTH FROM date_commande) AS mois,
ROUND(montant_ht, 2) AS montant_ht,
ROUND(montant_ht * 1.20, 2) AS montant_ttc
FROM {{ params.schema }}.commandes
WHERE EXTRACT(YEAR FROM date_commande) = {{ params.annee }}
AND statut = 'V'
-- sql/stats_client.sql
SELECT
client_id,
mois,
ROUND(SUM(montant_ttc), 2) AS ca_ttc,
COUNT(*) AS nb_commandes
FROM {{ params.schema }}.commandes_calc
GROUP BY client_id, mois
ORDER BY client_id, mois
Note : Noria exécute le pipeline en une seule connexion. La table intermédiaire commandes_calc peut rester en base (elle sera recréée à chaque run grâce à output_write: replace). Si tu veux éviter de la persister, tu peux fusionner les deux étapes en un seul SQL avec une CTE.