SQL Server 2016を使用していますが、使用しているデータは次の形式です。
CREATE TABLE #tab (cat CHAR(1), t CHAR(2), val1 INT, val2 CHAR(1));
INSERT INTO #tab VALUES
('A','Q1',2,NULL),('A','Q2',NULL,'P'),('A','Q3',1,NULL),('A','Q3',NULL,NULL),
('B','Q1',5,NULL),('B','Q2',NULL,'P'),('B','Q3',NULL,'C'),('B','Q3',10,NULL);
SELECT *
FROM #tab;
列の最後のnull以外の値を取得したいval1
およびval2
はcat
でグループ化され、t
で並べ替えられます。私が求めている結果は
cat val1 val2 A 1 P B 10 C
私が来た最も近いものはLAST_VALUE
を無視しながらORDER BY
これは、順序付けされた最後のnull以外の値が必要なため、機能しません。
SELECT DISTINCT
cat,
LAST_VALUE(val1) OVER(PARTITION BY cat ORDER BY (SELECT NULL) ) AS val1,
LAST_VALUE(val2) OVER(PARTITION BY cat ORDER BY (SELECT NULL) ) AS val2
FROM #tab
cat val1 val2 A NULL NULL B 10 NULL
実際のテーブルには、最後のnull以外の値を選択するために、cat
の列(日付列と文字列列)とval列(日付列、文字列列、数値列)があります。
この選択を行う方法のアイデア。
Itzik Ben Ganによる The Last non NULL Puzzle の連結手法を使用すると、サンプルのテーブルと列のデータ型では次のようになります。
select T.cat,
cast(substring(
max(cast(T.t as binary(2)) + cast(T.val1 as binary(4))),
3,
4
) as int),
cast(substring(
max(cast(T.t as binary(2)) + cast(T.val2 as binary(1))),
3,
1
) as char(1))
from #tab as T
group by T.cat;
ステップをCTEに分割するこのクエリを記述する別の方法は、おそらく何が起こっているかをよりよく示すためです。上記のクエリとまったく同じ実行プランを提供します。
with C1 as
(
-- Concatenate the ordering column with the value column
select T.cat,
cast(T.t as binary(2)) + cast(T.val1 as binary(4)) as val1,
cast(T.t as binary(2)) + cast(T.val2 as binary(1)) as val2
from #tab as T
),
C2 as
(
-- Get the max concatenated value per group
select C1.cat,
max(C1.val1) as val1,
max(C1.val2) as val2
from C1
group by C1.cat
)
-- Extract the value from the concatenated column
select C2.cat,
cast(substring(C2.val1, 3, 4) as int) as val1,
cast(substring(C2.val2, 3, 1) as char(1)) as val2
from C2;
このソリューションでは、null値を何かと連結するとnull値になるという事実を利用しています。 SET CONCAT_NULL_YIELDS_NULL(Transact-SQL)
パーティションにNULLのチェックを追加するだけで、
SELECT DISTINCT
cat,
FIRST_VALUE(val1) OVER(PARTITION BY cat ORDER BY CASE WHEN val1 is NULL then 0 else 1 END DESC, t desc) AS val1,
FIRST_VALUE(val2) OVER(PARTITION BY cat ORDER BY CASE WHEN val2 is NULL then 0 else 1 END DESC, t desc) AS val2
FROM #tab
これでうまくいくはずです。 row_number()と結合
適切な種類がない場合は、Q3の1つだけがnullでないことを期待する必要があります。
declare @t TABLE (cat CHAR(1), t CHAR(2), val1 INT, val2 CHAR(1));
INSERT INTO @t VALUES
('A','Q1',2,NULL),('A','Q2',NULL,'P'),('A','Q3',1,NULL),('A','Q3',NULL,NULL),
('B','Q1',5,NULL),('B','Q2',NULL,'P'),('B','Q3',NULL,'C'),('B','Q3',10,NULL);
--SELECT *
-- , row_number() over (partition by cat order by t) as rn
--FROM @t
--where val1 is not null or val2 is not null;
select t1.cat, t1.val1, t2.val2
from ( SELECT t.cat, t.val1
, row_number() over (partition by cat order by t desc) as rn
FROM @t t
where val1 is not null
) t1
join ( SELECT t.cat, t.val2
, row_number() over (partition by cat order by t desc) as rn
FROM @t t
where val2 is not null
) t2
on t1.cat = t2.cat
and t1.rn = 1
and t2.rn = 1