【问题标题】:Get rows with difference of dates being one获取日期差为一的行
【发布时间】:2012-09-08 20:26:59
【问题描述】:

我在SQLFiddle中定义了下表和行

我需要从 products 表中选择两行 start_datenvl(return_date,end_date) 是 1。即当前行的 start_date 和前一行的 nvl(return_date,end_date) 应该是 1

例如

PRODUCT_NO TSH098PRODUCT_REG_NO FLDGEND_DATE2012 年 8 月 15 日PRODUCT_NO TSH128PRODUCT_REG_NO FLDG start_date2012 年 8 月 16 日,所以差别只有一天。

如何使用 sql 获得所需的输出。

任何帮助都是非常值得赞赏的。

谢谢

【问题讨论】:

  • 在您的 cmets 中,您添加了额外的要求,关于连接和过滤条件。您没有在最初的问题中包含所有要求,从而浪费了人们的时间。请通过汇总所有这些版本并编辑您的问题来进行修改,以反映您的实际要求。

标签: sql database oracle oracle10g date-arithmetic


【解决方案1】:

您可以使用lag 分析函数来访问当前位置之前给定物理偏移量的行。根据您的排序顺序,它可能看起来像这样(虽然不是那么优雅)。

select *
  from products p
  join (select *
         from(select p.Product_no
                   , p.Product_Reg_No
                   , case 
                       when (lag(start_date, 1, start_date) over(order by product_reg_no)-
                            nvl(return_date, end_date)) = 1 
                            then lag(start_date, 1, start_date) 
                                   over(order by product_reg_no) 
                        end start_date
                   , End_Date
                   , Return_Date
               from products p 
              order by 2,1 desc
              )
        where start_date is not null
       ) s
    on (p.start_date = s.start_date or p.end_date = s.end_date)
 order by 2, 1 desc

SQL FIddle DEMO

【讨论】:

  • 感谢您的解决方案。对于PRODUCT_REG_NO='FLDG',有PRODUCT_NO TSH098TSH12start_datenvl(return_date, end_date) 相差一,但未选中。我想选择所有日期差为 1 的记录。
  • Nicholas 非常感谢,因为这看起来不错,我目前正在测试我的结果。我有一个小疑问,即在哪里包含我的 where 条件,如where PRODUCT_REG_NO='FLDG'?它应该在start_date is not null 之后或在last order by 2, 1 desc 上方 问候
  • 如果你想过滤最终结果,它可能刚好在最后一个订单之上
  • 因为我正在测试结果,请检查以下内容,我没有执行任何结果SQL Fiddle 其中select * from products p 有记录谢谢
  • 这都是关于排序的。在您的问题中,您一直按 product_no 和 product_reg_no 排序。在你给我的最后一个例子中,你是按 start_date (3) 订购的(这里是一个例子sqlfiddle.com/#!4/4915c/28)。您只需决定要使用哪些列对数据进行排序
【解决方案2】:

在 SQL 中,date + X 将 X 天添加到日期。所以你可以:

select  * 
from    products
where   start_date + 1 = nvl(end_date, return_date)

如果日期可能包含时间部分,请使用trunc 删除时间部分:

select  * 
from    products
where   trunc(start_date) + 1 = trunc(nvl(end_date, return_date))

Live example at SQL Fiddle.

【讨论】:

  • 我正在寻找的是那些 start_datenvl(return_date,end_date) 之差为 1 的行。即当前行的 start_date 和前一行的 nvl(return_date,end_date) 应该是一个,例如PRODUCT_REG_NO FLDGPRODUCT_NO TSH098 start_datePRODUCT_NO TSH128 nvl(return_date,end_date) 是一个。我希望我解释得当。
  • 啊哈,我错过了“上一行”部分。现在得走了,但看看lag function
【解决方案3】:

我的印象是,如果产品注册不匹配,您只希望匹配日期相差 1 天。所以我只是简单地联合它,我认为这就是你想要的

select p1.product_reg_no,
       p1.product_no product_no_1,
       p2.product_no product_no_2,
       p1.start_date start_date_1,
       nvl(p2.return_date,p2.end_date) return_or_end_date_2
  from products p1
  join products p2 on (p1.product_reg_no = p2.product_reg_no)
where  p1.start_date-1 = nvl(p2.return_date,p2.end_date)

SQL Fiddle

如果我对分组有误,那么只需离开连接条件,这与给定的示例产品表会带来相同的结果

select p1.product_reg_no,
       p1.product_no product_no_1,
       p2.product_no product_no_2,
       p1.start_date start_date_1,
       nvl(p2.return_date,p2.end_date) return_or_end_date_2
  from products p1, products p2
where  p1.start_date-1 = nvl(p2.return_date,p2.end_date)

SQL Fiddle 2

现在你说差是 1 天。我自动假设 start_date 比 nvl(return_date,end_date) 高 1 天。我还假设日期总是午夜。但是要排除所有这些,您可以使用 trunc 并双向进行:

select p1.product_reg_no,
       p1.product_no product_no_1,
       p2.product_no product_no_2,
       p1.start_date start_date_1,
       nvl(p2.return_date,p2.end_date) return_or_end_date_2
  from products p1, products p2
where  trunc(p1.start_date)-1 = trunc(nvl(p2.return_date,p2.end_date))
    or trunc(p1.start_date)+1 = trunc(nvl(p2.return_date,p2.end_date))

SQL Fiddle 3

这一切都有效,因为日期(不是时间戳)可以通过加减来计算。

编辑:根据您的评论,您希望比较 return_date or end_date 并且还需要相同的日期:

select p1.product_reg_no,
       p1.product_no product_no_1,
       p2.product_no product_no_2,
       p1.start_date start_date_1,
       p2.return_date return_date_2,
       p2.end_date end_date_2
  from products p1, products p2
where  trunc(p1.start_date)   = trunc(p2.return_date)
    or trunc(p1.start_date)-1 = trunc(p2.return_date)
    or trunc(p1.start_date)+1 = trunc(p2.return_date)
    or trunc(p1.start_date)   = trunc(p2.end_date)
    or trunc(p1.start_date)-1 = trunc(p2.end_date)
    or trunc(p1.start_date)+1 = trunc(p2.end_date)

SQL Fiddle 4

【讨论】:

  • 感谢您的帮助。我面临的一个主要问题是这个。对于PRODUCT_REG_NO='FLDG',有PRODUCT_NO TSH098TSH12start_datenvl(return_date, end_date) 相差一,但未选择。我想选择所有日期差为 1 的记录。我怎样才能做到这一点?问候
  • TSH098return_date 2012 年 5 月 31 日。 TSH012 的 start_date 为 2012 年 6 月 18 日。所以条件不匹配。 NVL 确实说取第一个值,但如果它为空,则取第二个值。但我想我猜你想要什么让我再添加一个。
  • @Polppan 请查看我的编辑。顺便一提。您只需要 PRODUCT_REG_NO 内的匹配项还是整个表中的匹配项?
  • Hol 匹配应在 PRODUCT_REG_NO 内。
  • @Polppan "TSH098 和 TSH12 的 start_date 和 nvl(return_date, end_date) 相差 1" 他们没有相差 1。TSH098 start_date 和 TSH012 (没有 TSH12) end_date 差为 0。您能详细说明为什么您认为应该选择它们吗?
【解决方案4】:

比较当前行与前一行的方法是使用 LAG() 函数。像这样的:

select * from 
(
select p.* 
       , lag (end_date) over 
         (order by start_date )
         as prev_end_date
       , lag (return_date) over 
         (order by start_date )
         as prev_return_date
from products p
)
where (trunc(start_date) - 1) = trunc(nvl(prev_return_date, prev_end_date))
order by 2,1 desc

但是,这不会返回您想要的结果,因为您尚未定义用于定义排序顺序的机制。如果没有排序顺序,“前一行”的概念就毫无意义。

但是,您可以这样做:

select p1.* 
       , p2.*
from products p1 cross join products p2
where (trunc(p2.start_date) - 1) = trunc(nvl(p1.return_date, p1.end_date))
order by 2, 1 desc

此 SQL 查询您的表两次,根据日期进行过滤。结果集中的每一行都包含来自每个表的记录。如果给定的 start_date 与多个 end_date 匹配,反之亦然,您将获得多次点击的记录。

【讨论】:

  • 不确定你为什么要执行CROSS JOIN?外行没有任何线索。做INNER JOIN 还不够:FROM p1 JOIN p2 ON (trunc(p2.start_date) - 1) = trunc(nvl(p1.return_date, p1.end_date))
  • @WojtusJ - 两种解决方案都返回相同的结果。我更喜欢 CROSS JOIN,因为它更好地传达了意图;在我看来,INNER JOIN 意味着一种键控关系。但您的品味或解释可能会有所不同。
  • 我明白了,这实际上是有道理的。 :-)
  • @WojtusJ - 也许 WITH 子句混淆了这个问题:它们是早期尝试解决它的遗留物,我应该完全重构它们(我现在已经这样做了)。
【解决方案5】:

你的意思是这样的?

SELECT T2.*
FROM PRODUCTS T1
    JOIN PRODUCTS T2 ON (
        nvl(T1.end_date, T1.return_date) + 1 = T2.start_date
    );

your SQL Fiddle example,它返回:

PRODUCT_NO    PRODUCT_REG_NO    START_DATE                          END_DATE                            RETURN_DATE
TSH128        FLDG              August, 16 2012 00:00:00-0400       September, 15 2012 00:00:00-0400    (null)
TSH125        SCRW              August, 08 2012 00:00:00-0400       September, 07 2012 00:00:00-0400    (null)
TSH137        SCRW              September, 08 2012 00:00:00-0400    October, 07 2012 00:00:00-0400      (null)
  • TSH128 因您已经解释的原因被退回。
  • 返回 TSH125,因为 TSH116 end_date 是 2012 年 8 月 7 日。
  • 返回 TSH137,因为 TSH125 end_date 是 2012 年 9 月 7 日。

如果您只想比较同一 product_reg_no 中的行,可以很容易地将其添加到 JOIN 条件中。如果您想要 1 天差异的两个“方向”,也可以轻松添加。

【讨论】:

  • 我只需要那些当前行的开始日期和上一行的差异是一个匹配 PRODUCT_REG_NO 的行。谢谢
  • @Polppan 很好,只需将其添加到 JOIN 条件中,正如我已经提到的。顺便说一句,“先前”一词仅在订购的上下文中才有意义。您的订单是什么?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-04-24
  • 2018-02-09
  • 2017-04-04
  • 2016-07-10
  • 1970-01-01
  • 2023-03-26
  • 2023-01-15
相关资源
最近更新 更多