按季度获取工作天数Oracle SQL

rn0zuynd  于 2023-08-03  发布在  Oracle
关注(0)|答案(3)|浏览(126)

我如何得到下面的结果,查询将通过给出开始和结束日期来运行,例如用户可以从1/1/2023 - 6/20/2023运行此查询
表数据:

employee  date_from  date_to   
x         1/1/2023   2/9/2023
y         10/1/2023  3/30/2023

字符集
预期结果:

employee  date_from  date_to       year   quarter                   comments
X         1/1/2023   2/9/2023     2023      q1         Employee X was active 40 out of the 90 days.
y         10/1/2023  3/30/2023    2022      q4         Employee y was active 90 out of the 90 days.
y         10/1/2023  3/30/2023    2023      q1         Employee y was active 90 out of the 90 days.


我需要这个与PL/SQL这是可能的吗?
谢谢你,谢谢

ni65a41a

ni65a41a1#

不需要生成开始日期和结束日期之间的所有天数;相反,您可以更高效地使用递归查询生成连续季度:

WITH quarters (employee, date_from, quarter_start, date_to) AS (
  SELECT employee,
         date_from,
         TRUNC(date_from, 'Q'),
         date_to
  FROM   employees
UNION ALL
  SELECT employee,
         date_from,
         ADD_MONTHS(quarter_start, 3),
         date_to
  FROM   quarters
  WHERE  ADD_MONTHS(quarter_start, 3) <= date_to
)
SEARCH DEPTH FIRST BY employee SET order_id
SELECT employee,
       date_from,
       date_to,
       EXTRACT(YEAR FROM quarter_start) AS year,
       TO_CHAR(quarter_start, '"Q"Q') AS quarter,
       LEAST(date_to + 1, ADD_MONTHS(quarter_start, 3))
       - GREATEST(quarter_start, date_from) AS days,
       'Employee ' || employee || ' was employed for '
       || (LEAST(date_to + 1, ADD_MONTHS(quarter_start, 3))
          - GREATEST(quarter_start, date_from))
       || ' out of the ' || (ADD_MONTHS(quarter_start, 3) - quarter_start)
       || ' days.' AS comments
FROM   quarters

字符集
其中,对于样本数据:

CREATE TABLE employees (employee, date_from, date_to) AS
SELECT 'x', DATE '2023-01-01', DATE '2023-02-09' FROM DUAL UNION ALL
SELECT 'y', DATE '2022-10-01', DATE '2023-03-03' FROM DUAL;


输出:
| 日期_从|日期至|年份|季度|天数|评论| COMMENTS |
| --|--|--|--|--|--| ------------ |
| 2019 -01- 21 00:00:00| 2019 -02- 29 00:00:00|二〇二三|Q1|四十|员工X在90天中被雇用了40天。||
| 2019 -01 - 12 00:00:00| 2019 -03- 23 00:00:00|二〇二二|Q4|九十二个|雇员y在92天中被雇用了92天。||
| 2019 -01 - 12 00:00:00| 2019 -03- 23 00:00:00|二〇二三|Q1|六十二|雇员y在90天中被雇用了62天。||
fiddle

1aaf6o9v

1aaf6o9v2#

可以使用以下查询获取两个给定日期之间的所有日期

SELECT TRUNC(:start_date) + (LEVEL - 1) AS query_date
      FROM dual
      CONNECT BY LEVEL <= TRUNC(:end_date) - TRUNC(:start_date) + 1

字符集
你也可以过滤出星期日添加(我认为编号是国家特定)

where TO_CHAR(dr.query_date, 'D' ) not in (7)


您可以使用TO_CHAR(dr.query_date , 'Q')获取给定日期的季度
因此,最终的查询应该看起来像这样:

WITH date_range AS (
  SELECT TRUNC(:start_date) + (LEVEL - 1) AS query_date
  FROM dual
  CONNECT BY LEVEL <= TRUNC(:end_date) - TRUNC(:start_date) + 1
),
employee as ( 
select 'x' as  employee,       to_date('1/1/2023','dd/mm/rrrr')  date_from,   to_date('9/2/2023','dd/mm/rrrr') date_to from dual
union all 
select 'y',       to_date( '1/10/2022','dd/mm/rrrr'), to_date('30/3/2023','dd/mm/rrrr')   from dual
)
SELECT
  e.employee,
  e.date_from,
  e.date_to,
  EXTRACT(YEAR FROM dr.query_date) AS year,
  'q' || TO_CHAR(dr.query_date , 'Q') AS quarter
  ,'Employee ' || e.employee || ' was active ' || COUNT(dr.query_date) || ' out of the ' || 90 || ' days.' AS comments
FROM
  employee e
JOIN
  date_range dr ON dr.query_date BETWEEN TRUNC(e.date_from) AND TRUNC(e.date_to)
  where TO_CHAR(dr.query_date, 'D' ) not in (7)
GROUP BY
  e.employee,
  e.date_from,
  e.date_to,
  EXTRACT(YEAR FROM dr.query_date),
  'q' || TO_CHAR(dr.query_date, 'Q')
ORDER BY
  e.employee,
  e.date_from;


结果:对于范围:

1/10/2022 - 20/06/2023

x   1/1/2023    9/2/2023    2023    q1  Employee x was active 35 out of the 90 days.
y   1/10/2022   30/3/2023   2022    q4  Employee y was active 78 out of the 90 days.
y   1/10/2022   30/3/2023   2023    q1  Employee y was active 77 out of the 90 days.

xmd2e60i

xmd2e60i3#

是的,没有PLSQL也可以。您需要一个日期表(可以“即时”生成),但存储“日历”并不罕见。例如,您可以从存储或生成的日历中排除周末。然后,您的查询使用该日历作为“from表”,员工是通过落在员工范围的from/to日期内的日期连接的数据,然后通过group by计算每个季度的日历日期。

CREATE TABLE mytable (
  employee VARCHAR2(50),
  date_from DATE,
  date_to DATE
);

INSERT INTO mytable (employee, date_from, date_to) 
  VALUES ('x', TO_DATE('1/1/2023', 'MM/DD/YYYY'), TO_DATE('2/9/2023', 'MM/DD/YYYY'));
INSERT INTO mytable (employee, date_from, date_to) 
  VALUES ('y', TO_DATE('10/1/2023', 'MM/DD/YYYY'), TO_DATE('3/30/2023', 'MM/DD/YYYY'));

WITH d AS (
  SELECT 
    TRUNC(DATE '2022-01-01') + (LEVEL - 1) AS date_value
  FROM 
    dual
  CONNECT BY LEVEL <= (DATE '2023-07-01' - DATE '2022-01-01') + 1
)
SELECT 
    e.employee
  , to_char(d.date_value,'YYYY')  || '-Q' || to_char(d.date_value,'Q')  Qtr
  , count(d.date_value) days
FROM d
  INNER JOIN mytable e on d.date_value between e.date_from and e.date_to
WHERE to_char(d.date_value,'D') in ('1','2','3','4','5') -- Monday to Friday only
GROUP BY
    e.employee
  , to_char(d.date_value,'YYYY')  || '-Q' || to_char(d.date_value,'Q')

字符集
| QTR|天数| DAYS |
| --|--| ------------ |
| 2023年第一季度|二十九| 29 |
fiddle

注意要显示整个起始/终止日期之间的所有季度,如果上面显示的是内部连接,则需要使用LEFT JOIN,并且还需要相应地更改where子句。

相关问题