Oracle SQL Query to check valid date range for an activity? - sql

I have a table called as Activity with columns like activity_id and activity_date.
Consider a data in activity table like below,
activity_id activity_date
1 1st June
2 1st July
3 1st August
4 1st September
5 1st October
Now I want to change the date of the activity 3, but I can not change the date to less than 1st July or more than 1st September as there are already some other activities on those dates.
The only valid dates for activity 3 are between 2nd July to 30th August.
Similarly, for activity 1, valid new date can be any date before 1st July.
Similarly, for activity 5, valid new date ranges from 2nd September to any date in future as its last activity.
I need to give the validation message to the user in front end if the new date is not within the range.
Input to the query will be activity id and the new activity date.
Below is the DDL script
CREATE TABLE "HEADCOUNT"."ACTIVITY"
( "ACTIVITY_ID" NUMBER(*,0) NOT NULL,
"ACTIVITY_DATE" DATE
);
Insert into "HEADCOUNT"."ACTIVITY" (ACTIVITY_ID,ACTIVITY_DATE) values (1,'01-06-2012');
Insert into "HEADCOUNT"."ACTIVITY" (ACTIVITY_ID,ACTIVITY_DATE) values (2,'01-07-2012');
Insert into "HEADCOUNT"."ACTIVITY" (ACTIVITY_ID,ACTIVITY_DATE) values (3,'01-08-2012');
Insert into "HEADCOUNT"."ACTIVITY" (ACTIVITY_ID,ACTIVITY_DATE) values (4,'01-09-2012');
Insert into "HEADCOUNT"."ACTIVITY" (ACTIVITY_ID,ACTIVITY_DATE) values (5,'01-10-2012');

This will find the date ranges for each row:
SELECT activity_id, activity_date
,NVL( LAG(activity_date) OVER(ORDER BY activity_id)
,TO_DATE('1900-01-01', 'YYYY-MM-DD')
) AS previous_date
,NVL( LEAD(activity_date) OVER(ORDER BY activity_id)
,TO_DATE('2100-01-01', 'YYYY-MM-DD')
) AS next_date
FROM activity
ORDER BY activity_id
Result:
ACTIVITY_ID ACTIVITY_DATE PREVIOUS_DATE NEXT_DATE
---------------------------------- ------------- ------------- ---------
1 01-JUN-12 01-JAN-00 01-JUL-12
2 01-JUL-12 01-JUN-12 01-AUG-12
3 01-AUG-12 01-JUL-12 01-SEP-12
4 01-SEP-12 01-AUG-12 01-OCT-12
5 01-OCT-12 01-SEP-12 01-JAN-00
Validation would then be for a given id:
"input date" > previous_date AND "input date" < next_date
Date ranges are based on previous and following records when ordered by activity_id. Perhaps the ordering should really be by activity_date, though. Using LAG and LEAD will allow for gaps in activity_ids as well.

Find date limits using a query like below (replace #param_id with changing activity id):
SELECT activity_id, activity_date
FROM activity
WHERE activity_id = #param_id-1
OR activity_id = #param_id+1
This query will return at most two results, but for first and last activities only one will be returned. So, you should read results in the front-end and decide what to do:
Specify beginning limit: Result with id #param_id-1 specifies beginning date limit. If no result with this id, this is first activity and no limit for begin date.
Specify ending limit: Result with id #param_id+1 specifies ending date limit. If no result with this id, this is last activity and no limit for end date.
Do or warn: If new date is within the range, perform change. Otherwise warn the user.

Related

Repeat a record for every day between two dates BigQuery?

I am attempting to produce a table of historical unfulfilled units. Currently, the database captures fulfillment date and order date for a record.
CREATE TABLE `input_table`
(order_name STRING,
line_item_id STRING,
order_date DATE,
fulfillment_date DATE)
Sample Record:
order_name: ABC
line_item_id: 123456
order_date: 2017-04-19
fulfillment_date: 2017-04-25
I want to produce a table that shows the fulfillment status by day, starting with the order date and ending with the date prior to the fulfillment date of each line item, e.g. in the above sample record the output_table would be:
Ultimately, this would allow me to query the count of unfulfilled line items each day:
SELECT
date,
count(line_item_id) AS unfulfilled_line_items
FROM
`output_table`
GROUP BY 1
Indicating the fulfillment status is not strictly necessary, considering it would only include dates in which the status was unfulfilled.
While I could do something like this:
with days as (SELECT
*
FROM
UNNEST(GENERATE_DATE_ARRAY('2017-01-01', CURRENT_DATE(), INTERVAL 1 day)) AS day)
SELECT
*
FROM
`input_table`
JOIN days
ON 1=1
AND order_date <= day
AND fulfillment_date > day
..the operation is fairly expensive.
Is there a better way of going about this?
I want to produce a table that shows the fulfillment status by day, starting with the order date and ending with the date prior to the fulfillment date of each line item
Consider below
select date, order_name, line_item_id, 'unfulfilled' fulfillment_status
from `project.dataset.table`,
unnest(generate_date_array(order_date, fulfillment_date - 1)) date
if applied to sample entry in your question - output is

Store day and date in same column in oracle

I have a situation where I need to store day and date(just a date) in single column in Oracle as below:
Column name
Monday
Tuesday
25
10
But I don't like combining string and number in single column. So I want any formula or design where each of these values gets unique identifier and store like below:
Column name
1 - - - - - refers to Monday
2............refers Tuesday
3-----------refers to a actual date 1
Above is an imaginary values just for understanding.
What I need is if have to get all entries with value Monday I should apply some formula and get a number which I should use in select query
Same way if I want to select entries with date =25 then I have to apply same formula and get unique number that represents date 25 and use it in my query
Store only the DATE in a column, the day of week is derived from that.
so:
CREATE TABLE junk
(
id number,
my_date date
);
insert into junk values ( 123, sysdate );
insert into junk values ( 234, add_months(sysdate,-123) );
commit;
select my_date "The actual date",
to_char(my_date,'Day') "The day of the week",
to_char(my_date,'Dy') "other way",
to_char(my_date,'D') "another way"
from junk;
The actual date The day o oth a
-------------------- --------- --- -
27-mar-2019 15:28:15 Wednesday Wed 4
27-dec-2008 15:28:15 Saturday Sat 7
2 rows selected.
Trying to do anything more than that, you will likely only hurt yourself in the long run (consider how you will maintain things if something is updated .. consider what happens if/when somebody "accidentally" updates 1 of the values, without updating the matching one? ... because they didn't understand it ... )
-- only pull records on Wednesday
select *
from junk
where to_char(my_date,'D') = 4
/
ID MY_DATE
---------- --------------------
123 27-mar-2019 15:28:15
1 row selected.
You can use the to_char() function in oracle:
SELECT TO_CHAR(date '2019-03-27', 'DAY') day FROM dual;
Which will return the day of the week.

Oracle: Date Range

I have the below Query & table:
Problem:
I'm trying to capture IDs that were Reassigned for that month.
For example the month of August total number of ReAssigned would be 1 and not 3
How can this be captured?
I need to count Per Month, for example. How mamy ids were reassigned for the month of August, Sept, ect.
So, the Most_Rent_Assigment date needs to fall within the month
Aug: 8/1-8/31
Sept: 9/1-9/30
Then count to capture how many ids were reassigned in that month
,CASE WHEN
H.FIRST_ASSGN_DT IS NULL THEN 'UnAssigned'
WHEN h.TOTAL_NUMBER_OF_REASSIGNMENTS = 0 OR h.TOTAL_NUMBER_OF_REASSIGNMENTS IS NULL
AND h.FIRST_ASSGN_DT IS NOT NULL THEN 'NewlyAssigned'
ELSE 'ReAssigned'
END ASSIGNED_STATUS
Table:
ID Total_Number_Reassignment Most_Recent_Assignment StartDate *Assigned Status
1 2 11/01/2016 08/1/2017 ReAssigned
2 3 08/02/2017 08/01/2017 ReAssigned
3 0 08/15/2017 NewlyAssigned
4 5 12/01/2016 09/01/2017 ReAssigned
Date ranges in Oracle can be accomplished by using BETWEEN.
If the columns are type DATE then you can just do: WHERE TRUNC(Most_Recent_Assignment) BETWEEN TO_DATE('2017-08-01') AND TO_DATE('2017-08-31')
Note: the TRUNC will truncate the date to midnight, so you don't lose results based on time of day.
Edit:
OP clarified his question.
To count occurrences for a particular month, you can group by month.
SELECT
TO_CHAR('Most_Recent_Assignment', 'mm'),
COUNT(TO_CHAR('Most_Recent_Assignment', 'mm'))
FROM TABLE
WHERE ...
GROUP BY TO_CHAR('Most_Recent_Assignment', 'mm')
TO_CHAR parses a date as a string. mm will return a 2-digit month. You can replace it with Month to get the name of the month.
The group will put all months together.

Oracle sql. finding last quarter's last date for given number of dates

I need to find the last quarter's last date and insert into another column for dates from present in a column. i.e read from the same table and insert into another column
EX
column 1 | column 2
02-aug-16|30-jun-16
05-dec-16|30-sep-16
Assuming you know how to insert a value in a column, and also assuming - if your date is not in the correct date datatype but instead is a string, then you know how to change it to a date with to_date() ...
the only remaining question is, given a date, how do you find the last date of the previous quarter.
trunc() can used with a date parameter. The function truncates the input date. You can give it a second argument to show what to truncate to. 'q' is for quarter. So trunc(date_col, 'q') will return the first day of the "current" quarter (current to the value stored in date_col, that is). Then you can subtract 1 (which means one day) to get the last day of the previous quarter.
SQL> select sysdate as today, trunc(sysdate, 'q') - 1 as last_day_of_prev_qtr from dual;
TODAY LAST_DAY_OF_PREV_QTR
---------- --------------------
2016-08-02 2016-06-30
If I got it right, truncate the source date to a quarter start and substract one day
select col1, TRUNC(col1,'Q') - interval '1' day col2
from (
select cast('02-aug-16' as date) col1 from dual
union all
select cast('05-dec-16' as date) col1 from dual
);

Generic query to fetch data based on date column in every month in Oracle

I have a task where I need to generate report once in every month let's say 1st of every month. I tried in many ways but every time I need to modify the query in order to get the data for tht particular month.
I used the following query to fetch the data of November:
Select * from user
where m_termination_dt>(sysdate-30) and m_termination_dt<(sysdate).
It is giving proper output.( I ran this query on 1st of Dec so subtracted 30days). Here the concern is, as November has 30 days it worked. For Dec I have to minus 31 days instead 30 which is again a human involvement. So is there any way I can write a query without changing the query.
Can anyone please help me with a generic query so that I can automate that query in my system.
Sai.
Add_months(trunc(sysdate, 'month'), -1) gives first day of previous month. Trunc(sysdate, 'month') gives first day of current month.
So if you run this query you get data from previous month, it does not matter how many days it had:
select * from users
where add_months(trunc(sysdate, 'month'), -1) <= m_termination_dt
and m_termination_dt < trunc(sysdate, 'month')
Example:
create table users (id number(5), m_termination_dt date);
insert into users values (1, date '2015-11-01');
insert into users values (2, date '2015-11-18');
insert into users values (3, date '2015-11-30');
insert into users values (4, date '2015-12-01');
commit;
Output of query:
ID M_TERMINATION_DT
------ ----------------
1 2015-11-01
2 2015-11-18
3 2015-11-30