maximum date less than the quarter date - sql

I have table days table.There is oper_day column:
oper_day
01.01.2021
02.01.2021
03.01.2021
**********
**********
31.12.2022
I want to output the maximum date available in a table that is less than the first date of each quarter for example: quarter_date: 01.10.2022 if 30.09.2022 has in a table I give 30.09.2022 else 29.09.2022 .How can I write query?

One could accomplish this by putting the dates into groups by quarter. Then finding the MAX date for that quarter.
SELECT ADD_MONTHS(TRUNC(td.oper_day, 'Q'), 3) next_quarter_begin
,MAX(td.oper_day) max_date_prev_quarter
FROM table_days td
GROUP BY ADD_MONTHS(TRUNC(td.oper_day, 'Q'), 3)
ORDER BY 1

You appear to be asking how to find the last date in each quarter, if so then GROUP BY the data TRUNCated to the start of the quarter and find the maximum date in each quarter (as it will be the latest date before the start of the next quarter).
If you want to ensure that the values are from quarters before the current quarter then you can add a WHERE filter to ignore values in the current quarter or later.
Like this:
SELECT TO_CHAR(TRUNC(oper_day, 'Q'), 'YYYY-Q') AS quarter,
MAX(oper_day) AS last_oper_day_in_quarter
FROM table_name
WHERE oper_day < TRUNC(SYSDATE, 'Q')
GROUP BY TRUNC(oper_day, 'Q')
Which, for the sample data:
CREATE TABLE table_name (oper_day) AS
SELECT DATE '2022-01-01' + LEVEL - 1
FROM DUAL
CONNECT BY DATE '2022-01-01' + LEVEL - 1 <= SYSDATE;
Outputs:
QUARTER
LAST_OPER_DAY_IN_QUARTER
2022-1
31-MAR-22
2022-2
30-JUN-22
2022-3
30-SEP-22
fiddle

Related

DB2 query to fetch last month of data. on current month

Every month I have to fetch records for the previous month from a Db2 database. How can I write a Db2 query to fetch the last month of data without hard-coding the date range? For example, when run in December 2021, the query would return records dated between '2021-11-01' AND '2021-11-30', and those dates would change dynamically when I run the same query a month later.
It's easy to can precompute the date range in a cte and then use it in the main query. Assuming your table t has a ts column to filter by, you can do:
with
r as (
select
to_date(year(c) || '-' || month(c) || '-01' , 'YYYY-MM-DD') as e,
to_date(year(p) || '-' || month(p) || '-01' , 'YYYY-MM-DD') as b
from (
select current date as c, current date - 1 month as p from sysibm.sysdummy1
) x
)
select *
from t
cross join r
where t.ts >= r.b and t.ts < r.e
See example at db<>fiddle.
There are a few ways to describe the prior month as a date range in a Db2 SQL query. Some datetime SQL functions are not available on Db2 for z/OS, but even then you can still use date arithmetic and the LAST_DAY() function.
First day of last month: LAST_DAY(CURRENT DATE - 2 MONTHS) + 1 DAY
Last day of last month: LAST_DAY(CURRENT DATE - 1 MONTH)
First day of this month: LAST_DAY(CURRENT DATE - 1 MONTH) + 1 DAY
Inclusive-exclusive example (preferred approach):
SELECT ... WHERE someDateTimeColumn >= LAST_DAY(CURRENT DATE - 2 MONTHS) + 1 DAY
AND someDateTimeColumn < LAST_DAY(CURRENT DATE - 1 MONTH) + 1 DAY
Inclusive-inclusive example (calling the DATE() function will prevent implicit type conversion which could skip some some qualifying rows):
SELECT ... WHERE someDateTimeColumn >= LAST_DAY(CURRENT DATE - 2 MONTHS) + 1 DAY
AND DATE(someDateTimeColumn) <= LAST_DAY(CURRENT DATE - 1 MONTH)
If you're querying Db2 for LUW v11.1 or newer, you can also call the THIS_MONTH() function to get the first day of an input month.
First day of last month: THIS_MONTH(CURRENT DATE - 1 MONTH)
First day of this month: THIS_MONTH(CURRENT DATE)
Inclusive-exclusive example:
SELECT ... WHERE someDateTimeColumn >= THIS_MONTH(CURRENT DATE - 1 MONTH)
AND someDateTimeColumn < THIS_MONTH(CURRENT DATE)

How to assign mm/dd/yyyy records to associated Fiscal month found in another table

Note: this question is for both SQL and ORACLE and we do not have permissions for creation of temp table or stored procedures.
The database has two tables.
One table has a field of End Dates of Months along with a text field which identifies the "Fiscal Month" label.
Second table has dates by day (mm/dd/yyyy) with numeric data associated.
We need to retrieve the second table data (summing the numerics) grouping by the associated "Fiscal Month" found in table One.
Within one query or using CTE or a better solution, how to perform some kind of lookup on Table One to retrieve the Fiscal Month that the mm/dd/yyy date in Table two should be grouped on.
Table 1 (Fiscal Month End Dates)
2015-05-29 - Fiscal Month is 'May2015'
2015-06-30 - Fiscal Month is 'Jun2015'
2015-07-31 - Fiscal Month is 'Jul2015'
Table 2 (mm/dd/yyyy) which needs to be summed and grouped by Fiscal Month
2015-05-29 should be grouped on 'May2015'
2015-06-30 should be grouped on 'Jun2015'
So the approach I have used is to create a range of dates for a particular fiscal month.
Since you have the last dates of each fiscal month, you can get the previous one as the end of the previous fiscal month. Of course this is with a starting hard-limit because the first month in the fiscal month table will not have the previous month date (I used 1st of Jan of the year).
Then when you join it with your daily data, you can use these two dates to determine which fiscal month the data belongs to.
To get the previous fiscal month end date, we can use the LAG analytic function using the month to order the rows.
The rest of the query is pretty straightforward.
WITH
fiscal_end_dates
AS
(SELECT TO_DATE ('2015-05-29', 'YYYY-MM-DD') AS last_date FROM DUAL
UNION ALL
SELECT TO_DATE ('2015-06-30', 'YYYY-MM-DD') AS last_date FROM DUAL
UNION ALL
SELECT TO_DATE ('2015-07-31', 'YYYY-MM-DD') AS last_date FROM DUAL),
daily_data
AS
(SELECT TO_DATE ('2015-05-29', 'YYYY-MM-DD') AS data_date,
10 AS some_value
FROM DUAL
UNION ALL
SELECT TO_DATE ('2015-05-30', 'YYYY-MM-DD') AS data_date,
14 AS some_value
FROM DUAL
UNION ALL
SELECT TO_DATE ('2015-06-20', 'YYYY-MM-DD') AS data_date,
34 AS some_value
FROM DUAL
UNION ALL
SELECT TO_DATE ('2015-07-04', 'YYYY-MM-DD') AS data_date,
34 AS some_value
FROM DUAL),
fiscal_date_range
AS
(SELECT last_date,
NVL (
LAG (last_date, 1) OVER (ORDER BY EXTRACT (MONTH FROM last_date)),
TO_DATE (EXTRACT (YEAR FROM last_date) || '-01-01', 'YYYY-MM-DD')
)
AS prev_month_fiscal_end_date,
INITCAP (TO_CHAR (last_date, 'MON')) || EXTRACT (YEAR FROM last_date)
AS fiscal_month
FROM fiscal_end_dates)
SELECT dd.*,
fdr.fiscal_month
FROM daily_data dd,
fiscal_date_range fdr
WHERE dd.data_date > fdr.prev_month_fiscal_end_date
AND dd.data_date <= fdr.last_date;
This is the result (I took the liberty of adding a few more rows in your daily data table just to show the query working)
DATA_DATE SOME_VALUE FISCAL_MONTH
5/29/2015 10 May2015
5/30/2015 14 Jun2015
6/20/2015 34 Jun2015
7/4/2015 34 Jul2015
All you need to do now is to use the result set and perform your grouping and aggregation.

SQL to check current date with column of type string which stores date in the format MMDD is not 120 days old

Oracle table in my application has a column with name "transaction_date" of type string. It stores date in the format MMDD, where MM = month and DD = day.
Please help me to write a SQL statement which will compare the transaction_date column with the current system date, if transaction_date is less than or equal to 120 days, then fetch the records from the table.
The problem I am facing is, transaction_date in db does not have year just month and day as a string value, so how to check if that value is not more than 120 days, that check should work if value in column is of previous year. For example, SQL should work for the scenario where current system date is lets say 01 feb 2018, and the transaction_date column in table has value "1225" (25th dec of previous year).
As a general disclaimer, your current table design is sub optimal, because a) you are storing dates as text, and b) you are not even storing the year for each date. From what you wrote, it looks like you want to consider all data as having occurred within the last year, from the current date.
One trick we can try here is to compare the MMDD text for each record in your table against TO_CHAR(SYSDATE, 'MMDD'), using the following logic:
If the MMDD is less than or equal to today, then it gets assigned to current year (2018 as of the time of writing this answer)
If the MMDD is greater than today, then it gets assigned to previous year (2017).
Then, we may build dates for each record using the appropriate year and check if it is within 120 days of SYSDATE.
WITH yourTable AS (
SELECT '0101' AS date_col FROM dual UNION ALL
SELECT '1001' FROM dual UNION ALL
SELECT '1027' FROM dual UNION ALL
SELECT '1215' FROM dual
)
SELECT
date_col
FROM yourTable
WHERE
(date_col <= TO_CHAR(SYSDATE, 'MMDD') AND
TO_DATE(date_col || TO_CHAR(SYSDATE, 'YYYY'), 'MMDDYYYY') >= SYSDATE - 120) OR
(date_col > TO_CHAR(SYSDATE, 'MMDD') AND
TO_DATE(date_col ||
TO_CHAR(TRUNC(ADD_MONTHS(SYSDATE, -12), 'YEAR'), 'YYYY'), 'MMDDYYYY') >=
SYSDATE - 120);
Demo

select Last date of next year in oracle

How to get the last date of next year in oracle sql?
Last date of next year is one day before the first day of the year after next. So you should be able to get it by adding 24 months to the first day of THIS year, and then subtracting one day. Like this:
select sysdate, add_months(trunc(sysdate, 'y'), 24) - 1 as last_day_next_year from dual;
SYSDATE LAST_DAY_NEXT_YEAR
---------- ------------------
2016-08-04 2017-12-31
1 row selected.
Try this:
select add_months(to_date('31-DEC-' || to_char(current_date, 'YYYY')),12) from dual;
Something like...
Select last_day(add_months(sysdate, 12 + (12- to_number(to_char(sysdate, 'mm'))))) from dual;

Given a date, select all the week numbers that month contains

I have an ApEx application for tracking working hours.
I have a view that looks like this:
CREATE OR REPLACE VIEW HOURSDAYS
AS SELECT
(MAX("TO_X") - MIN("FROM_X"))*24 -
(max(case when PROJECT_ID = 999 then to_x else to_date('01012000','DDMMYYYY') end) -
max(case when PROJECT_ID = 999 then from_x else to_date('01012000','DDMMYYYY') end))*24 AS TIME_SPENT,
DAY,
PERSON_ID
FROM ATTENDANCE_HOURS
GROUP BY PERSON_ID, DAY
ORDER BY DAY DESC
I need the sum of hours per week. So I have this:
SELECT TO_CHAR(DAY,'IW'), MIN(DAY), MAX(DAY), SUM(TIME_SPENT)
FROM HOURSDAYS
WHERE PERSON_ID = (SELECT ID FROM ATTENDANCE_PEOPLE WHERE MAIL_SSO = V('APP_USER') AND ROLE = 0) AND
EXTRACT (YEAR FROM DAY) = EXTRACT (YEAR FROM TO_DATE(:P100_DATE_PICKER,'DD-MON-RR'))
GROUP BY TO_CHAR(DAY,'IW')
ORDER BY TO_CHAR(DAY,'IW') ASC
And now the fun begins: I have a page item P100_DATE_PICKER and I need to display only those weeks that have > 0 days that belong to the month to which belongs the day I've picked using the date picker.
For example, for 1.1.2015 I want only weeks 1, 2, 3, 4, 5 displayed. 24.3.2015: 9, 10, 11, 12, 13, 14.
If anyone is interested in why would I do that, it is for validation - number of work hours per week cannot exceed 20/32, depends on what type of contract do you have.
You can do like this:
where day >= trunc(trunc(TO_DATE(:P100_DATE_PICKER,'DD-MON-RR'), 'MM'),'IW')
and day < trunc(last_day(TO_DATE(:P100_DATE_PICKER,'DD-MON-RR')),'IW') + 7
The first line starts by trunc to MM, which gives you the first day of the month of your date. Then trunc again to IW gives you the date of the monday in the week that contains the first day of month.
The second line uses last_day to get the last day of the month of your date. Then trunc to IW gets the monday of that week, and adding 7 days get the monday after. Then by using < rather than <= you get the desired result.
Also I suggest you do not group and order by TO_CHAR(DAY,'IW') but instead use TRUNC(DAY,'IW') for your grouping and ordering. Otherwise you can get into problems around new year, because your code for example when showing the weeks for December 2014 would have to show 49 to 52 of year 2014 as well as week 1 of year 2015. If you use TO_CHAR that week 1 would be wrongly sorted before weeks 49 to 52.
Similarly do not use EXTRACT(YEAR FROM ...) or even TO_CHAR(...,'YYYY') when you are working with ISO weeks. Because the date '2014-12-31' belongs to ISO week 1 year 2015, there is a special date format string 'IYYY' which gives the correct year for the ISO week. Try it out and see the difference between 'YYYY' and 'IYYY'.
This will not answer all your questions. This is the quick example of week calculation from a given date according to the subject line of your question. Pls copy/paste to see results. Also, here's the link to check the week numbers:
http://www.epochconverter.com/date-and-time/weeknumbers-by-year.php
SELECT given_date
, end_date
, TRUNC(calc_start_date, 'iw') wk_starts
, TRUNC(calc_start_date, 'iw') + 7 - 1/86400 wk_ends
, TO_CHAR(calc_start_date, 'iw') wk_number
, calc_start_date
FROM
(
SELECT trunc(sysdate, 'mm') given_date
, trunc(sysdate, 'mm')-7 + LEVEL*7 AS calc_start_date
, Last_Day(trunc(SYSDATE, 'mm')) end_date
FROM dual
CONNECT BY LEVEL <= ROUND((trunc(last_day(sysdate)) - trunc(sysdate, 'mm')+7)/7) -- number of weeks --
)
/
GIVEN DATE END DATE WK_STARTS WK_ENDS WK NUMBER
-------------------------------------------------------------------------
1/1/2015 1/31/2015 12/29/2014 1/4/2015 11:59:59 PM 01
1/1/2015 1/31/2015 1/5/2015 1/11/2015 11:59:59 PM 02
1/1/2015 1/31/2015 1/12/2015 1/18/2015 11:59:59 PM 03
1/1/2015 1/31/2015 1/19/2015 1/25/2015 11:59:59 PM 04
1/1/2015 1/31/2015 1/26/2015 2/1/2015 11:59:59 PM 05
The best way to achieve the last part is to have a calendar table that has the following columns
Date, Day eg Mon, Month, Quarter, Year, WeekNo
Then for a given date you can simply select all the weeks that belong to the month.
This allows you to define weekNo so that it fits in with company circumstances. Eg occasionally you may get a 53 week year, especially if they operate 4-4-5 accounting periods
with days as (
select
to_number(to_char(trunc(to_date('2015-03-24', 'yyyy-MM-dd'), 'month'), 'WW')) w1,
to_number(to_char(last_day(to_date('2015-03-24', 'yyyy-MM-dd')), 'WW')) w2
from dual)
select w1+level-1 week from days
connect by level <= w2-w1+1
Subquery days gives minimum and maximum week, query with connect by returns numbers between these values. Please replace example date with :P100_DATE_PICKER.