J'ai une table comme celle-ci:
+-------------------------+------+------+------+------+ | Date | A | B | C | D | +-------------------------+------+------+------+------+ | 2010-11-16 10:02:00.000 | 10 | 14 | 1250 | 17 | +-------------------------+------+------+------+------+
J'ai besoin d'une requête pour extraire la première valeur non nulle de chaque colonne. Une sorte de "coalescence verticale".
Le résultat souhaité serait:
+-------------------------+------+------+------+------+ | Date | A | B | C | D | +-------------------------+------+------+------+------+ | 2010-11-16 10:02:00.000 | 10 | NULL | NULL | NULL | | 2010-09-21 00:00:00.000 | 86 | 14 | NULL | 17 | | 2010-07-27 00:00:00.000 | 125 | 12 | NULL | 11 | | 2010-05-29 15:24:00.000 | NULL | NULL | 1250 | NULL | +-------------------------+------+------+------+------+
le tableau est trié par date (desc)
le tableau actuel a beaucoup plus de colonnes (40) et de lignes (jusqu'à 5000)
Modifier:
Ma table actuelle a beaucoup plus de colonnes et de lignes (environ 40 colonnes et jusqu'à 5000 lignes disons). Je crains que plusieurs requêtes triées ne détériorent les performances de la requête. Cependant, si aucune solution plus propre n'apparaît, je vais y aller.
3 Réponses :
Ceci est une approche possible, si votre ordre décroît de la colonne [Date]
:
Entrée:
;WITH DatesCTE AS ( SELECT [Date] = MAX([Date]), [DateA] = MAX(CASE WHEN A IS NOT NULL THEN [Date] END), [DateB] = MAX(CASE WHEN B IS NOT NULL THEN [Date] END), [DateC] = MAX(CASE WHEN C IS NOT NULL THEN [Date] END), [DateD] = MAX(CASE WHEN D IS NOT NULL THEN [Date] END) FROM #Table ) SELECT d.[Date], A = MAX(CASE WHEN t.[Date] = d.[DateA] THEN A END), B = MAX(CASE WHEN t.[Date] = d.[DateB] THEN B END), C = MAX(CASE WHEN t.[Date] = d.[DateC] THEN C END), D = MAX(CASE WHEN t.[Date] = d.[DateD] THEN D END) FROM DatesCTE d CROSS APPLY #Table t GROUP BY d.[Date]
Déclaration:
Date A B C D 2010-11-16 10:02:00.000 10 14 1250 17
Résultat:
SELECT [Date] = (SELECT TOP 1 [Date] FROM #Table WHERE [Date] IS NOT NULL ORDER BY [Date] DESC), [A] = (SELECT TOP 1 [A] FROM #Table WHERE [A] IS NOT NULL ORDER BY [Date] DESC), [B] = (SELECT TOP 1 [B] FROM #Table WHERE [B] IS NOT NULL ORDER BY [Date] DESC), [C] = (SELECT TOP 1 [C] FROM #Table WHERE [C] IS NOT NULL ORDER BY [Date] DESC), [D] = (SELECT TOP 1 [D] FROM #Table WHERE [D] IS NOT NULL ORDER BY [Date] DESC)
Mise à jour - une autre approche possible, utilisant des fonctions d'agrégation:
CREATE TABLE #Table ( [Date] datetime, A int, B int, C int, D int ) INSERT INTO #Table ([Date], A, B, C, D) VALUES ('2010-11-16T10:02:00.000', 10 , NULL, NULL, NULL), ('2010-09-21T00:00:00.000', 86 , 14 , NULL, 17 ), ('2010-07-27T00:00:00.000', 125 , 12 , NULL, 11 ), ('2010-05-29T15:24:00.000', NULL, NULL, 1250, NULL)
cela pourrait fonctionner, malheureusement ma table actuelle a beaucoup plus de colonnes et cette solution produira une requête vraiment moche, cependant, je l'envisage, merci (question modifiée)
@Jacopo J'ai mis à jour la réponse avec une autre approche utilisant des fonctions d'agrégation. Je vois, que Salman A utilise une approche similaire, il vous suffit de tester. Merci.
Vous pouvez essayer quelque chose comme celui-ci pour éviter les commandes. Je sais qu'il est assez fastidieux au début de définir 50 variables, mais vous n'aurez pas à vous inquiéter plus tard.
declare @A int = (select top 1 A from #Table where A is not null) declare @B int = (select top 1 B from #Table where B is not null) declare @C int = (select top 1 C from #Table where C is not null) declare @D int = (select top 1 D from #Table where D is not null) select top 1 Date,@A,@B,@C,@D from #Table
Je n'ai pas testé cela mais il semble assez méchant:
with cte as ( select max(case when a is not null then date end) as date_a , max(case when b is not null then date end) as date_b , max(case when c is not null then date end) as date_c , max(case when d is not null then date end) as date_d from t ) select max(date) as date , min(case when date = date_a then a end) as a , min(case when date = date_b then b end) as b , min(case when date = date_c then c end) as c , min(case when date = date_d then d end) as d from t cross join cte
Sans les fonctions de fenêtre:
with cte as ( select date , a, max(case when a is not null then date end) over () as date_a , b, max(case when b is not null then date end) over () as date_b , c, max(case when c is not null then date end) over () as date_c , d, max(case when d is not null then date end) over () as date_d from t ) select max(date) as date , min(case when date = date_a then a end) as a , min(case when date = date_b then b end) as b , min(case when date = date_c then c end) as c , min(case when date = date_d then d end) as d from cte