テーブルはこんな感じ
ID A1 A2 A3 A4 A5 A6 A7 A8 A9 1 イェ イェ イェ ヌル イェ イェ イェ ヌル ヌル 2 はい はい はい NULL NULL NULL NULL NULL NULL 3 イェイェイェイェイェイェイェイェイェヌル
ID は主キーです。
行の最後にnullでない値の列名を取得したい、結果はこのようなものです
IDラスト 1 A7 2 A3 3 A8
これについて何か助けはありますか?
テーブルはこんな感じ
ID A1 A2 A3 A4 A5 A6 A7 A8 A9 1 イェ イェ イェ ヌル イェ イェ イェ ヌル ヌル 2 はい はい はい NULL NULL NULL NULL NULL NULL 3 イェイェイェイェイェイェイェイェイェヌル
ID は主キーです。
行の最後にnullでない値の列名を取得したい、結果はこのようなものです
IDラスト 1 A7 2 A3 3 A8
これについて何か助けはありますか?
これはどう?を使用しUNPIVOT
てデータを変換してから、null/空白ではない最後の最大値を選択します。
;with cte as
(
select id
, last
, value
, row_number() over(partition by id order by last) rn
from
(
select id,
isnull(a1, '') as a1,
isnull(a2, '') as a2,
isnull(a3, '') as a3,
isnull(a4, '') as a4,
isnull(a5, '') as a5,
isnull(a6, '') as a6,
isnull(a7, '') as a7,
isnull(a8, '') as a8,
isnull(a9, '') as a9
from t
) x
unpivot
(
value
for last in (a1, a2, a3, a4, a5, a6, a7, a8, a9)
) u
)
select id, max(last) as last
from cte
where value != ''
group by id
SQL FiddlewithDemoを参照してください
編集、実際にはそれほど複雑である必要はありません:
select id
, max(last) last
from
(
select id, a1, a2, a3, a4, a5, a6, a7, a8, a9
from t
) x
unpivot
(
value
for last in (a1, a2, a3, a4, a5, a6, a7, a8, a9)
) u
group by id
SQL FiddlewithDemoを参照してください
このスキーマについての私の不安にもかかわらず、この「逆優先度」を条件付きと考えてください。
select
id,
case
-- first match terminates search
when A9 is not null then 'A9'
when A8 is not null then 'A8'
when A7 is not null then 'A7'
..
else null
as lastNonNullColumn
from ..
評価の順序はTSQLで保証されているため(CASEを参照)、逆方向にインチワームします:)
指定された順序で、各WHEN句のBoolean_expressionを評価します。
また、おそらくUNPIVOT
(またはROLLUP
[?]またはmanual UNION
)を使用することもできます。つまり、列名の固定セットを値にピボットすると、単純なクエリになります。つまり、テーブルが正規化されている場合、これは簡単に実行できます:-)
select
id,
max(colName) as lastNonNullColumn
from <<normalized_derived_table>>
where colValue is not null
group by id
これは、列の順序を指定できる疑似 UNPIVOT バージョンです (列名が位置でソートされない場合)。
SELECT
T.ID,
X.Name
FROM
T
CROSS APPLY (
SELECT TOP 1 Name FROM (
VALUES (1, 'A1', T.A1), (2, 'A2', T.A2), (3, 'A3', T.A3), (4, 'A4', T.A4),
(5, 'A5', T.A5), (6, 'A6', T.A6), (7, 'A7', T.A7), (8, 'A8', T.A8),
(9, 'A9', T.A9)
) X (Pos, Name, Col)
WHERE Col IS NOT NULL
ORDER BY X.Pos DESC
) X;
ただし、実際の IO と CPU は自然な UNPIVOT 方法よりもそれほど悪くはありませんが (実行計画は悪く見えますが、実際のサーバーへの影響はそれほど悪くはありません)、これは最高のパフォーマーではありません。@pst によって与えられた単純な CASE 式は次のとおりです。
列名をそのままソートできると仮定すると、UNPIVOT はさらに単純化できます。
SELECT ID, Max(Last)
FROM T UNPIVOT (Value FOR Last IN (A1, A2, A3, A4, A5, A6, A7, A8, A9)) U
GROUP BY ID;
最後に、残念ながら他のバージョンよりもパフォーマンスが悪いと私が考えたクレイジーなバージョンを次に示します。
SELECT
T.ID,
Coalesce(
(SELECT 'A9' WHERE T.A9 IS NOT NULL),
(SELECT 'A8' WHERE T.A8 IS NOT NULL),
(SELECT 'A7' WHERE T.A7 IS NOT NULL),
(SELECT 'A6' WHERE T.A6 IS NOT NULL),
(SELECT 'A5' WHERE T.A5 IS NOT NULL),
(SELECT 'A4' WHERE T.A4 IS NOT NULL),
(SELECT 'A3' WHERE T.A3 IS NOT NULL),
(SELECT 'A2' WHERE T.A2 IS NOT NULL),
(SELECT 'A1' WHERE T.A1 IS NOT NULL)
) LastNotNullColumn
FROM T
ORDER BY ID
理論的には、エンジンは CASE 式のバージョンにより似たプランを思いつくことができますが、そうではありません。select ステートメントごとに 1 つのテーブル オブジェクトがあり、CASE 式の約 2 倍の CPU を消費するという、この計画は非常にクレイジーに見えます。
私がテストしたすべてのバージョンは、同じ数の論理読み取りを使用し、CPU のみが異なります。テストには 15,000 行を使用しました。
最後に、あなたのスキーマがおそらく最適ではないことを警告しないわけにはいきません。あなたのデータが何であるかはわかりませんが、最後のデータを見つけようとしているということは、おそらく列がライフサイクルの時間または段階を表していることを示唆しており、それは正しいデータベース設計ではありません. 代わりに、データをピボットせずに保存します。ピボットされた結果セットが必要になったら、PIVOT を実行できます。また、ID ごとの最新の値のクエリが少し簡単になります。