选择每列的最后一个值,每个用户,最简单的方法(Select the last value of ea

2019-10-17 09:53发布

这是一个扩展版本相关的前面的问题 。 我已经张贴了一个新问题了欧文Brandstetter修改建议我这样做。 (我意识到,我其实是想这个,人家回答我的第一个问题后)

具有下列数据(空白表示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

我怎样才能得到每列的最后一个非空值的所有用户,最简单的方法是什么? 因此,对于给定的数据将是导致:

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

我没有太多发现,似乎做类似我形容是一些功能COALESCE ,但预计将在我的情况下,这是行不通的。

:标准SQL如果可能的话,PostgreSQL的除外。 所涉及的列数可能会发生变化,从而不依赖于这三个特定列的解决方案是最好的。

Answer 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;

- > sqlfiddle所请求的PostgreSQL。

唯一的非标准元件是DISTINCT ON ,这是一个扩展DISTINCT在标准。 更换最终SELECT这个为标准的SQL:

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的实现是最接近标准之一。

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 ,我们需要填写的所有列。



Answer 2:

这个查询很容易转换到MS SQL。 如果你需要更具体的东西加评论。 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 |


文章来源: Select the last value of each column, per user, the simplest way
标签: sql null