为啥 LAG 除了一行之外的所有内容都返回 null?

Posted

技术标签:

【中文标题】为啥 LAG 除了一行之外的所有内容都返回 null?【英文标题】:Why does LAG return null for all but one row?为什么 LAG 除了一行之外的所有内容都返回 null? 【发布时间】:2020-03-01 07:30:00 【问题描述】:

我正在尝试使用 LAG 获取组中排名第二的行以及前一个(排名第一的)行中的值。

但是,应用条件 where place = 2 会导致结果集的第一行只有 LAG 结果。

包含LEAD 列会导致预期结果显示在LAG 列中。

我使用的是 Oracle 19c,我正在使用的表如下所示:

UKNUM PARTY   UKVOTES
----- ------ --------
    1 con        6605
    1 lab       23745
    1 lib        8206
    1 plaid      1859

每个uknum 至少有三行,并且所有列都不为空。

这是我的查询,旨在获取第二个放置的行(ukvotes),并为第一个放置的行提供 ukvotes 值:

SELECT
  *
FROM
  (
    SELECT
      uknum,
      rank() OVER (PARTITION BY uknum ORDER BY ukvotes DESC) AS place,
      lag(ukvotes) OVER (PARTITION BY uknum ORDER BY ukvotes DESC) AS lag
    FROM
      ukresults
  )
WHERE
  place = 2
  AND uknum BETWEEN 1 AND 5;

这会产生以下结果:

     UKNUM      PLACE        LAG
---------- ---------- ----------
         1          2      23745
         2          2
         3          2
         4          2
         5          2

我希望对于第 2-5 行,LAG 列中会有一个非空值。

LEAD 添加到查询会导致LAG 列包含预期结果:

SELECT
  *
FROM
  (
    SELECT
      uknum,
      rank() OVER (PARTITION BY uknum ORDER BY ukvotes DESC) AS place,
      lag(ukvotes) OVER (PARTITION BY uknum ORDER BY ukvotes DESC) AS lag,
      lead(ukvotes) OVER (PARTITION BY uknum ORDER BY ukvotes DESC) AS lead
    FROM
      ukresults
  )
WHERE
  place = 2
  AND uknum BETWEEN 1 AND 5;
     UKNUM      PLACE        LAG       LEAD
---------- ---------- ---------- ----------
         1          2      23745       6605
         2          2      19262       7426
         3          2      15393      10372
         4          2      31288       6070
         5          2      24148      11599

我很困惑为什么第一个查询没有产生预期的结果,所有行的值都是LAG

Here’s a DBFiddle with the data and the queries loaded.

更新:这里是来自 Oracle 19c 的查询计划,首先用于仅使用 LAG 的查询,然后是单独使用 LEADLAGLEAD 一起使用的查询计划(对于包含LEAD)。

LAG 唯一计划:

--------------------------------------------------------------------------------------
| Id  | Operation                | Name      | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT         |           |   197 |  7683 |     5  (20)| 00:00:01 |
|*  1 |  VIEW                    |           |   197 |  7683 |     5  (20)| 00:00:01 |
|*  2 |   WINDOW SORT PUSHED RANK|           |   197 |  1773 |     5  (20)| 00:00:01 |
|   3 |    TABLE ACCESS CLUSTER  | UKRESULTS |   197 |  1773 |     4   (0)| 00:00:01 |
|*  4 |     INDEX RANGE SCAN     | UKNUMX    |     1 |       |     2   (0)| 00:00:01 |
--------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter("PLACE"=2)
   2 - filter(RANK() OVER ( PARTITION BY "UKNUM" ORDER BY
              INTERNAL_FUNCTION("UKVOTES") DESC )<=2)
   4 - access("UKNUM"<=50)

仅限LEAD / LAGLEAD 计划:

------------------------------------------------------------------------------------
| Id  | Operation              | Name      | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT       |           |   197 |  7683 |     5  (20)| 00:00:01 |
|*  1 |  VIEW                  |           |   197 |  7683 |     5  (20)| 00:00:01 |
|   2 |   WINDOW SORT          |           |   197 |  1773 |     5  (20)| 00:00:01 |
|   3 |    TABLE ACCESS CLUSTER| UKRESULTS |   197 |  1773 |     4   (0)| 00:00:01 |
|*  4 |     INDEX RANGE SCAN   | UKNUMX    |     1 |       |     2   (0)| 00:00:01 |
------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter("PLACE"=2)
   4 - access("UKNUM"<=50)

【问题讨论】:

显示每一行的投票数。原始数据也会有所帮助。您可以设置某种 dbfiddle。 您的查询似乎没有产生您让我们期望的结果,但也许我没有正确的数据。 See this dbfiddle DBFiddle 可在此处获得:dbfiddle.uk/…@BobJarvis 它仅提供第一行的结果,其余为 null。 啊,非常好 - 感谢 dbfiddle。 :-) 【参考方案1】:

这看起来像是优化器中的一个错误。

好像它在评估 RANK 之后,但在评估 LAG 之前将 WHERE place = 2 过滤器推送到主查询。但是,只有在第一行之后。

WHERE place = 1 会产生更奇怪的结果。

如果我在 dbfiddle 中将 Oracle 版本更改为 11g,它会产生预期的结果。

也许,这是 dbfiddble 中的错误?

【讨论】:

如果是这样,我认为这可能是Oracle本身的问题。原始结果来自我大学的 Oracle 19c 实例。 rank() 推得更深(通过将其放在子查询中,lag-using 子查询从中提取可以解决此问题。看来 where 子句需要与 @987654327 分开@ 由两个级别(即在此处的外部查询中)。【参考方案2】:

这很令人困惑。 lag() 真是个小伙子,因为顺序是降序的。

我的最佳猜测是uknums 2-5 没有第三名。

【讨论】:

lag() is really a lad ... 今日报价 :-) 每个uknum至少有三行。

以上是关于为啥 LAG 除了一行之外的所有内容都返回 null?的主要内容,如果未能解决你的问题,请参考以下文章

为啥除了 _view 之外都设置了 IBOutlets?

在最后一行之外使用 Lag Function

Laravel 除了 '/' 之外的所有路由都返回 404

Laravel 除了“/”之外的所有路由在 AWS EC2 上返回 404

为啥socketio广播向除了发送它的那个客户端之外的所有客户端发送?

Excel2007怎么选中一列中除了第一行之外的下面所有的行?