在Oracle SQL中使用rank()和子查询从更新日期检索上次余额

在Oracle SQL中使用rank()和子查询从更新日期检索上次余额,sql,oracle,window-functions,rank,Sql,Oracle,Window Functions,Rank,从表中检索余额信息时遇到问题。数据集如下所示: | Name | Last Name | Balance | Update Date | +---------------+---------------+---------+-------------+ | John | Doe | $1600 | 2017-01-01 | | John | Doe | $12 | 2017-01-

从表中检索余额信息时遇到问题。数据集如下所示:

| Name          | Last Name     | Balance | Update Date |
+---------------+---------------+---------+-------------+
| John          | Doe           | $1600   | 2017-01-01  |
| John          | Doe           |   $12   | 2017-01-02  |
| John          | Doe           |    $1   | 2017-01-03  |
| John          | Doe           |   $16   | 2017-01-04  |
| John          | Doe           |   $16   | 2017-01-05  |
| John          | Doe           |   $16   | 2017-01-06  |
任务是获取具有更新日期的最新余额,但如果相同余额在几天内相同,那么在这种情况下,我们需要获取具有此余额的第一个更新日期,因此在这种情况下,我们需要以下结果:

| Name          | Last Name     | Balance | Update Date |
+---------------+---------------+---------+-------------+
| John          | Doe           |   $16   | 2017-01-04  |
我尝试使用我的查询:

select 
    a.name,
    a.last_name,
    a.balance,
    a.update_date
from
    (select
        name,
        last_name,
        balance,
        update_date,
        rank () over (partition by name, last_name order by update_date desc) top
    from
        customer_balance) a
where
    a.top = 1
但它显然会返回:

| Name          | Last Name     | Balance | Update Date |
+---------------+---------------+---------+-------------+
| John          | Doe           |   $16   | 2017-01-06  |
我不知道如何修改它以获得期望的结果。请注意,我的访问权限有限,因此不允许使用临时表、函数或类似的内容。只是简单的选择,没有什么花哨的

非常感谢您的帮助。

您可以使用查找包含最新更新日期行的具有相同余额的行组。整个数据集顶部的行与最新余额之间的差值将为0,然后使用group by选择最早的更新日期,如下所示:

WITH customer_balance AS (SELECT 'John' first_name, 'Doe' last_name, 1600 balance, to_date('01/01/2017', 'dd/mm/yyyy') update_date FROM dual UNION ALL
                          SELECT 'John' first_name, 'Doe' last_name, 12 balance, to_date('02/01/2017', 'dd/mm/yyyy') update_date FROM dual UNION ALL
                          SELECT 'John' first_name, 'Doe' last_name, 1 balance, to_date('03/01/2017', 'dd/mm/yyyy') update_date FROM dual UNION ALL
                          SELECT 'John' first_name, 'Doe' last_name, 16 balance, to_date('04/01/2017', 'dd/mm/yyyy') update_date FROM dual UNION ALL
                          SELECT 'John' first_name, 'Doe' last_name, 16 balance, to_date('05/01/2017', 'dd/mm/yyyy') update_date FROM dual UNION ALL
                          SELECT 'John' first_name, 'Doe' last_name, 16 balance, to_date('06/01/2017', 'dd/mm/yyyy') update_date FROM dual UNION ALL
                          SELECT 'John' first_name, 'Doe2' last_name, 1600 balance, to_date('01/01/2017', 'dd/mm/yyyy') update_date FROM dual UNION ALL
                          SELECT 'John' first_name, 'Doe2' last_name, 12 balance, to_date('02/01/2017', 'dd/mm/yyyy') update_date FROM dual UNION ALL
                          SELECT 'John' first_name, 'Doe2' last_name, 1 balance, to_date('03/01/2017', 'dd/mm/yyyy') update_date FROM dual UNION ALL
                          SELECT 'John' first_name, 'Doe2' last_name, 16 balance, to_date('04/01/2017', 'dd/mm/yyyy') update_date FROM dual UNION ALL
                          SELECT 'John' first_name, 'Doe2' last_name, 15 balance, to_date('05/01/2017', 'dd/mm/yyyy') update_date FROM dual UNION ALL
                          SELECT 'John' first_name, 'Doe2' last_name, 16 balance, to_date('06/01/2017', 'dd/mm/yyyy') update_date FROM dual UNION ALL
                          SELECT 'John' first_name, 'Doe2' last_name, 16 balance, to_date('07/01/2017', 'dd/mm/yyyy') update_date FROM dual UNION ALL
                          SELECT 'John' first_name, 'Doe3' last_name, 1600 balance, to_date('01/01/2017', 'dd/mm/yyyy') update_date FROM dual UNION ALL
                          SELECT 'John' first_name, 'Doe3' last_name, 12 balance, to_date('02/01/2017', 'dd/mm/yyyy') update_date FROM dual UNION ALL
                          SELECT 'John' first_name, 'Doe3' last_name, 1 balance, to_date('03/01/2017', 'dd/mm/yyyy') update_date FROM dual UNION ALL
                          SELECT 'John' first_name, 'Doe3' last_name, 16 balance, to_date('04/01/2017', 'dd/mm/yyyy') update_date FROM dual UNION ALL
                          SELECT 'John' first_name, 'Doe3' last_name, 16 balance, to_date('05/01/2017', 'dd/mm/yyyy') update_date FROM dual UNION ALL
                          SELECT 'John' first_name, 'Doe3' last_name, 16 balance, to_date('06/01/2017', 'dd/mm/yyyy') update_date FROM dual UNION ALL
                          SELECT 'John' first_name, 'Doe3' last_name, 17 balance, to_date('07/01/2017', 'dd/mm/yyyy') update_date FROM dual)
SELECT first_name,
       last_name,
       balance,
       min(update_date) update_date
FROM   (SELECT first_name,
               last_name,
               balance,
               update_date,
               row_number() OVER (PARTITION BY first_name, last_name ORDER BY update_date DESC) -- row number across the entire dataset (i.e. for each first_name and last_name)
                 - row_number() OVER (PARTITION BY first_name, last_name, balance ORDER BY update_date DESC) grp -- row number across each balance in the entire dataset.
        FROM   customer_balance)
WHERE  grp = 0
GROUP BY first_name,
         last_name,
         balance;

FIRST_NAME LAST_NAME    BALANCE UPDATE_DATE
---------- --------- ---------- -----------
John       Doe               16 04/01/2017
John       Doe2              16 06/01/2017
John       Doe3              17 07/01/2017
我提供了3个场景:

其中,最新的行用于相同的余额,但该余额不会出现在数据集(即原始数据集)的较早位置 最新的行用于相同的余额,但该余额在数据集中较早出现 最近一行的余额与前一行不同。 可以使用查找包含最新更新日期行的余额相同的行组。整个数据集顶部的行与最新余额之间的差值将为0,然后使用group by选择最早的更新日期,如下所示:

WITH customer_balance AS (SELECT 'John' first_name, 'Doe' last_name, 1600 balance, to_date('01/01/2017', 'dd/mm/yyyy') update_date FROM dual UNION ALL
                          SELECT 'John' first_name, 'Doe' last_name, 12 balance, to_date('02/01/2017', 'dd/mm/yyyy') update_date FROM dual UNION ALL
                          SELECT 'John' first_name, 'Doe' last_name, 1 balance, to_date('03/01/2017', 'dd/mm/yyyy') update_date FROM dual UNION ALL
                          SELECT 'John' first_name, 'Doe' last_name, 16 balance, to_date('04/01/2017', 'dd/mm/yyyy') update_date FROM dual UNION ALL
                          SELECT 'John' first_name, 'Doe' last_name, 16 balance, to_date('05/01/2017', 'dd/mm/yyyy') update_date FROM dual UNION ALL
                          SELECT 'John' first_name, 'Doe' last_name, 16 balance, to_date('06/01/2017', 'dd/mm/yyyy') update_date FROM dual UNION ALL
                          SELECT 'John' first_name, 'Doe2' last_name, 1600 balance, to_date('01/01/2017', 'dd/mm/yyyy') update_date FROM dual UNION ALL
                          SELECT 'John' first_name, 'Doe2' last_name, 12 balance, to_date('02/01/2017', 'dd/mm/yyyy') update_date FROM dual UNION ALL
                          SELECT 'John' first_name, 'Doe2' last_name, 1 balance, to_date('03/01/2017', 'dd/mm/yyyy') update_date FROM dual UNION ALL
                          SELECT 'John' first_name, 'Doe2' last_name, 16 balance, to_date('04/01/2017', 'dd/mm/yyyy') update_date FROM dual UNION ALL
                          SELECT 'John' first_name, 'Doe2' last_name, 15 balance, to_date('05/01/2017', 'dd/mm/yyyy') update_date FROM dual UNION ALL
                          SELECT 'John' first_name, 'Doe2' last_name, 16 balance, to_date('06/01/2017', 'dd/mm/yyyy') update_date FROM dual UNION ALL
                          SELECT 'John' first_name, 'Doe2' last_name, 16 balance, to_date('07/01/2017', 'dd/mm/yyyy') update_date FROM dual UNION ALL
                          SELECT 'John' first_name, 'Doe3' last_name, 1600 balance, to_date('01/01/2017', 'dd/mm/yyyy') update_date FROM dual UNION ALL
                          SELECT 'John' first_name, 'Doe3' last_name, 12 balance, to_date('02/01/2017', 'dd/mm/yyyy') update_date FROM dual UNION ALL
                          SELECT 'John' first_name, 'Doe3' last_name, 1 balance, to_date('03/01/2017', 'dd/mm/yyyy') update_date FROM dual UNION ALL
                          SELECT 'John' first_name, 'Doe3' last_name, 16 balance, to_date('04/01/2017', 'dd/mm/yyyy') update_date FROM dual UNION ALL
                          SELECT 'John' first_name, 'Doe3' last_name, 16 balance, to_date('05/01/2017', 'dd/mm/yyyy') update_date FROM dual UNION ALL
                          SELECT 'John' first_name, 'Doe3' last_name, 16 balance, to_date('06/01/2017', 'dd/mm/yyyy') update_date FROM dual UNION ALL
                          SELECT 'John' first_name, 'Doe3' last_name, 17 balance, to_date('07/01/2017', 'dd/mm/yyyy') update_date FROM dual)
SELECT first_name,
       last_name,
       balance,
       min(update_date) update_date
FROM   (SELECT first_name,
               last_name,
               balance,
               update_date,
               row_number() OVER (PARTITION BY first_name, last_name ORDER BY update_date DESC) -- row number across the entire dataset (i.e. for each first_name and last_name)
                 - row_number() OVER (PARTITION BY first_name, last_name, balance ORDER BY update_date DESC) grp -- row number across each balance in the entire dataset.
        FROM   customer_balance)
WHERE  grp = 0
GROUP BY first_name,
         last_name,
         balance;

FIRST_NAME LAST_NAME    BALANCE UPDATE_DATE
---------- --------- ---------- -----------
John       Doe               16 04/01/2017
John       Doe2              16 06/01/2017
John       Doe3              17 07/01/2017
我提供了3个场景:

其中,最新的行用于相同的余额,但该余额不会出现在数据集(即原始数据集)的较早位置 最新的行用于相同的余额,但该余额在数据集中较早出现 最近一行的余额与前一行不同。
也许你可以试试这个查询

WITH bal AS
  (SELECT 'John' first_name,
                 'Doe' last_name,
                       1600 balance,
                       to_date('20170101', 'YYYYMMDD') update_date
   FROM dual
   UNION ALL SELECT 'John',
                    'Doe',
                    12 balance,
                    to_date('20170102', 'YYYYMMDD') update_date
   FROM dual
   UNION ALL SELECT 'John',
                    'Doe',
                    1 balance,
                    to_date('20170103', 'YYYYMMDD') update_date
   FROM dual
   UNION ALL SELECT 'John',
                    'Doe',
                    16 balance,
                    to_date('20170104', 'YYYYMMDD') update_date
   FROM dual
   UNION ALL SELECT 'John',
                    'Doe',
                    16 balance,
                    to_date('20170105', 'YYYYMMDD') update_date
   FROM dual
   UNION ALL SELECT 'John',
                    'Doe',
                    16 balance,
                    to_date('20170106', 'YYYYMMDD') update_date
   FROM dual
   UNION ALL SELECT 'John',
                    'Doe',
                    328 balance,
                    to_date('20170107', 'YYYYMMDD') update_date
   FROM dual) -- The main query

SELECT *
FROM
  (SELECT bal.*,
          LAG(balance) OVER(PARTITION BY first_name, last_name
                            ORDER BY update_date)prev_balance
   FROM bal )
WHERE prev_balance IS NULL
  OR balance != prev_balance
在第一步中,我们得到先前的平衡。 在第二个步骤中,我们删除先前余额等于当前余额的所有行。
顺便说一句,很抱歉我用智能手机回答了布局问题。

也许你可以试试这个查询

WITH bal AS
  (SELECT 'John' first_name,
                 'Doe' last_name,
                       1600 balance,
                       to_date('20170101', 'YYYYMMDD') update_date
   FROM dual
   UNION ALL SELECT 'John',
                    'Doe',
                    12 balance,
                    to_date('20170102', 'YYYYMMDD') update_date
   FROM dual
   UNION ALL SELECT 'John',
                    'Doe',
                    1 balance,
                    to_date('20170103', 'YYYYMMDD') update_date
   FROM dual
   UNION ALL SELECT 'John',
                    'Doe',
                    16 balance,
                    to_date('20170104', 'YYYYMMDD') update_date
   FROM dual
   UNION ALL SELECT 'John',
                    'Doe',
                    16 balance,
                    to_date('20170105', 'YYYYMMDD') update_date
   FROM dual
   UNION ALL SELECT 'John',
                    'Doe',
                    16 balance,
                    to_date('20170106', 'YYYYMMDD') update_date
   FROM dual
   UNION ALL SELECT 'John',
                    'Doe',
                    328 balance,
                    to_date('20170107', 'YYYYMMDD') update_date
   FROM dual) -- The main query

SELECT *
FROM
  (SELECT bal.*,
          LAG(balance) OVER(PARTITION BY first_name, last_name
                            ORDER BY update_date)prev_balance
   FROM bal )
WHERE prev_balance IS NULL
  OR balance != prev_balance
在第一步中,我们得到先前的平衡。 在第二个步骤中,我们删除先前余额等于当前余额的所有行。
顺便说一句,很抱歉我在智能手机上回答了布局问题。

我没有时间写出一个经过测试的解决方案,但分析函数lead和lag的目的是:

select name, last_name, balance, update_date
  from (select name,
               last_name,
               balance,
               update_date,
               lead(balance) over (partition by first_name, last_name
                                      order by update_date) 
                 as next_balance
         where balance = :target_balance
         order by update_date
       )
 where balance <> next_balance
   and rownum = 1

我没有时间写出经过测试的解决方案,但分析函数lead和lag旨在:

select name, last_name, balance, update_date
  from (select name,
               last_name,
               balance,
               update_date,
               lead(balance) over (partition by first_name, last_name
                                      order by update_date) 
                 as next_balance
         where balance = :target_balance
         order by update_date
       )
 where balance <> next_balance
   and rownum = 1

@KaushikNayak您的答案也是有效的,只是您缺少子查询和主查询之间的相关性——即,您应该为每个姓名和姓氏查找maxupdate_日期,而不是在整个表中查找。比较两个答案会很有趣,因为其中一个可能比另一个更有效,特别是如果名字、姓氏和更新日期都有索引的话。评论我的答案,因为你在我按下send之前删除了你的答案!谢谢你,博内斯特。我意识到了这一点,并将很快随着更改取消删除它。显然,我的原始解决方案即使具有相关性,也不适用于某些场景,当我稍微修改它以获得正确的结果时,它最终成为Tabibitosan的变体。因此,我认为它可能不会比这更有效。@KaushikNayak这太可惜了@KaushikNayak您的答案也是有效的,只是您缺少子查询和主查询之间的相关性-即,您应该为每个姓名和姓氏查找maxupdate_日期,而不是整个表。比较两个答案会很有趣,因为其中一个可能比另一个更有效,特别是如果名字、姓氏和更新日期都有索引的话。评论我的答案,因为你在我按下send之前删除了你的答案!谢谢你,博内斯特。我意识到了这一点,并将很快随着更改取消删除它。显然,我的原始解决方案即使具有相关性,也不适用于某些场景,当我稍微修改它以获得正确的结果时,它最终成为Tabibitosan的变体。所以,我认为它可能不会比这更有效。@KaushikNayak,那太可惜了