Derniers tips / Mémos
Modélise nativement « exactement l'un de ces types », avec un switch exhaustif vérifié par le compilateur.
Fin des state machines générées par le compilateur : moins d'allocations et des stack traces enfin lisibles.
Recherche sémantique directement dans SQL Server 2025, sans base vectorielle séparée, en LINQ typé.
Framework UI cross-platform pour .NET : Windows, macOS, Linux, iOS, Android et WebAssembly.
Découvrez les nouvelles APIs asynchrones pour la compression ZIP et les améliorations de performance dans .NET 10
CROSS APPLY et OUTER APPLY en T‑SQL
APPLY est un join latéral : pour chaque ligne de la table de gauche, on exécute une expression qui renvoie une table (fonction table, OPENJSON, STRING_SPLIT, sous‑requête corrélée). CROSS APPLY se comporte comme un inner join (exclut les lignes sans résultat), tandis qu’OUTER APPLY conserve la ligne de gauche même si l’expression ne renvoie rien.
Cas d’usage
Dépivoter un CSV par ligne (STRING_SPLIT) avec CROSS APPLY
Quand la fonction renvoie 0 ligne (NULL/chaîne vide), la ligne de gauche disparaît — pratique pour filtrer les entrées vides.
-- Sample data
CREATE TABLE dbo.Customers (
CustomerId INT PRIMARY KEY,
Name NVARCHAR(50),
TagsCsv NVARCHAR(200)
);
INSERT INTO dbo.Customers (CustomerId, Name, TagsCsv) VALUES
(1, 'Alice', 'vip,tech'),
(2, 'Bob', NULL),
(3, 'Cara', '');
-- Join each row to a TVF result: split a CSV column into rows per customer
-- CROSS APPLY behaves like an inner join (filters out NULL/empty results)
SELECT
C.CustomerId,
C.Name,
TAG.value AS Tag
FROM dbo.Customers AS C
CROSS APPLY STRING_SPLIT(C.TagsCsv, ',') AS TAG;Résultat attendu
CustomerId Name Tag
---------- ----- -----
1 Alice vip
1 Alice techSélectionner le TOP 1 par groupe via OUTER APPLY
Calculer par ligne une sous‑requête ordonnée (ex: l’article le plus commandé) tout en conservant les lignes sans détails.
-- Sample data
CREATE TABLE dbo.Orders (
OrderId INT PRIMARY KEY,
CustomerId INT
);
CREATE TABLE dbo.Products (
Id INT PRIMARY KEY,
Name NVARCHAR(50)
);
CREATE TABLE dbo.OrderLines (
OrderId INT,
ProductId INT,
Quantity INT
);
INSERT INTO dbo.Orders (OrderId, CustomerId) VALUES (101, 1), (102, 2);
INSERT INTO dbo.Products (Id, Name) VALUES (10, 'Keyboard'), (20, 'Mouse');
INSERT INTO dbo.OrderLines (OrderId, ProductId, Quantity) VALUES
(101, 10, 1),
(101, 20, 3);
-- Order 102 has no lines
-- Keep outer rows even when the function returns no rows
SELECT
O.OrderId,
O.CustomerId,
X.TopItemId,
X.TopItemName,
X.Qty
FROM dbo.Orders AS O
OUTER APPLY (
SELECT TOP (1)
OL.ProductId AS TopItemId,
P.Name AS TopItemName,
OL.Quantity AS Qty
FROM dbo.OrderLines AS OL
JOIN dbo.Products AS P ON P.Id = OL.ProductId
WHERE OL.OrderId = O.OrderId
ORDER BY OL.Quantity DESC
) AS X;Résultat attendu
OrderId CustomerId TopItemId TopItemName Qty
------- ---------- --------- ----------- ---
101 1 20 Mouse 3
102 2 NULL NULL NULLParser du JSON par ligne avec OPENJSON
Analyser un payload JSON stocké en colonne et l’exploser en (clé, valeur) sans perdre les enregistrements mal formés (OUTER APPLY).
-- Sample data
CREATE TABLE dbo.RawEvents (
Id INT PRIMARY KEY,
Payload NVARCHAR(MAX)
);
INSERT INTO dbo.RawEvents(Id, Payload) VALUES
(1, '{"country":"FR","score":42}'),
(2, NULL);
-- Parse JSON per row; OUTER APPLY to keep rows with invalid/missing JSON
SELECT
T.Id,
J.[key] AS Prop,
J.value AS Val
FROM dbo.RawEvents AS T
OUTER APPLY OPENJSON(T.Payload) AS J;Résultat attendu
Id Prop Val
-- ------- ---
1 country FR
1 score 42
2 NULL NULLAlternatives
Fonctions de fenêtre (OVER / PARTITION BY)
Pour le TOP 1 par groupe, on peut utiliser ROW_NUMBER() sur une CTE (ou table dérivée) partitionnée par la clé et filtrer WHERE rn = 1.
Avantages
- Souvent très performantes et lisibles pour les classements par groupe
- Contrôle précis avec ROW_NUMBER(), RANK(), FIRST_VALUE(), etc.
Inconvénients
- Nécessite une étape supplémentaire (CTE/table dérivée) puis un filtrage
- Moins naturel pour appeler une fonction table dépendante de chaque ligne (ex. OPENJSON/STRING_SPLIT)
CTE / tables dérivées
On peut encapsuler des sous‑requêtes corrélées ou des calculs intermédiaires dans une CTE et les rejoindre.
Avantages
- Structure claire pour des transformations multi‑étapes
- Facile à tester et à commenter par bloc
Inconvénients
- Peut être plus verbeux que APPLY quand on a besoin d’une expansion par ligne
- La lisibilité baisse si plusieurs CTE s’empilent
JOINs avec agrégations / sous‑requêtes
Pour récupérer une ligne par groupe, un JOIN sur une sous‑requête agrégée (ou une table dérivée munie d’un ROW_NUMBER()) est une alternative classique.
Avantages
- Approche standard, facile à optimiser avec des index
- Fonctionne sans fonctions table ni OPENJSON
Inconvénients
- Moins flexible pour les expansions par ligne (unpivot) qu’avec APPLY
- Manipuler les jeux de colonnes scalaires issus d’agrégations peut être moins direct
Résumé
Utilisez APPLY quand vous devez dériver des lignes/colonnes par ligne source via une expression tabulaire corrélée (fonction table inline, OPENJSON, STRING_SPLIT). Choisir CROSS vs OUTER selon le besoin de filtrer ou non les résultats vides.
Avantages
- Expressif pour les expansions par ligne (dépivotage, parsing, top N par groupe)
- Combinable avec ORDER BY/TOP pour des sélections scalaires par ligne
- Alternative performante aux CTE complexes selon le cas
Inconvénients
- Attention aux fonctions table multi‑instructions (moins optimisées que les fonctions table inline)
- Peut dégrader les perfs si l’expression est lourde (évaluer le plan)
- Fonctionnalités comme OPENJSON nécessitent SQL Server 2016+
Bonnes pratiques
- Privilégiez les fonctions table inline (une seule SELECT) pour aider l’optimiseur
- Filtrez le plus tôt possible (WHERE dans l’expression APPLY)
- Ajoutez les index utiles sur les colonnes corrélées
- Mesurez le plan d’exécution et comparez à des alternatives (JOIN, CTE)