1

これは、関連する前の質問の拡張バージョンです。ErwinBrandstetterがそうするように提案した新しい質問を投稿しました。(人々が私の最初の質問に答えた後、私は実際にこれが欲しかったことに気づきました)

次のデータがある(空白はNULLを意味します):

ID    User  ColA    ColB    ColC
1     1     15              20
2     1     11      4       
3     1             3
4     2     5       5       10
5     2     6 
6     2             8
7     1             1

最も簡単な方法で、すべてのユーザーの各列の最後のNULL以外の値を取得するにはどうすればよいですか?したがって、指定されたデータの結果は次のようになります。

User  ColA    ColB    ColC
1     11      1       20
2     6       8       10

私が説明したのと同じようなことをしているように見える関数はあまり見つかりCOALESCEませんでしたが、私の場合は期待どおりに機能しません。

:可能であれば標準SQL、それ以外の場合はPostgreSQL。関連する列の数が変わる可能性があるため、これら3つの特定の列に関連付けられていないソリューションが最適です。

4

2 に答える 2

1

このクエリは、MSSQLに簡単に変換できます。さらに具体的なものが必要な場合は、コメントを追加してください。Mysqlクエリ:

SQLFIDDLEExample

SELECT
t1.User,
(SELECT ColA 
           FROM Table1
           WHERE ColA is not null
           AND Table1.User = t1.User
           ORDER BY ID DESC
           LIMIT 1 ) as ColA,
(SELECT ColB 
           FROM Table1
           WHERE ColB is not null
           AND Table1.User = t1.User
           ORDER BY ID DESC
           LIMIT 1 ) as ColB,
(SELECT ColC 
           FROM Table1
           WHERE ColC is not null
           AND Table1.User = t1.User
           ORDER BY ID DESC
           LIMIT 1 ) as ColC
FROM Table1 t1
GROUP BY t1.User

結果:

| USER | COLA | COLB | COLC |
-----------------------------
|    1 |   11 |    1 |   20 |
|    2 |    6 |    8 |   10 |
于 2012-11-13T17:40:01.143 に答える
1

「標準」SQL

前の質問に投稿したものと同様に、再帰CTEはエレガントで、おそらく標準SQLでそれを行うための最速の方法です(特にユーザーあたりの行数が多い場合)。

WITH RECURSIVE t AS (
   SELECT row_number() OVER (PARTITION BY usr ORDER  BY id DESC) AS rn
         ,usr, cola, colb, colc
   FROM   tbl
   )

   , x AS (
   SELECT rn, usr, cola, colb, colc
   FROM   t
   WHERE  rn = 1

   UNION ALL
   SELECT t.rn, t.usr
        , COALESCE(x.cola, t.cola)
        , COALESCE(x.colb, t.colb)
        , COALESCE(x.colc, t.colc)
   FROM   x
   JOIN   t USING (usr)
   WHERE  t.rn = x.rn + 1
   AND    (x.cola IS NULL OR x.colb IS NULL OR x.colc IS NULL)
   )
SELECT DISTINCT ON (usr)
       usr, cola, colb, colc
FROM   x
ORDER  BY usr, rn DESC;

->要求されたPostgreSQLのsqlfiddle。

唯一の非標準要素はDISTINCT ON、であり、これは標準の拡張DISTINCTです。SELECT標準SQLの場合、finalをこれに置き換えます。

SELECT usr
      ,max(cola) As cola
      ,max(colb) As colb
      ,max(colc) As colc
FROM   x
GROUP  BY usr
ORDER  BY usr;

「標準SQL」の要求は、使用が制限されています。標準は紙にのみ存在します。RDBMSは100%標準SQLを実装していません。標準には無意味な部分が所々に含まれているため、それも無意味です。間違いなく、PostgreSQLの実装は標準に最も近いものの1つです。

PL/pgSQL関数

このソリューションはPostgreSQLに固有ですが、非常にうまく機能するはずです。

私は上のフィドルで示されているのと同じテーブルの上に構築しています。

CREATE OR REPLACE FUNCTION f_last_nonull_per_user()
RETURNS SETOF tbl AS
$func$
DECLARE
   _row tbl;  -- table name can be used as row type
   _new tbl;
BEGIN

FOR _new IN
   SELECT * FROM tbl ORDER BY usr, id DESC
LOOP
   IF _new.usr = _row.usr THEN 
      _row.id := _new.id;   -- copy only id
      IF _row.cola IS NULL AND _new.cola IS NOT NULL THEN
         _row.cola := _new.cola; END IF;   -- only if no value found yet
      IF _row.colb IS NULL AND _new.colb IS NOT NULL THEN
         _row.colb := _new.colb; END IF;
      IF _row.colc IS NULL AND _new.colc IS NOT NULL THEN
         _row.colc := _new.colc; END IF;
   ELSE
      IF _new.usr <> _row.usr THEN  -- doesn't fire on first row
         RETURN NEXT _row;
      END IF;   
      _row := _new;  -- remember row for next iteration
   END IF;
END LOOP;

RETURN NEXT _row;  -- return row for last usr

END
$func$ LANGUAGE plpgsql;

電話:

SELECT * FROM f_last_nonull_per_user();

id行全体を返します-すべての列を埋めるのに必要な最小値を含みます。

于 2012-11-13T17:00:23.260 に答える