2016-09-28 6 views
3

私はNULLをデータで埋める必要があるテーブルを持っています。しかし、前と後の値が同じであれば、NULLを置き換えたいだけです。TSQLは同じ値の間だけNULLを更新します

G ID Val NewValue 
a 1 N  N 
a 2 N  N 
a 3 NULL NULL -- notice this is the last value of group 'a' so keep NULL and if NULL was the first of the group, keep NULL as well 

b 4 P  P 
b 5 N  N 
b 6 NULL N  -- N before and N after so the new value ='N' 
b 7 N  N 

c 8 N  N 
c 9 N  N 
c 10 NULL NULL -- N before but P after so keep NULL 
c 11 P  P 
c 12 N  N 
c 13 N  N 
c 14 N  N 

d 15 P  P 
d 16 NULL P -- P before and P after (the series) so make 'P' 
d 17 NULL P -- P before and P after (the series) so make 'P' 
d 18 P  P 
d 19 N  N 

私は簡単な部分の解決方法を持っています。以下のクエリができます:

    IDの
  • 正しい結果= 6、16、およびIDの17の
  • 誤った結果= 3と10

をだから私の質問は、残りの部分を行う方法ですか?

DECLARE @Table TABLE(
    G varchar(1), 
    ID INT, 
    Val varchar(2) 
    ) 
INSERT INTO @Table VALUES 
('a',1,'N'),('a',2,'N'),('a',3, NULL), 
('b',4,'P'),('b',5,'N'),('b',6, NULL),('b',7,'N'), 
('c',8, 'N'),('c',9, 'N'),('c',10,NULL),('c',11,'P'),('c',12,'N'),('c',13,'N'),('c',14,'N'), 
('d',15, 'P'),('d',16, NULL),('d',17,NULL),('d',18,'P'),('d',19,'N') 
SELECT *, 
CASE WHEN Val IS NULL 
    THEN (SELECT TOP 1 Val FROM @Table WHERE ID<T.ID AND G=T.G AND Val IS NOT NULL ORDER BY ID DESC) 
    ELSE Val END AS NewVal 
FROM @Table T 

ORDER BY G, ID 
+0

使用LAG/LEADと@dfundakoが言うように、あなたは、上のパーティショニングとLAG/LEADの簡単な使用この – dfundako

+2

を行うことができるはず " G 'カラムは、例の最後のケースを除いて、良いでしょう。複数のNULLが連続しています... – smj

+0

LAG/LEAD非常に興味深い関数について知りませんでした。 – user918967

答えて

1

問題を解決する際に最も重要なことは、単純な英語のアプローチを理解することです。ここでは、例えば、最も近い2つの非NULL値を見つけることができます。それらが存在し、等しい場合、それらの値を使用します。ここで

は、SQLへのこのアプローチの翻訳です:

select t.*, isnull(t.Val, case when pr.Val = nr.Val then pr.Val end) as [NewVal] 
from @table t 
    outer apply (
     select top (1) tp.Val from @table tp 
     where tp.G = t.G and tp.Val is not null and tp.Id < t.Id 
     order by tp.Id desc 
    ) pr 
    outer apply (
     select top (1) tn.Val from @table tn 
     where tn.G = t.G and tn.Val is not null and tn.Id > t.Id 
     order by tn.Id 
    ) nr 
order by t.g, t.Id; 
+0

いずれか前にAPPLYを使用していない! 1つの質問に2つの新しいSQL関数(私にとって)。あなたは素晴らしいです。とにかくこのクエリがテーブルのデータがたくさんあると予測できないのかどうかは予測できますか?実行したいテーブルは〜150K行です。 – user918967

+0

150Kはピーナッツですが、クエリでは適切なインデックスが有効です。クラスタ化された/ PKは '(G、Id)'に、クラスタ化されていない 'Val'はクラスタ化されています。また、私が正しく理解していれば、すべてではなく、いくつかの行だけを更新する必要があります。 –

1

これは、論理を説明する必要がある最後のものを差し引いたものと、それをどのように処理するのかを求めます。

SELECT 
    t.G, 
    t.ID, 
    val, 
    case 
     when val is null and 
      lag(val) over (partition by G order by ID) = lead(val) over (partition by G order by ID) then lag(val) over (partition by G order by ID) 
     else val 
    end as NewValue 
FROM @Table T 
関連する問題