Using variables in Oracle SQL - sql

I'm not much experienced with Oracle SQL and been trying to accomplish some task for which I intend to use
select count(*) from TABLE where date = (select trunc(sysdate -1 ) from dual)
The above query gives me the result for all the records with sysdate minus 1. I want to automate it to handle the scenario of Sat and Sunday and modify the query to something like below
select count(*) from TABLE where date = (select trunc(sysdate -#var ) from dual)
where #var = 2 if the query runs on Sunday and #var = 3 if the query is running on Monday

TRUNC( SYSDATE, 'IW' ) will truncate the date to the start of the ISO week - so midnight Monday. TRUNC( SYSDATE ) - TRUNC( SYSDATE, 'IW' ) will give the number of full days since the start of the ISO week (Monday = 0, Tuesday = 1, ... Sunday = 6) and you can just put that into a CASE statement:
select count(*)
from your_table
where date_col = TRUNC(SYSDATE) - CASE TRUNC(SYSDATE) - TRUNC(SYSDATE, 'IW')
WHEN 0 THEN 3 -- Monday
WHEN 6 THEN 2 -- Sunday
ELSE 1 -- Other days
END

Related

Obtain data from Friday, Saturday and Sunday if today is Monday SQL Oracle

I am looking to obtain all data in a table from yesterday in SQL Oracle.
This is simply enough using the WHERE clause, i.e,
SELECT *
FROM My_Data
WHERE TO_DATE(My_Data.Date,'YYYY-MM-DD') = TRUNC(SYSDATE)-1
However if I now need to add more logic where if the day of the query is a Monday (SYSDATE) then obtain data between Friday and Sunday.
Using a between statement is no issue, I'm just not sure if I can include in a where statement given I'm unable to use case statement here.
Thanks
SELECT
*
FROM
My_Data
WHERE
TO_DATE(My_Data.Date,'YYYY-MM-DD')
Between Case When To_Char(SYSDATE, 'DY') = 'MON' Then TRUNC(SYSDATE)-3 ELSE TRUNC(SYSDATE)-1 END
And TRUNC(SYSDATE)-1
You can use the Case expression in Where clause. Regards...
Don't use TO_DATE on a column that is already a date (and if it is a string then don't store dates as strings).
So you are not dependent on the date language session parameter, you can compare the date to the start of the ISO week (which is independent of language) and you can compare on a date range so that Oracle can use an index on your date column:
SELECT *
FROM My_Data
WHERE "DATE" < TRUNC(SYSDATE)
AND "DATE" >= CASE TRUNC(SYSDATE) - TRUNC(SYSDATE, 'IW')
WHEN 0 -- Monday
THEN TRUNC(SYSDATE) - 3
ELSE TRUNC(SYSDATE) - 1
END;
or:
SELECT *
FROM My_Data
WHERE "DATE" < TRUNC(SYSDATE)
AND ( ( TRUNC(SYSDATE) - TRUNC(SYSDATE, 'IW') = 0 AND "DATE" >= TRUNC(SYSDATE) - 3 )
OR "DATE" >= TRUNC(SYSDATE) - 1
);

Get last 5 day record excluding Weekend days?

I'm trying to extract records from table inserted in last 5 working days on Oracle SQL
I'm unable to exclude Sunday and Saturday from this(as they're coming into consideration)
Select * from xyz where xdate=:businessDate - 5
Here businessDate (yyyymmdd format) is value taken as a parameter
Any pointers would greatly  be valued and appreciated .
SELECT *
FROM xyz
WHERE
xdate >= TRUNC(SYSDATE) - 7 --last 7 days AND
TRUNC (xdate) - TRUNC (xdate, 'IW') NOT IN (5,6) --not sat or sun
Take the last 7 days data, and remove any data from saturday or sunday.
TRUNC (xdate) - TRUNC (xdate, 'IW') returns a value between 0 and 6 inclusive. 0 is monday. This is not affected by NLS date settings because it uses the same region setting in both truncs, so regardless if the week starts on sunday or monday in your country, the result of the calc is the same
In any given 7 day period you have 5 week days and 2 weekend days
Note that this query assumes the table has no future dated data. If it does put a restriction that xdate must be less than sysdate
I am not an oracle expert but this could also work
SELECT *
FROM xyz
WHERE xdate IN (:businessDate - 1, :businessDate - 2, :businessDate - 3, :businessDate - 4, :businessDate - 5, :businessDate - 6, :businessDate - 7) AND MOD(TO_CHAR(xdate, 'J'), 7) + 1 NOT IN (6, 7);
I would do:
select * from xyz
where xdate >= TO_DATE( :businessDate, 'yyyymmdd' ) - 7
and to_char( xdate, 'DAY' ) not in ( 'SAT', 'SUN' )
That's assuming you're working in an English database. That should give you the past 7 days worth, but exclude weekends.
You'll need to do something like the following:
WITH cteData AS (SELECT TO_DATE(:BUSINESSDATE, 'YYYYMMDD') AS BUSINESS_DATE FROM DUAL)
SELECT *
FROM XYZ x
CROSS JOIN cteData d
WHERE TRUNC(x.XDATE) BETWEEN d.BUSINESS_DATE - 7
AND d.BUSINESS_DATE AND
TRUNC(x.XDATE) - TRUNC(x.XDATE, 'IW') <= 4
Find out the weekend day i.e. Sunday or Saturday by using 'DAY' format in format_date function e.g.
IF FORMAT_DATE(Date,'DAY')='SATURDAY' then date-1
IF FORMAT_DATE(Date,'DAY')='SUNDAY' then date-2
You would get only 5 days record

Oracle group data by Thursday to Wednesday

My query groups data from Monday to Sunday. I would like to be able to group data from Thursday to Wednesday. How can I alter the two lines starting with TO_CHAR to do it?
SELECT SOMESTUFF
,WEEKNUM
,WEEKENDING
FROM (
SELECT SOMEDETAILS
,TRXDATE
,TO_CHAR(TRXDATE, 'IW') AS WEEKNUM
,TO_CHAR(TRUNC(TRXDATE, 'IW') + 7 - 1 / 86400, 'MM/DD/YYYY') AS WEEKENDING
FROM SOMETABLE
) DETAILS
INNER JOIN SOMEOTHERTABLES
GROUP BY SOMEFIELDS
,WEEKNUM
,WEEKENDING
You can use NEXT_DAY(TRXDATE - 1, 'WED') to find the end of your week:
TO_CHAR(NEXT_DAY(TRXDATE - 1, 'WED'), 'MM/DD/YYYY')
TO_CHAR(NEXT_DAY(TRXDATE - 1, 'WED'), 'IW')
What week number do you expect?
Easiest solution is to simply add one to the weeknum when the date is Wed-Sunday (or subtract if it's Monday or Tuesday). Something like this would work
TO_CHAR(TRXDATE, 'IW') + CASE
WHEN TO_CHAR(TRXDATE, 'D') > 2 THEN 1
ELSE 0
END AS WEEKNUM

Sort by day of the week from Monday to Sunday

If I write
select ename, to_char(hiredate,'fmDay') as "Day" order by "Day";
Then it sorts the result based on Day like; from Friday, then Monday and last Wednesday,
like sorting by characters.
But I want to sort it by day of the week; from Monday to Sunday.
You're getting it in the order you are because you're ordering by a string (and this wouldn't work because you're not selecting from anything).
You could order by the format model used to create the day of the week in numeric form, D, but as Sunday is 1 in this I would recommend using mod() to make this work.
i.e. assuming the table
create table a ( b date );
insert into a
select sysdate - level
from dual
connect by level <= 7;
This would work:
select mod(to_char(b, 'D') + 5, 7) as dd, to_char(b, 'DAY')
from a
order by mod(to_char(b, 'D') + 5, 7)
Here's a SQL Fiddle to demonstrate.
In your case your query would become:
select ename, to_char(hiredate,'fmDay') as "Day"
from my_table
order by mod(to_char(hiredate, 'D') + 5, 7)
SELECT
*
FROM
classes
ORDER BY
CASE
WHEN Day = 'Sunday' THEN 1
WHEN Day = 'Monday' THEN 2
WHEN Day = 'Tuesday' THEN 3
WHEN Day = 'Wednesday' THEN 4
WHEN Day = 'Thursday' THEN 5
WHEN Day = 'Friday' THEN 6
WHEN Day = 'Saturday' THEN 7
END ASC
Assuming that user has a table called classes in that table user has class_id (primary key), class name, Day.
Take a look at other formats for TO_CHAR. Instead of 'fmDay' use 'D' and it will give you the day of the week from 1 to 7. Then you can easily sort on it.
Here's a list of date formats: http://docs.oracle.com/cd/B19306_01/server.102/b14200/sql_elements004.htm
Why to complicate when you can add another column with numbers 1-7 corresponding to days and then sort by this column...
I just encountered the same requirement -- to order a query result by day of the week, but not starting with Sunday. I used the following query in Oracle to start w/ Monday. (Modify it to start the ordering w/ any day of the week, e.g., change 'MONDAY' to 'TUESDAY'.)
SELECT ename, to_char(hiredate, 'fmDAY') AS "Day"
FROM emp
ORDER BY (next_day(hiredate, 'MONDAY') - hiredate) DESC
Or:
SELECT ename, to_char(hiredate, 'fmDAY') AS "Day"
FROM emp
ORDER BY (hiredate - next_day(hiredate, 'MONDAY'))
The D format mask of to_char maps days of the week to the values 1-7.
But!
The output of this depends on the client's setting for NLS_TERRITORY. The US considers Sunday to be day 1. Whereas most of the rest of the world consider Monday to be the start:
alter session set nls_territory = AMERICA;
with dts as (
select date'2018-01-01' + level - 1 dt
from dual
connect by level <= 7
)
select to_char ( dt, 'Day' ) day_name,
to_char ( dt, 'd' ) day_number
from dts
order by day_number;
DAY_NAME DAY_NUMBER
Sunday 1
Monday 2
Tuesday 3
Wednesday 4
Thursday 5
Friday 6
Saturday 7
alter session set nls_territory = "UNITED KINGDOM";
with dts as (
select date'2018-01-01' + level - 1 dt
from dual
connect by level <= 7
)
select to_char ( dt, 'Day' ) day_name,
to_char ( dt, 'd' ) day_number
from dts
order by day_number;
DAY_NAME DAY_NUMBER
Monday 1
Tuesday 2
Wednesday 3
Thursday 4
Friday 5
Saturday 6
Sunday 7
Sadly, unlike many other NLS parameters, you can't pass NLS_TERRITORY as the third parameter of to_char:
with dts as (
select date'2018-01-01' dt
from dual
)
select to_char ( dt, 'Day', 'NLS_DATE_LANGUAGE = SPANISH' ) day_name
from dts;
DAY_NAME
Lunes
with dts as (
select date'2018-01-01' dt
from dual
)
select to_char ( dt, 'Day', 'NLS_TERRITORY = AMERICA' ) day_name
from dts;
ORA-12702: invalid NLS parameter string used in SQL function
So any solution relying on D for sorting is a bug!
To avoid this, subtract the most recent Monday from the date (if today is Monday, the most recent Monday = today). You can do this with the IW format mask. Which returns the start of the ISO week. Which is always a Monday:
with dts as (
select date'2018-01-01' + level - 1 dt
from dual
connect by level <= 7
)
select to_char ( dt, 'Day' ) day_name,
( dt - trunc ( dt, 'iw' ) ) day_number
from dts
order by day_number;
DAY_NAME DAY_NUMBER
Monday 0
Tuesday 1
Wednesday 2
Thursday 3
Friday 4
Saturday 5
Sunday 6
For Sunday-Saturday sorting, add one to the date before finding the start of the ISO week:
with dts as (
select date'2018-01-01' + level - 1 dt
from dual
connect by level <= 7
)
select to_char ( dt, 'Day' ) day_name,
( dt - trunc ( dt + 1, 'iw' ) ) day_number
from dts
order by day_number;
DAY_NAME DAY_NUMBER
Sunday -1
Monday 0
Tuesday 1
Wednesday 2
Thursday 3
Friday 4
Saturday 5
If you want Monday to be always treated as first day of week you could use:
-- Not affected by NLS_TERRITORY
-- ALTER SESSION SET NLS_TERRITORY="AMERICA"; -- Sunday is first day of week
-- ALTER SESSION SET NLS_TERRITORY="GERMANY"; -- Monday is first day of week
SELECT *
FROM tab
ORDER BY 1+TRUNC(dt)-TRUNC(dt,'IW');
db<>fiddle demo
It's simple.
SELECT last_name, hire_date,TO_CHAR(hire_date, 'DAY') DAY
FROM employees
ORDER BY TO_CHAR(hire_date - 1, 'd');
TO_CHAR(hire_date - 1, 'd') puts a 'Monday' into a box named 'Sunday'.
As it's said, there's a function for it:
SELECT *
FROM table
ORDER BY WEEKDAY(table.date);
Might be late to post the answer, But seems simple solution to sort by day of week starting from Monday.
select hiredate,dayname(hiredate) from emp order by (dayofweek(hiredate)+5)%7;
Answer for why added 5 ?
Because , In numbers Sunday has value 1, Monday has 2 ... and Saturday has 7
So, 2 + 5 = 7 and mod(%) with 7 will give us new values start from Monday.
You can change this number to start with any other day of week.
2nd option
simply use WEEKDAY() function return weekday number for a given date.
**Note:**
0 = monday
1 = tuesday
2 = wednesday
3 = thursday
4 = friday
5 = saturday
6 = sunday
select hiredate,dayname(hiredate) from emp order by WEEKDAY(hiredate);
I improved on Ben's answer by giving you a result which starts from 1 rather than 0. The query would be like this:
select
mod(to_char(b, 'D')+ 5, 7) +1 as dd,
to_char(b, 'DAY')
from a
order by mod(to_char(b, 'D')+ 5, 7);
On the other hand, if you want your week to start from Sunday, then you should use this query:
select
mod(to_char(b, 'D')+ 6, 7) +1 as dd,
to_char(b, 'DAY')
from a
order by mod(to_char(b, 'D')+ 6, 7)
Hope this helps :)
with s as (select trunc(sysdate) + level dt from dual connect by level <= 7)
select to_char(dt, 'fmDay', 'nls_date_language=English') d
from s
order by dt - trunc(dt, 'iw');
D
------------------------------------
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
Sunday
7 rows selected.
I have some simple idea hope you like it. i don't which sql you are using so please correct syntax error.
select ename, to_char(hiredate,'fmDay') as "Day" from ABC_TABLE
JOIN (VALUES (1,'Monday'),(2,'Tuesday'),(3,'Wednesday'),(4,'Thursday'),(5,'Friday'),(6,'Saturday'),(7,'Sunday')) weekdays(seq,[Days]) on
ABC_TABLE.to_char(hiredate,'fmDay') = weekdays.[Days]
order by weekdays.seq;
I you want to start next week after end a week then just find quarter of month and add in order by clouse.
just for in of find Quarter in(MSSQL): select DatePart(QUARTER, cast(cast(mydate as char(8)) as date))
This should do the trick in sql:
ORDER BY
CASE DATENAME(dw,<<enter your date variable here>>)
WHEN 'Monday' THEN 1
WHEN 'Tuesday' THEN 2
WHEN 'Wednesday' THEN 3
WHEN 'Thursday' THEN 4
WHEN 'Friday' THEN 5
WHEN 'Saturday' THEN 6
WHEN 'Sunday' Then 7
END ASC;
Of course if you want different order you just switch the values after then.
For Postgres at least, to have Monday always be day 1, use TO_CHAR(b, 'ID') to return the ISO 8601 day of the week - Monday (1) to Sunday (7).
This is the International Standard's Organization (ISO) Day of the Week hence the I in front of the D in 'ID'.
https://www.postgresqltutorial.com/postgresql-to_char/
I think this is too late, but I will leave this here if anyone else needs it.
You can just order by day of the week, but it depends on the territory of your session.
In order to show the territory, you can simply execute this query:
SELECT * FROM V$NLS_PARAMETERS WHERE parameter = 'NLS_TERRITORY';
NLS_TERRITORY specifies the name of the territory whose conventions are to be followed for day and week numbering.
Back to the previous query, if you have AMERICA as the value, then Monday has the value 2.
To order your results from Monday to Sunday, you have at least 2 roads:
1- Decrease one day from hire_date:
SELECT * FROM employees ORDER BY TO_CHAR(hire_date - 1, 'd');
2- Alter the territory to a value that has 1 as the value of Monday, for example, FRANCE:
ALTER SESSION SET NLS_TERRITORY = 'FRANCE';
SELECT * FROM employees ORDER BY TO_CHAR(hire_date, 'd');
This is my solution.
select ename, to_char(hiredate,'fmDay') "Day"
from employees
order by DECODE(to_number(to_char(hiredate ,'D'))-1,
0, 7,
to_number(to_char(hiredate ,'D'))-1);
Basically converts day to a number by using 'D' format from to_char function.
to_number(to_char(hire_date ,'D')
But that causes Sunday to be 1 and Monday to be 2 and so on so I subtract 1 to make Monday 1.
(to_number(to_char(hire_date ,'D') - 1
Then I use DECODE function to basically say if 0, then 7, else original number. That just makes Sunday be 7 instead of 0 and everything else is in its proper place(Monday is 1, Tuesday is 2...so on).
DECODE(to_number(to_char(hire_date ,'D'))-1,
0, 7,
to_number(to_char(hire_date ,'D'))-1);

Number of fridays between two dates

How do I find the number of fridays between two dates(including both the dates) using a select statement in oracle sql?
This will do it:
select ((next_day(date2-7,'FRI')-next_day(date-1,'FRI'))/7)+1 as num_fridays
from data
Perhaps best if I break that down. The NEXT_DAY function returns the next day that is a (Friday in this case) after the date.
So to find the first Friday after d1 would be:
next_day( d1, 'FRI')
But if d1 is a Friday that would return the following Friday, so we adjust:
next_day( d1-1, 'FRI')
Similarly to find the last Friday up to and including d2 we do:
next_day( d1-7, 'FRI')
Subtracting the 2 gives a number of days: 0 if they are the same date, 7 if they a re a week apart and so on:
next_day( d1-7, 'FRI') - next_day( d1-1, 'FRI')
Convert to weeks:
(next_day( d1-7, 'FRI') - next_day( d1-1, 'FRI')) / 7
Finally, if they are the same date we get 0, but really there is 1 Friday, and so on so we add one:
((next_day( d1-7, 'FRI') - next_day( d1-1, 'FRI')) / 7) + 1
I have to throw in my two cents for using a calendar table. (It's a compulsion.)
select count(*) as num_fridays
from calendar
where day_of_week = 'Fri'
and cal_date between '2011-01-01' and '2011-02-17';
num_fridays
-----------
6
Dead simple to understand. Takes advantage of indexes.
Maybe I should start a 12-step group. Calendar Table Anonymous.
See:
Why should I consider using an auxiliary calendar table?
The article's code is specifically for SQL Server but the techniques are portable to most SQL platforms.
With a Calendar table in place your query could be as simple as
SELECT COUNT(*) AS friday_tally
FROM YourTable AS T1
INNER JOIN Calendar AS C1
ON C1.dt BETWEEN T1.start_date AND T1.end_date
WHERE C1.day_name = 'Friday'; -- could be a numeric code
select sum(case when trim(to_char(to_date('2009-01-01','YYYY-MM-DD')+rownum,'Day')) = 'Friday' then 1 else 0 end) number_of_fridays
from dual
connect by level <= to_date('&end_date','YYYY-MM-DD') - to_date('&start_date','YYYY-MM-DD')+1;
Original source - http://forums.oracle.com/forums/thread.jspa?messageID=3987357&tstart=0
Try modifying this one:
CREATE OR REPLACE FUNCTION F_WORKINGS_DAYS
(V_START_DATE IN DATE, V_END_DATE IN DATE)
RETURN NUMBER IS
DAY_COUNT NUMBER := 0;
CURR_DATE DATE;
BEGIN -- loop through and update
CURR_DATE := V_START_DATE;
WHILE CURR_DATE <= V_END_DATE
LOOP
IF TO_CHAR(CURR_DATE,'DY') NOT IN ('SAT','SUN') -- Change this bit to ignore all but Fridays
THEN DAY_COUNT := DAY_COUNT + 1;
END IF;
CURR_DATE := CURR_DATE + 1;
END LOOP;
RETURN DAY_COUNT;
END F_WORKINGS_DAYS;
/
SELECT (NEXT_DAY('31-MAY-2012','SUN')
-NEXT_DAY('04-MAR-2012','SUN'))/7 FROM DUAL
select ((DATEDIFF(dd,#a,#b)) + DATEPART(dw,(#a-6)))/7