web-dev-qa-db-ja.com

SQL QUERYは、行のNULL値を以前の既知の値からの値に置き換えます

2列あります

date   number       
----   ------
1      3           
2      NULL        
3      5           
4      NULL        
5      NULL        
6      2          
.......

NULL値を新しい値に置き換える必要があります。日付列の前の日付の最後の既知の値から値を取得します。例:date = 2 number = 3、date 4 and 5 number = 5および5。NULL値が表示されます無作為に。

24
mike

Sql Serverを使用している場合、これは動作するはずです

DECLARE @Table TABLE(
        ID INT,
        Val INT
)

INSERT INTO @Table (ID,Val) SELECT 1, 3
INSERT INTO @Table (ID,Val) SELECT 2, NULL
INSERT INTO @Table (ID,Val) SELECT 3, 5
INSERT INTO @Table (ID,Val) SELECT 4, NULL
INSERT INTO @Table (ID,Val) SELECT 5, NULL
INSERT INTO @Table (ID,Val) SELECT 6, 2


SELECT  *,
        ISNULL(Val, (SELECT TOP 1 Val FROM @Table WHERE ID < t.ID AND Val IS NOT NULL ORDER BY ID DESC))
FROM    @Table t
20
Adriaan Stander

MySQLソリューションは次のとおりです。

_UPDATE mytable
SET number = (@n := COALESCE(number, @n))
ORDER BY date;
_

これは簡潔ですが、他のブランドのRDBMSでは必要ありません。他のブランドの場合、より関連性の高いブランド固有のソリューションが存在する場合があります。そのため、使用しているブランドをお知らせください。

@Paxがコメントしたように、ベンダーに依存しないのは良いことですが、それに失敗した場合は、選択したブランドのデータベースを最大限に活用することも良いことです。


上記のクエリの説明:

_@n_はMySQLユーザー変数です。 NULLから始まり、UPDATEが行を実行するときに各行に値が割り当てられます。 numberがNULLでない場合、_@n_にはnumberの値が割り当てられます。 numberがNULLの場合、COALESCE()はデフォルトの_@n_の以前の値になります。いずれの場合でも、これはnumber列の新しい値になり、UPDATEは次の行に進みます。 _@n_変数は行ごとに値を保持するため、後続の行は前の行からの値を取得します。 MySQLはORDER BYをUPDATEとともに特別に使用しているため、UPDATEの順序は予測可能です(これは標準SQLではありません)。

15
Bill Karwin

最良のソリューションは、ビルカーウィンが提供するものです。私は最近、比較的大きな結果セットでこれを解決しなければなりませんでした(各列にこのタイプの「現在の行でこの値がnullの場合、最後の非null値を表示する」を必要とする12列の1000行)とトップ1の更新メソッドを使用して以前の既知の値の選択(または上位1のサブクエリ)が非常に遅く実行されました。

私はSQL 2005を使用していますが、変数置換の構文はmysqlとは少し異なります。

UPDATE mytable 
SET 
    @n = COALESCE(number, @n),
    number = COALESCE(number, @n)
ORDER BY date

最初のsetステートメントは、変数@nの値を現在の行の値 'number'に更新します( 'number'がNULLでない場合(COALESCEは、渡された最初の非NULL引数を返します)。2番目のsetステートメントは、実際のそれ自身への 'number'の列値(nullでない場合)または変数@n(常に最後に検出されたNULL以外の値が含まれます)。

このアプローチの利点は、一時テーブルを何度もスキャンするために追加のリソースが消費されないことです... @nの行内更新は、最後の非NULL値の追跡を処理します。

私は彼の答えを投票するのに十分な担当者がいませんが、誰かがそうすべきです。最もエレガントで最高のパフォーマンスを発揮します。

9
voutmaster

Oracleソリューション(10g以上)は次のとおりです。分析関数last_value()ignore nullsオプションと共に使用し、列の最後の非NULL値を置き換えます。

SQL> select *
  2  from mytable
  3  order by id
  4  /

        ID    SOMECOL
---------- ----------
         1          3
         2
         3          5
         4
         5
         6          2

6 rows selected.

SQL> select id
  2         , last_value(somecol ignore nulls) over (order by id) somecol
  3  from mytable
  4  /

        ID    SOMECOL
---------- ----------
         1          3
         2          3
         3          5
         4          5
         5          5
         6          2

6 rows selected.

SQL>
8
APC

次のスクリプトはこの問題を解決し、プレーンなANSI SQLのみを使用します。このソリューションを SQL2008SQLite および Oracle11g でテストしました。

CREATE TABLE test(mysequence INT, mynumber INT);

INSERT INTO test VALUES(1, 3);
INSERT INTO test VALUES(2, NULL);
INSERT INTO test VALUES(3, 5);
INSERT INTO test VALUES(4, NULL);
INSERT INTO test VALUES(5, NULL);
INSERT INTO test VALUES(6, 2);

SELECT t1.mysequence, t1.mynumber AS ORIGINAL
, (
    SELECT t2.mynumber
    FROM test t2
    WHERE t2.mysequence = (
        SELECT MAX(t3.mysequence)
        FROM test t3
        WHERE t3.mysequence <= t1.mysequence
        AND mynumber IS NOT NULL
       )
) AS CALCULATED
FROM test t1;
6
Gerardo Lima

私はそれが非常に古いフォーラムであることを知っていますが、私の問題のトラブルシューティング中にこれに遭遇しました:)ちょうど他の人が上記の問題に少し複雑な解決策を与えていることに気付きました。以下の私の解決策をご覧ください:

DECLARE @A TABLE(ID INT, Val INT)

INSERT INTO @A(ID,Val) SELECT 1, 3
INSERT INTO @A(ID,Val) SELECT 2, NULL
INSERT INTO @A(ID,Val) SELECT 3, 5
INSERT INTO @A(ID,Val) SELECT 4, NULL
INSERT INTO @A(ID,Val) SELECT 5, NULL
INSERT INTO @A(ID,Val) SELECT 6, 2

UPDATE D
    SET D.VAL = E.VAL
    FROM (SELECT A.ID C_ID, MAX(B.ID) P_ID
          FROM  @A AS A
           JOIN @A AS B ON A.ID > B.ID
          WHERE A.Val IS NULL
            AND B.Val IS NOT NULL
          GROUP BY A.ID) AS C
    JOIN @A AS D ON C.C_ID = D.ID
    JOIN @A AS E ON C.P_ID = E.ID

SELECT * FROM @A

これが誰かを助けることを願っています:)

4
Cyrus Christ

Redshiftのソリューションを探している場合、これはframe節で機能します:

SELECT date, 
       last_value(columnName ignore nulls) 
                   over (order by date
                         rows between unbounded preceding and current row) as columnName 
 from tbl
2
PieCharmed

非常に一般的な意味で:

UPDATE MyTable
SET MyNullValue = MyDate
WHERE MyNullValue IS NULL
2
Degan

これがMS Accessのソリューションです。

例のテーブルはtabと呼ばれ、フィールドはidvalです。

SELECT (SELECT last(val)
          FROM tab AS temp
          WHERE tab.id >= temp.id AND temp.val IS NOT NULL) AS val2, *
  FROM tab;
1
George

まず、本当に値を保存する必要がありますか?あなたは仕事をするビューを使用するだけです:

_SELECT  t."date",
        x."number" AS "number"
FROM    @Table t
JOIN    @Table x
    ON  x."date" = (SELECT  TOP 1 z."date"
                    FROM    @Table z
                    WHERE   z."date" <= t."date"
                        AND z."number" IS NOT NULL
                    ORDER BY z."date" DESC)
_

本当にID ("date")列があり、それが(クラスター化された)主キーである場合、このクエリはかなり高速です。ただし、クエリプランを確認してください。Val列を含むカバーインデックスを用意することをお勧めします。

また、プロシージャを避けることができるときにプロシージャが気に入らない場合は、UPDATEに対して同様のクエリを使用することもできます。

_UPDATE  t
SET     t."number" = x."number"
FROM    @Table t
JOIN    @Table x
    ON  x."date" = (SELECT  TOP 1 z."date"
                    FROM    @Table z
                    WHERE   z."date" < t."date" --//@note: < and not <= here, as = not required
                        AND z."number" IS NOT NULL
                    ORDER BY z."date" DESC)
WHERE   t."number" IS NULL
_

注:コードは「SQL Server」で動作する必要があります。

1
van
UPDATE TABLE
   SET number = (SELECT MAX(t.number)
                  FROM TABLE t
                 WHERE t.number IS NOT NULL
                   AND t.date < date)
 WHERE number IS NULL
0
OMG Ponies