查看: 1752|回复: 3

[每日一题] PL/SQL Challenge 每日一题:2018-7-12 LAST_DAY函数

[复制链接]
论坛徽章:
520
奥运会纪念徽章:垒球
日期:2008-09-15 01:28:12生肖徽章2007版:鸡
日期:2008-11-17 23:40:58生肖徽章2007版:马
日期:2008-11-18 05:09:48数据库板块每日发贴之星
日期:2008-11-29 01:01:02数据库板块每日发贴之星
日期:2008-12-05 01:01:03生肖徽章2007版:虎
日期:2008-12-10 07:47:462009新春纪念徽章
日期:2009-01-04 14:52:28数据库板块每日发贴之星
日期:2009-02-08 01:01:03生肖徽章2007版:蛇
日期:2009-03-09 22:18:532009日食纪念
日期:2009-07-22 09:30:00
跳转到指定楼层
1#
发表于 2018-7-17 03:49 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

最先答对且答案未经编辑的puber将获得纪念章一枚(答案不可编辑但可发新贴补充或纠正),其他会员如果提供有价值的分析、讨论也可获得纪念章一枚。

每两周的优胜者可获得itpub奖励的技术图书一本。

以往旧题索引:
http://www.itpub.net/forum.php?m ... eid&typeid=1808

原始出处:
http://www.plsqlchallenge.com/

作者: Kim Berg Hansen

运行环境:SQLPLUS, SERVEROUTPUT已打开
注:本题给出答案时候要求给予简要说明才能得到奖品

我有一张发票表:

create table qz_invoices (
   id       integer primary key
, inv_date date
, customer varchar2(20)
, amount   number
)
/

-- For implicit conversion in INSERT and output format in choices
alter session set nls_date_format = 'YYYY-MM-DD'
/

insert into qz_invoices values (41, '2018-01-30', 'Andys Pub', 299)
/
insert into qz_invoices values (42, '2018-02-28', 'Ajax Inc' , 299)
/
insert into qz_invoices values (43, '2018-03-01', 'Acme Corp', 149)
/
insert into qz_invoices values (44, '2018-04-03', 'Adams Bar', 442)
/
commit
/

支付条款是发票必须在发票日期当月的最后一天支付。

这当然会使得一些人的信用额度比其他人更长,可能有点不公平。因此,为了获取这个情况,我想要一张发票清单,显示每张发票有多少天的信用额度。换句话说,从发票日期到付款日期(该月的最后一天)的天数。

为了做这个清单,我写了这个未完成的查询:

select id
     , inv_date
     , ##REPLACE##
          as credit_days
     , customer
     , amount
  from qz_invoices
order by id
/


哪些选项包含了一个列别名为CREDIT_DAYS的表达式,可以用来取代 ##REPLACE## 使得查询返回非个所需的输出:

        ID INV_DATE   CREDIT_DAYS CUSTOMER                 AMOUNT
---------- ---------- ----------- -------------------- ----------
        41 2018-01-30           1 Andys Pub                   299
        42 2018-02-28           0 Ajax Inc                    299
        43 2018-03-01          30 Acme Corp                   149
        44 2018-04-03          27 Adams Bar                   442

(A)
last_day(inv_date) - inv_date

(B)
trunc(add_months(inv_date, 1), 'MM') - 1 - inv_date

(C)
add_months(trunc(inv_date, 'MM'), 1) - 1 - inv_date

(D)
trunc(inv_date + interval '1' month, 'MM')
- interval '1' day - inv_date

(E)
trunc(inv_date, 'MM') + interval '1' month
- interval '1' day - inv_date

(F)
31 - extract(day from inv_date)

(G)
extract(day from last_day(inv_date))
- extract(day from inv_date)
论坛徽章:
8
秀才
日期:2018-07-23 13:55:51秀才
日期:2018-07-23 14:00:48技术图书徽章
日期:2018-07-23 14:01:23秀才
日期:2018-07-23 14:01:23秀才
日期:2018-08-31 10:37:01秀才
日期:2018-08-31 10:37:20技术图书徽章
日期:2018-08-31 10:37:44秀才
日期:2018-08-31 10:37:44
2#
发表于 2018-7-17 09:10 | 只看该作者
答案ABCEG
A:LAST_DAY函数返回指定日期对应月份的最后一天。没问题
B:先通过add_months获取下一个月的日期(原则上这里的日不会变,如果开始日期是某月的最后一天,那么,结果将会调整以使返回值仍对应新的一月的最后一天。如果,结果月份的天数比开始月份的天数少,那么,也会向回调整以适应有效日期),再用trunc获取下个月的第一天,再减1天也会获得本月的最后一天。运算也没问题
C:跟B选项有点类似,先trunc获取本月的第一天-->通过add_months获取下个月的第一天-->减掉1回到本月的最后一天。也没问题
D:通过inerval计算下个月的同一天,但id为41,下个月也就是2月份没有30号的。会报错
E:trunc取当月的第一天,再通过inerval取下个月的第一天(这里不用担心D的情况了,每个月都有第1天),再取上个月的最后一天。没问题
F:extract(day from inv_date),取本月所在的日,但不是每个月都有31天啊。ID为42,44的计算有偏差
G:本月的最有一天减去发票开票日期所在日,没问题

使用道具 举报

回复
论坛徽章:
0
3#
发表于 2018-7-17 09:25 | 只看该作者
本帖最后由 jinyetongxiao 于 2018-7-17 09:29 编辑

ABCEG
A last_day返回指定日期所在月份的最后一天
B trunc截取到加1月后的第一天(即下一个月的第一天),再减一天之后为上一个月最后一天
C 与B逻辑类似,只是先取发票当月第一天,再取下一个月减去一天(即发票当月最后一天)
D 有语法错误,20180130 interval '1' month 是20180230,日期格式错误
E interval 进行日期计算
F extract 提取日期里的某一部分,直接用31减不对,2月就没31
G 截取本月一共多少天减去发票的日期

使用道具 举报

回复
论坛徽章:
520
奥运会纪念徽章:垒球
日期:2008-09-15 01:28:12生肖徽章2007版:鸡
日期:2008-11-17 23:40:58生肖徽章2007版:马
日期:2008-11-18 05:09:48数据库板块每日发贴之星
日期:2008-11-29 01:01:02数据库板块每日发贴之星
日期:2008-12-05 01:01:03生肖徽章2007版:虎
日期:2008-12-10 07:47:462009新春纪念徽章
日期:2009-01-04 14:52:28数据库板块每日发贴之星
日期:2009-02-08 01:01:03生肖徽章2007版:蛇
日期:2009-03-09 22:18:532009日食纪念
日期:2009-07-22 09:30:00
4#
 楼主| 发表于 2018-7-18 06:57 | 只看该作者
答案ABCEG, 2楼得奖。

A:(推荐) LAST_DAY函数返回当月的最后一天,这恰恰就是我们所需要的。
B: 将当前日期加上一个月,然后在用MM参数执行truncate,这会给出下个月的第一天。然后再减去一天就得到当月的最后一天,所以我们得到的结果和前一选项相同。
C: 在加上一个月之前先执行MM参数的truncate也会给出下个月的第一天,所以结果和前一选项相同。
D:
原则上,这与选项B相同,但利用INTERVAL增加一个月时,如果当一个月的最后几天的天数在目标月份中不存在,这种情况就无法处理。在这种情况下,为1月30日增加一个月会得到2月30日,而这是不存在的,因此会引发错误:ORA-01839: date not valid for month specified
选择B中的ADD_MONTHS函数对这种情况有特殊处理。
E: 但是,如果我们将“操作顺序”对调一下,先执行TRUNCATE,它总是会得到我们要增加INTERVAL的当月的第一天,所以这是被支持的并且十分可行。
F: 因为不是所有的月份都有31天,这是一个太过原始的计算,会给我们这个错误的输出:

        ID INV_DATE   CREDIT_DAYS CUSTOMER                 AMOUNT
---------- ---------- ----------- -------------------- ----------
        41 2018-01-30           1 Andys Pub                   299
        42 2018-02-28           3 Ajax Inc                    299
        43 2018-03-01          30 Acme Corp                   149
        44 2018-04-03          28 Adams Bar                   442
G: 但是,利用LAST_DAY使得我们能够取出当月的正确的天数,从而计算出所需的结果。

使用道具 举报

回复

您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

TOP技术积分榜 社区积分榜 徽章 团队 统计 知识索引树 积分竞拍 文本模式 帮助
  ITPUB首页 | ITPUB论坛 | 数据库技术 | 企业信息化 | 开发技术 | 微软技术 | 软件工程与项目管理 | IBM技术园地 | 行业纵向讨论 | IT招聘 | IT文档
  ChinaUnix | ChinaUnix博客 | ChinaUnix论坛
CopyRight 1999-2011 itpub.net All Right Reserved. 北京盛拓优讯信息技术有限公司版权所有 联系我们 未成年人举报专区 
京ICP备16024965号-8  北京市公安局海淀分局网监中心备案编号:11010802021510 广播电视节目制作经营许可证:编号(京)字第1149号
  
快速回复 返回顶部 返回列表