1

私は(私の意見では)トリッキーなSQLの問題を抱えています。

サブスクリプションのあるテーブルを取得しました。各サブスクリプションには、時間の経過とともに変化する ID と一連の属性があります。属性値が変更されると、サブスクリプション キーと新しい値を使用して新しい行が作成されますが、変更された属性に対してのみです。変更されなかった属性の値は空のままです。これは次のようになります (結果を正しくソートするために使用する ValidTo と ValidFrom の日付は省略しました)。

SubID  Att1  Att2   
 1      J
 1            L 
 1      B
 1            H
 1      A     H

次の結果が得られるように、このテーブルを変換する必要があります。

SubID  Att1  Att2   
 1      J     
 1      J     L
 1      B     L
 1      B     H
 1      A     H

だから基本的に; 属性が空の場合、その属性の以前の値を取得します。解決策は何でも…。つまり、結果を得るために何をしなければならないかは問題ではありません。テーブルの上のビュー、新しいテーブルを作成するための SSIS パッケージ、または 3 番目の何かです。

4

6 に答える 6

1

これはOracle 11gで動作します

  select  SUBID
         ,NVL(ATT1,LAG(ATT1) over(order by ValidTo)) ATT1
         ,NVL(ATT2,lag(ATT2) over(order by ValidTo)) ATT2 
  from table_name

Gordon Linoff と Jack Douglas に同意します。このコードには、null を持つ複数のレコードが挿入された場合の制限があります..しかし、以下のコードはそれを処理します..

select SUBID
      ,NVL(ATT1,LAG(ATT1 ignore nulls) over(order by VALIDTO)) ATT1
      ,NVL(ATT2,LAG(ATT2 ignore nulls) over(order by VALIDTO)) ATT2
from Table_name

SQLフィドル http://sqlfiddle.com/#!4/3b530/4を参照してください

于 2012-12-06T14:15:27.790 に答える
1

相関サブクエリを使用してこれを行うことができます。

select t.subid,
       (select t2.att1 from t t2 where t2.rowid <= t.rowid and t2.att1 is not null order by rowid desc limit 1) as att1,
       (select t2.att2 from t t2 where t2.rowid <= t.rowid and t2.att2 is not null order by rowid desc limit 1) as att1
from t

これは、行の順序を指定する行 ID または同等のもの (作成日時など) があることを前提としています。limitまた、結果を制限するためにも使用します。他のデータベースでは、これがtop代わりに使用される場合があります。(そして、Oracle はもう少し複雑な式を使用します。)

ValidTo を使用してこれを記述します。ただし、ValidTo と ValidFrom があるため、実際の式はさらに複雑になります。他の時点での代入値に関して、これらの値を使用するためのルールを明確にするために、質問が必要です。

于 2012-12-06T14:16:34.333 に答える
1

(SSISに言及したという事実に基づいて)OUTER APPLY前の行を取得するために使用できると仮定します:

DECLARE @T TABLE (SubID INT, Att1 CHAR(1), Att2 CHAR(2), ValidFrom DATETIME);
INSERT @T VALUES
    (1, 'J', '', '20121201'),
    (1, '', 'l', '20121202'),
    (1, 'B', '', '20121203'),
    (1, '', 'H', '20121204'),
    (1, 'A', 'H', '20121205');

SELECT  T.SubID,
        Att1 = COALESCE(NULLIF(T.att1, ''), prev.Att1, ''),
        Att2 = COALESCE(NULLIF(T.att2, ''), prev.Att2, '')
FROM    @T T
        OUTER APPLY
        (   SELECT  TOP 1 Att1, Att2
            FROM    @T prev
            WHERE   prev.SubID = T.SubID
            AND     prev.ValidFrom < t.ValidFrom
            ORDER BY ValidFrom DESC
        ) prev
ORDER BY T.ValidFrom;

(順序が正しいことを確認するために、ValidFrom にランダムな値を追加する必要がありました)

編集

空の値を持つ複数の連続した行がある場合、上記は機能しません-たとえば

DECLARE @T TABLE (SubID INT, Att1 CHAR(1), Att2 CHAR(2), ValidFrom DATETIME);
INSERT @T VALUES
    (1, 'J', '', '20121201'),
    (1, '', 'l', '20121202'),
    (1, 'B', '', '20121203'),
    (1, '', 'H', '20121204'),
    (1, '', 'J', '20121205'),
    (1, 'A', 'H', '20121206');

これが発生する可能性が高い場合は、次の 2 つOUTER APPLYのが必要になります。

SELECT  T.SubID,
        Att1 = COALESCE(NULLIF(T.att1, ''), prevAtt1.Att1, ''),
        Att2 = COALESCE(NULLIF(T.att2, ''), prevAtt2.Att2, '')
FROM    @T T
        OUTER APPLY
        (   SELECT  TOP 1 Att1
            FROM    @T prev
            WHERE   prev.SubID = T.SubID
            AND     prev.ValidFrom < t.ValidFrom
            AND     COALESCE(prev.Att1 , '') != ''
            ORDER BY ValidFrom DESC
        ) prevAtt1
        OUTER APPLY
        (   SELECT  TOP 1 Att2
            FROM    @T prev
            WHERE   prev.SubID = T.SubID
            AND     prev.ValidFrom < t.ValidFrom
            AND     COALESCE(prev.Att2 , '') != ''
            ORDER BY ValidFrom DESC
        ) prevAtt2
ORDER BY T.ValidFrom;

ただし、各 OUTER APPLY は 1 つの値のみを返すため、これを相関サブクエリに変更します。これは、PrevAtt1.Att1必要かどうかにかかわらず、すべての行に対して「PrevAtt2.Att2」が評価されるためです。ただし、これを次のように変更すると:

SELECT  T.SubID,
        Att1 = COALESCE(
                    NULLIF(T.att1, ''), 
                    (   SELECT  TOP 1 Att1
                        FROM    @T prev
                        WHERE   prev.SubID = T.SubID
                        AND     prev.ValidFrom < t.ValidFrom
                        AND     COALESCE(prev.Att1 , '') != ''
                        ORDER BY ValidFrom DESC
                    ), ''),
        Att2 = COALESCE(
                    NULLIF(T.att2, ''), 
                    (   SELECT  TOP 1 Att2
                        FROM    @T prev
                        WHERE   prev.SubID = T.SubID
                        AND     prev.ValidFrom < t.ValidFrom
                        AND     COALESCE(prev.Att2 , '') != ''
                        ORDER BY ValidFrom DESC
                    ), '')
FROM    @T T
ORDER BY T.ValidFrom;

サブクエリは、すべての行ではなく、必要な場合 (つまり、Att1 または Att2 が空白の場合) にのみ評価されます。実行計画はこれを示しておらず、実際、後者の「実際の実行計画」はより集中的に見えますが、そうではないことはほぼ確実です。ただし、いつものように、重要なのはテストです。データに対して両方を実行し、どちらが最高のパフォーマンスを発揮するかを確認し、読み取りなどの IO 統計を確認します。

于 2012-12-06T13:36:03.047 に答える
0

SQL Serverには触れたことがありませんが、Oracleと同じように分析関数をサポートしていることを読みました。

> select * from MYTABLE order by ValidFrom;

     SUBID A A VALIDFROM
---------- - - -------------------
         1 J   2012-12-06 15:14:51
         2 j   2012-12-06 15:15:20
         1   L 2012-12-06 15:15:31
         2   l 2012-12-06 15:15:39
         1 B   2012-12-06 15:15:48
         2 b   2012-12-06 15:15:55
         1   H 2012-12-06 15:16:03
         2   h 2012-12-06 15:16:09
         1 A H 2012-12-06 15:16:20
         2 a h 2012-12-06 15:16:29


select
  t.SubID
 ,last_value(t.Att1 ignore nulls)over(partition by t.SubID order by t.ValidFrom rows between unbounded preceding and current row) as Att1
 ,last_value(t.Att2 ignore nulls)over(partition by t.SubID order by t.ValidFrom rows between unbounded preceding and current row) as Att2
 ,t.ValidFrom
from MYTABLE t;

     SUBID A A VALIDFROM
---------- - - -------------------
         1 J   2012-12-06 15:45:33
         1 J L 2012-12-06 15:45:41
         1 B L 2012-12-06 15:45:49
         1 B H 2012-12-06 15:45:58
         1 A H 2012-12-06 15:46:06
         2 j   2012-12-06 15:45:38
         2 j l 2012-12-06 15:45:44
         2 b l 2012-12-06 15:45:53
         2 b h 2012-12-06 15:46:02
         2 a h 2012-12-06 15:46:09
于 2012-12-06T14:46:13.167 に答える
0
with Tricky1 as (
    Select SubID, Att1, Att2, row_number() over(order by ValidFrom) As rownum 
    From Tricky
)
select T1.SubID, T1.Att1, T2.Att2
from Tricky1 T1
cross join Tricky1 T2
where (ABS(T1.rownum-T2.rownum) = 1 or (T1.rownum = 1 and T2.rownum = 1))
and T1.Att1 is not null
;

また、SQL に前の値の概念がない場合の前の値へのアクセスについては、こちらを参照してください。

于 2012-12-06T13:58:10.283 に答える
0

私はかなり長い間それをしていました。かなり簡単な方法を見つけました。他の方法があるはずだと私が知っているような最善の解決策ではありませんが、ここにあります。

重複も 2008R2 で統合する必要がありました。

したがって、重複レコードのセットを含むテーブルを作成しようとすることができれば。

あなたの例によれば、「ATT1」が空白のテーブルを1つ作成してください。次に、「SubId」の内部結合を使用して更新クエリを使用して、必要なデータを入力します

于 2014-04-28T13:46:25.687 に答える