如果日期值在周末,则重置日期值

Posted

技术标签:

【中文标题】如果日期值在周末,则重置日期值【英文标题】:Resetting a date value if it falls on weekend 【发布时间】:2019-06-20 15:55:49 【问题描述】:

我正在编写一个过程并使用动态游标和 SQL 查询,我将其作为字符串传递到 V_SQL 变量中。查询如下:

在 where 条件下,我正在通过日期,但条件是如果它是月底,并且它落在周五、周六或周日,则将其重置为周四。例如,6 月 30 日将是星期日,因此在 SQL 查询中传递给 day_of_month 的值应该是 27,即从星期四开始的第几天。

能否请您帮我写一个单独的函数是否会好,我应该放什么代码以获得更好的性能和预期的结果。

V_SQL := 'SELECT B.FIN_ELEM, A.ORG_UNIT_ID, A.GL_ACCOUNT_ID, B.CMN_COA_ID, B.PROD1, B.PROD2, B.PROD3, '||
'A.AS_OF_DATE, SUM(CURRENT_BAL) AS CB_SUM, SUM(AVG_BAL) AS AB_SUM, B.FLAG1 FROM DAILYGL A, AL_LOOKUP B '||
'WHERE A.GL_ACCOUNT_ID = B.GL_ACCT ***AND DAY_OF_MONTH = '|| TO_DO_FUNCTION(V_RUN_DATE)***

' AND ROWNUM <=15 GROUP BY B.FIN_ELEM, A.ORG_UNIT_ID, A.GL_ACCOUNT_ID,B.CMN_COA_ID, B.PROD1, B.PROD2, B.PROD3';

DAY_OF_MONTH = '|| TO_DO_FUNCTION(V_RUN_DATE)

如果最后一个工作日是周五、周六或周日,则将传递所需的结果。

【问题讨论】:

好像不需要使用动态SQL...?您还有一个带有 order-by 子句或内联视图的 rownum 过滤器,因此它可能无法达到您的预期。无论如何,如果v_run_date 是 6 月 28 日或 29 日怎么办 - 这些也应该调整到 27 日? 8月-30日、31日应该发生的事情要调整到29日;剩下 9 月 1 日? 【参考方案1】:

你并不需要一个函数,你可以通过一个 case 表达式来实现,比如:

AND DAY_OF_MONTH = case
    when last_day(v_run_date) - v_run_date <= 3
      and to_char(v_run_date, 'Dy', 'NLS_DATE_LANGUAGE=ENGLISH') in ('Fri', 'Sat', 'Sun')
      and to_char(last_day(v_run_date), 'Dy', 'NLS_DATE_LANGUAGE=ENGLISH') in ('Fri', 'Sat', 'Sun')
    then next_day(trunc(v_run_date, 'IW'), 'THURSDAY')
    else v_run_date
  end

也就是说,如果v_run_date 在该月最后一天的三天内,并且 v_run_date 是星期五、星期六或星期日,并且该月的最后一天是周五、周六或周日,然后使用该周的周四日期。

今年全年生成日期的演示:

with cte (v_run_date) as (
  select date '2018-12-31' + level
  from dual
  connect by level <= 365
)
select v_run_date,
  to_char(v_run_date, 'Dy', 'NLS_DATE_LANGUAGE=ENGLISH') as dy,
  case
    when last_day(v_run_date) - v_run_date <= 3
      and to_char(v_run_date, 'Dy', 'NLS_DATE_LANGUAGE=ENGLISH') in ('Fri', 'Sat', 'Sun')
      and to_char(last_day(v_run_date), 'Dy', 'NLS_DATE_LANGUAGE=ENGLISH') in ('Fri', 'Sat', 'Sun')
    then next_day(trunc(v_run_date, 'IW'), 'THURSDAY')
    else v_run_date
  end as adjusted_date
from cte;

V_RUN_DATE DY           ADJUSTED_D
---------- ------------ ----------
2019-01-01 Tue          2019-01-01
2019-01-02 Wed          2019-01-02
...
2019-01-30 Wed          2019-01-30
2019-01-31 Thu          2019-01-31
2019-02-01 Fri          2019-02-01
2019-02-02 Sat          2019-02-02
...
2019-03-26 Tue          2019-03-26
2019-03-27 Wed          2019-03-27
2019-03-28 Thu          2019-03-28
2019-03-29 Fri          2019-03-28
2019-03-30 Sat          2019-03-28
2019-03-31 Sun          2019-03-28
2019-04-01 Mon          2019-04-01
...
2019-04-30 Tue          2019-04-30
2019-05-01 Wed          2019-05-01
...
2019-05-28 Tue          2019-05-28
2019-05-29 Wed          2019-05-29
2019-05-30 Thu          2019-05-30
2019-05-31 Fri          2019-05-30
2019-06-01 Sat          2019-06-01
2019-06-02 Sun          2019-06-02
...
2019-06-26 Wed          2019-06-26
2019-06-27 Thu          2019-06-27
2019-06-28 Fri          2019-06-27
2019-06-29 Sat          2019-06-27
2019-06-30 Sun          2019-06-27
2019-07-01 Mon          2019-07-01
...
2019-07-31 Wed          2019-07-31
2019-08-01 Thu          2019-08-01
...
2019-08-28 Wed          2019-08-28
2019-08-29 Thu          2019-08-29
2019-08-30 Fri          2019-08-29
2019-08-31 Sat          2019-08-29
2019-09-01 Sun          2019-09-01
...
2019-11-27 Wed          2019-11-27
2019-11-28 Thu          2019-11-28
2019-11-29 Fri          2019-11-28
2019-11-30 Sat          2019-11-28
2019-12-01 Sun          2019-12-01
...

db<>fiddle


正如评论中提到的,您的代码似乎不需要是动态的;您可以对所显示的内容使用静态 SQL,例如:

SELECT *
-- into ...
FROM (
  SELECT B.FIN_ELEM, A.ORG_UNIT_ID, A.GL_ACCOUNT_ID,
    B.CMN_COA_ID, B.PROD1, B.PROD2, B.PROD3, A.AS_OF_DATE,
    SUM(CURRENT_BAL) AS CB_SUM, SUM(AVG_BAL) AS AB_SUM, B.FLAG1
  FROM DAILYGL A
  JOIN AL_LOOKUP B ON B.GL_ACCT = A.GL_ACCOUNT_ID
  WHERE DAY_OF_MONTH = case
      when last_day(v_run_date) - v_run_date <= 3
        and to_char(v_run_date, 'Dy', 'NLS_DATE_LANGUAGE=ENGLISH') in ('Fri', 'Sat', 'Sun')
        and to_char(last_day(v_run_date), 'Dy', 'NLS_DATE_LANGUAGE=ENGLISH') in ('Fri', 'Sat', 'Sun')
      then next_day(trunc(v_run_date, 'IW'), 'THURSDAY')
      else v_run_date
    end
  GROUP BY B.FIN_ELEM, A.ORG_UNIT_ID, A.GL_ACCOUNT_ID,B.CMN_COA_ID, B.PROD1, B.PROD2, B.PROD3
  -- order by something...
)
WHERE ROWNUM <= 15;

我也切换到了 ANSI 连接,但更重要的是,将主查询移到了内联视图中,然后对其应用了 rownum 过滤器;除非/直到您在该内联视图中添加 order-by 子句,否则这不会有任何区别。没有它,你的原版将得到一组不确定的(不是完全随机的,但类似的效果)15 行。如果您订购内部查询,那么您将始终根据您的订购条件获得前 15 行。从 12c 开始,您可以使用 a row limiting clause 代替 subquery/rownum 过滤器,但这种方法在早期版本中也可以使用。

显然,您仍然需要将其选择到变量中或将其用作游标查询 - 无论您计划动态执行什么操作。

【讨论】:

【参考方案2】:

希望能解决你的问题

V_RUN_DATE - (case when V_RUN_DATE > LAST_DAY (V_RUN_DATE)- 3 then  V_RUN_DATE else  
 (case TO_CHAR(date V_RUN_DATE, 'DY') when  'FRI' then 1 when 'SAT' then 2 when 'SUN' then 3 else 0 end) end)

【讨论】:

这会在每个周五/周六/周日进行调整,而不仅仅是在月末调整? (还需要注意 NLS 对“DY”产生的影响。) @AlexPoole 添加了月末条件

以上是关于如果日期值在周末,则重置日期值的主要内容,如果未能解决你的问题,请参考以下文章

如果列的值在 Oracle sql 中的格式不同,则使用特定格式更新日期

java 给一个日期,往后加三天,如果是周末,节假日不算天数。直到加满三天为之,得出日期。

如果不是 NAN,则用以前的值替换 Pandas 中的缺失值

在包含多个开始和日期列表的表中查找日期,如果找到则返回默认值

如果在日历外单击,则引导日期选择器删除输入值

MySQL:如果在日期之后达到特定值,则获取所有行