i have a query
select count(1) z1
from sales_data
where ed_date >= sysdate -7
i need to group by ed
_date, remove the timestamp, and condition where ed_date is from 1/1/2021 to current sysdate
You can truncate the date:
select trunc(ed_date, 'DD') as ed_date, count(*) as z1
from sales_data
where ed_date >= date '2021-01-01'
and ed_date < trunc(sysdate, 'DD')
group by trunc(ed_date, 'DD')
The 'DD' truncation limit is the default so you could omit that, as just trunc(ed_date), if you prefer. Note though that it doesn't remove the time, it just sets it to midnight - an Oracle date always has both date and time components.
You can also order by trunc(ed_date), and in the select list you can format that however you want:
select to_char(trunc(ed_date), MM/DD/YYYY') as ed_date, count(*) as z1
from sales_data
where ed_date >= date '2021-01-01'
and ed_date < trunc(sysdate) -- to exclude today and future
-- and ed_date < trunc(sysdate) + 1 -- to include today but not tomorrow onwards
group by trunc(ed_date)
order by trunc(ed_date)
Related
I have the following code to pull records from a daterange in PostgreSQL, it works as intended. The "end date" is determined by the "date" column from the last record, and the "start date" is calculated by subtracting a 7-day interval from the "end date".
SELECT date
FROM files
WHERE daterange((
(SELECT date FROM files ORDER BY date DESC LIMIT 1) - interval '7 day')::date, -- "start date"
(SELECT date FROM files ORDER BY date DESC LIMIT 1)::date, -- "end date"
'(]') #> date::date
ORDER BY date ASC
I'm trying to rewrite this query using CTEs, so I can replace those subqueries with values such as end_date and start_date. Is this possible using this method or should I look for other alternatives like variables? I'm still learning SQL.
WITH end_date AS
(
SELECT date FROM files ORDER BY date DESC LIMIT 1
),
start_date AS
(
SELECT date FROM end_date - INTERVAL '7 day'
)
SELECT date
FROM files
WHERE daterange(
start_date::date,
end_date::date,
'(]') #> date::date
ORDER BY date ASC
Right now I'm getting the following error:
ERROR: syntax error at or near "-"
LINE 7: SELECT date FROM end_date - INTERVAL '7 day'
You do not need two CTEs, it's one just fine, which can be joined to filter data.
WITH RECURSIVE files AS (
SELECT CURRENT_DATE date, 1 some_value
UNION ALL
SELECT (date + interval '1 day')::date, some_value + 1 FROM files
WHERE date < (CURRENT_DATE + interval '1 month')::date
),
dates AS (
SELECT
(MAX(date) - interval '7 day')::date from_date,
MAX(date) to_date
FROM files
)
SELECT f.* FROM files f
JOIN dates d ON daterange(d.from_date, d.to_date, '(]') #> f.date
You even can make it to be a daterange initially in CTE and use it later like this
WITH dates AS (
SELECT
daterange((MAX(date) - interval '7 day')::date, MAX(date), '(]') range
FROM files
)
SELECT f.* FROM files f
JOIN dates d ON d.range #> f.date
Here the first CTE is used just to generate some data.
It will get all file lines for dates in the last week, excluding from_date and including to_date.
date
some_value
2022-09-26
25
2022-09-27
26
2022-09-28
27
2022-09-29
28
2022-09-30
29
2022-10-01
30
2022-10-02
31
I think this is what you want:
WITH end_date AS
(
SELECT date FROM files ORDER BY date DESC LIMIT 1
),
start_date AS
(
SELECT date - INTERVAL '7 day' as date
FROM end_date
)
SELECT F.date, S.date startDate, E.date endDate
FROM files F
JOIN start_date S on F.date >= S.date
JOIN end_date E on F.date <= E.date
ORDER BY date ASC;
I hope I'm not repeating anything, but if I understand your problem correctly I think this will work:
with cte as (
select max (date)::date as max_date from files
)
select date
from files
cross join cte
where date >= max_date - 7
Or perhaps even:
select date
from files
where date >= (select max (date)::date - 7 from files)
Since you have already determined that the CTE has the max date, there is really no need to further bound it with a between, <= or range. You can simply say anything after that date minus 7 days.
The error in your code above is because you want this:
SELECT date - INTERVAL '7 day' as date FROM end_date
And not this:
SELECT date FROM end_date - INTERVAL '7 day'
You are subtracting from the table, which doesn't make sense.
Let's say I want to get the profit between two dates. Then I can do something like this:
SELECT SUM(Profit)
FROM Sales
WHERE date BETWEEN '2014-01-01' AND '2014-02-01' AND <other_filters>
I would then like to compare it to a previous period offset by a fixed amount. It could be written something like this to get it in two rows:
SELECT SUM(Profit)
FROM Sales
WHERE date BETWEEN '2014-01-01' AND '2014-02-01' AND <other_filters>
UNION ALL
SELECT SUM(Profit)
FROM Sales
WHERE date BETWEEN '2014-01-01' - INTERVAL 1 YEAR AND '2014-02-01' - INTERVAL 1 YEAR AND <other_filters>
Is there a way to do this without a union? I am looking for something like this:
SELECT
SELECT SUM(Profit),
???
FROM Sales
WHERE date BETWEEN '2014-01-01' AND '2014-02-01' AND <other_filters>
I think the tricky part here is how to 'un-do' the where filter for the offseted-time calculation.
You can use conditional aggregation and OR the range checks in the WHERE clause (unless they are subsequent in which case you can combine them directly of course).
SELECT sum(CASE
WHEN date >= '2014-01-01'
AND date < '2014-02-02' THEN
profit
ELSE
0
END),
sum(CASE
WHEN date >= '2014-01-01' - INTERVAL 1 YEAR
AND date < '2014-02-02' - INTERVAL 1 YEAR THEN
profit
ELSE
0
END)
FROM sales
WHERE date >= '2014-01-01'
AND date < '2014-02-02'
OR date >= '2014-01-01' - INTERVAL 1 YEAR
AND date < '2014-02-02' - INTERVAL 1 YEAR;
Note: Prefer not to use BETWEEN here but check for a right half open range check. That way, if the precision of date changes, records on the end past midnight are still in the results.
I have a requirement to display dates every week starting from current date for 48 months.
I was wondering if it is possible through SQL or i will have to write a function to achieve it.
Below is my SQL so far :-
SELECT
CALENDAR_DATE
FROM
CALENDAR --My Table Name
WHERE
(
CALENDAR_DATE >= trunc(sysdate)
AND
CALENDAR_DATE <= ADD_MONTHS(TRUNC(SYSDATE, 'MONTH'), 48)-1
)
This would give me output as
9/10/2020
9/11/2020
9/12/2020
9/13/2020
Expected Output :-
9/10/2020
9/17/2020
9/24/2020
10/01/2020
If I understand correctly:
WHERE CALENDAR_DATE >= trunc(sysdate) AND
CALENDAR_DATE <= ADD_MONTHS(TRUNC(SYSDATE, 'MONTH'), 48) - 1 AND
MOD(CALENDAR_DATE - TRUNC(sysdate), 7) = 0
This uses the mod() operation on the date difference. You can also check the day of the week:
WHERE CALENDAR_DATE >= trunc(sysdate) AND
CALENDAR_DATE <= ADD_MONTHS(TRUNC(SYSDATE, 'MONTH'), 48) - 1 AND
TO_CHAR(CALENDAR_DATE, 'DY') = TO_CHAR(sysdate, 'DY')
For the query below, I'm trying to pull a specific date range depending on the current day of the month. If it's the 20th or less (e.g. "2/7/2020") then I want the date range for January. Otherwise, I want the date range for February. Is it possible to be done with a case statement? Or there is a better way?
SELECT
account,
start_date,
amount
FROM
table1
WHERE
CASE
WHEN (
SELECT
CAST(EXTRACT(DAY FROM sysdate) AS NUMBER)
FROM
dual
) <= 20 THEN
start_date
BETWEEN '2020-01-01' AND '2020-01-31'
ELSE start_date BETWEEN '2020-02-01' AND '2020-02-29'
END
You can do this by avoiding the case statement and using truncate the date - 20 to the month, e.g.:
SELECT account,
start_date,
amount
FROM table1
WHERE start_date >= TRUNC(SYSDATE - 20, 'mm')
AND start_date < add_months(TRUNC(dt - 20, 'mm'), 1);
If you really had to use a CASE expression (you can't use a CASE statement in SQL), you would need to do something like:
SELECT account,
start_date,
amount
FROM table1
WHERE start_date >= CASE WHEN to_char(SYSDATE, 'dd') <= '20' THEN add_months(TRUNC(SYSDATE, 'mm'), -1) ELSE TRUNC(SYSDATE, 'mm') END
AND start_date < CASE WHEN to_char(SYSDATE, 'dd') <= '20' THEN TRUNC(SYSDATE, 'mm') ELSE add_months(TRUNC(SYSDATE, 'mm'), 1) END;
N.B. if you're using a function, you don't need to wrap it in a select .. from dual, you can use it directly in the SQL statement.
I've also assumed that you want a dynamic range, e.g. if the day of the month is 20 or less, the range is for the previous month, otherwise the current month.
ETA: You would use the above two queries if there is an index on the start_date column, otherwise you could simply do:
SELECT account,
start_date,
amount
FROM table1
WHERE TRUNC(start_date, 'mm') = TRUNC(SYSDATE - 20, 'mm');
Case statements return single values. As such you should pull out the start date and you'll need two case statements.
select account, start_date, amount
from table1 where
start_date between
(case
when (select cast(extract(day from sysdate) as number) from dual) <= 20 then '2020-01-01'
else '2020-02-01'
end) and
(case
when (select cast(extract(day from sysdate) as number) from dual) <= 20 then '2020-01-31'
else '2020-02-29'
end)
One method subtracts 20 days and then gets the month boundary:
where start_date >= trunc(sysdate - interval '20' day, 'MON') and
start_date < trunc(sysdate - interval '20' day, 'MON') + interval '1' month
This approach is index (and partition) friendly -- an appropriate index on start_date can be used. It is also safe if start_date has time components.
Note: You can use sysdate without having to use a subquery.
You can use or operator with last_day function as following:
Select * from your_table
Where (
start_date <= trunc(sysdate,'mm') + 20
and start_date between trunc(sysdate,'mm') - interval '1' month and trunc(sysdate,'mm') - 1
)
Or
(
start_date > trunc(sysdate,'mm') + 20
and start_date between trunc(sysdate, 'mm') and last_day(sysdate)
)
This approach will use index on start_date, if any.
Cheers!!
select account, amount, start_date
from table1
where ( ( (select cast (extract (day from sysdate) as number) from dual) <= 20
and start_date between date '2020-01-01' and date '2020-01-31')
or ( (select cast (extract (day from sysdate) as number) from dual) > 20
and start_date between date '2020-02-01' and date '2020-02-29')
);
Using CASE expressions as BETWEEN operands:
SELECT account
, start_date
, amount
FROM table1
WHERE start_date BETWEEN CASE
WHEN extract(day from sysdate) <= 20
THEN trunc(sysdate -interval '1' month, 'month')
ELSE trunc(sysdate, 'month')
END
AND CASE
WHEN extract(day from sysdate) <= 20
THEN last_day(sysdate -interval '1' month)
ELSE last_day(sysdate)
END
Recently I've been developing a leave management system. In this application I need a report like in a month wise employee leave statement.
So here's my sample table:
Employee Id application Date Start Date End Date
20130002 14-Mar-2016 16-Mar-2016 17-Mar-2016
20130012 15-Mar-2016 29-Mar-2016 2-Apr-2016
20130003 14-Mar-2016 15-Mar-2016 16-Mar-2016
20130005 10-Mar-2016 24-Mar-2016 24-Mar-2016
20130002 10-Mar-2016 20-Mar-2016 25-Mar-2016
20130006 13-Mar-2016 8-Mar-2016 17-Mar-2016
20130001 14-Mar-2016 4-Apr-2016 24-Apr-2016
20130003 15-Mar-2016 16-May-2016 18-May-2016
20130011 10-Mar-2016 7-Jun-2016 7-Jun-2016
Now I need a report where I can get month wise this report. Suppose I need only March's data, like this:
<pre>
Employee Id application Date Start Date End Date
20130002 14-Mar-2016 16-Mar-2016 17-Mar-2016
20130012 15-Mar-2016 29-Mar-2016 31-Mar-2016
20130003 14-Mar-2016 15-Mar-2016 16-Mar-2016
20130005 10-Mar-2016 24-Mar-2016 24-Mar-2016
20130002 10-Mar-2016 20-Mar-2016 25-Mar-2016
20130006 13-Mar-2016 8-Mar-2016 17-Mar-2016
</pre>
How can I achieve this - by PL/SQL or any SQL language?
Assuming that the time component of the dates is set to 00:00:00 then:
SELECT EmployeeId,
application_date,
GREATEST( start_date, DATE '2016-03-01' ) AS start_date,
LEAST( end_date, DATE '2016-03-31' ) AS end_date
FROM table_name
WHERE Start_date <= DATE '2016-03-31'
AND end_date >= DATE '2016-03-01'
You can use a bind variable to replace the hard-coded dates like this:
SELECT EmployeeId,
application_date,
GREATEST( start_date, :month_start ) AS start_date,
LEAST( end_date, LAST_DAY( :month_start ) ) AS end_date
FROM table_name
WHERE Start_date <= LAST_DAY( :month_start )
AND end_date >= :month_start
If you have time components then:
SELECT EmployeeId,
application_date,
GREATEST( start_date, :month_start ) AS start_date,
LEAST( end_date, :month_start + INTERVAL '1' MONTH - INTERVAL '1' SECOND )
AS end_date
FROM table_name
WHERE Start_date < :month_start + INTERVAL '1' MONTH
AND end_date >= :month_start
This is for SQL Server
SELECT *
FROM Leaves
WHERE MONTH(StartDate) <= 4 and Month(EndDate) >= 4
For Oracle
SELECT *
FROM Leaves
WHERE EXTRACT(month FROM StartDate) <= 4 and EXTRACT(month FROM EndDate) >= 4
The condition should be
WHERE StartDate>='01-Mar-2016' and EndDate <'01-Apr-2016'
You could use a parameter for the required month in format 'YYYYMM' (for March 2016: '201603')
then the where-clause would be:
where parameter = to_char(start_date,'yyyymm')
or parameter = to_char(end_date,'yyyymm')
if a leave can be longer than a month then you also need to check if the parameter is between start and end date.
..
or parameter between to_char(start_date,'yyyymm') and to_char(end_date,'yyyymm')
to display the correct date in the report you can use a CASE statement combined with the first_day and last_day function to display the first and the last day of the month.
(here the parameter_date is the required month as a DATE, not a VARCHAR2)
CASE
WHEN start_date < first_day(parameter_date) THEN first_day(parameter_date)
ELSE start_date
END as start_Date,
CASE
WHEN end_date > last_day(parameter_date) THEN last_day(parameter_date)
ELSE end_date
END as end_Date