select count of records between 4 and 12 months - sql

I Have a query to count the records which are aged between 4 and 12 months in my table
SELECT count(*)
from tm_process_state
where tstamp BETWEEN ADD_MONTHS(trunc(SYSDATE,'MONTH'), -12)
AND LAST_DAY(TRUNC(SYSDATE, 'MONTH') - 4);
After running am getting count value as 1240, which is wrong. As it is counting records from 01/03/13 to 27/02/2014.
What i am trying to count is records between sysdate-90 and sysdate-365.
Then I tried below condition
tstamp < sysdate-90 and tstamp>sysdate-365
Now the count is 598, which is correct.
But here, I cannot give here like 365(As it varies based on leap and non-leap year)
can any one please tell me how to alter the first query to display correct count?

This condition:
where tstamp BETWEEN ADD_MONTHS(trunc(SYSDATE,'MONTH'), -12) AND LAST_DAY(TRUNC(SYSDATE, 'MONTH') - 4)
is calculating from the beginning for the months (due to the TRUNC(SYSDATE, 'MONTH')) to the end of a month Why not just do:
where tstamp BETWEEN ADD_MONTHS(SYSDATE, -12) AND ADD_MONTHS(SYSDATE, -3)
If you are concerned about time values on the dates, just trunc() the values:
where tstamp BETWEEN trunc(ADD_MONTHS(SYSDATE, -12)) AND trunc(ADD_MONTHS(SYSDATE, -3))

I think that you need this:
select
trunc(ADD_MONTHS(SYSDATE, -12),'MONTH') ,
LAST_DAY(ADD_MONTHS(SYSDATE, -4))
from dual;
instead of:
select
ADD_MONTHS(trunc(SYSDATE,'MONTH'), -12) ,
LAST_DAY(TRUNC(SYSDATE, 'MONTH') - 4)
from dual;
see this demo: http://sqlfiddle.com/#!4/d41d8/26418

you want use simply sum function
like this
SELECT SUM(case when transits.direction = 1 then 1 else 0 end) ,
SUM(case when transits.direction = 0 then 1 else 0 end) from t1 t
where t.device in ('A','B') group by t.device
I do not know actual condion in your query so ...
SELECT
sum(case when tstamp BETWEEN ADD_MONTHS(trunc(SYSDATE,'MONTH'), -12) then 1 else 0 end) as yearCount ,
sum(case when LAST_DAY(TRUNC(SYSDATE, 'MONTH') - 4) then 1 else 0 end) as lastThreeMonth
FROM tm_process_state

Related

Oracle:: Compare count of records in Current Hour and same Hour for yesterday

I am trying to to compare number of transaction happened in current hour with yesterday same hour and last week same day same hour, below sql works perfectly if requirment is for the entire day but for hours it doesn't work.
SELECT
SUM(
CASE
WHEN request_time >= trunc(sysdate)
AND request_time <= sysdate THEN
perf_count
ELSE
NULL
END
) AS total_today,
SUM(
CASE
WHEN request_time >= trunc(sysdate) - INTERVAL '1' DAY
AND request_time < trunc(sysdate) THEN
perf_count
ELSE
NULL
END
) AS total_yesterday,
SUM(
CASE
WHEN request_time >= trunc(sysdate - 7)
AND request_time < trunc(sysdate - 6) THEN
perf_count
ELSE
NULL
END
) AS total_last_week
FROM
perf_fact
WHERE
request_time >= add_months(
trunc(
sysdate, 'MM'
), - 1
)
AND request_time <= sysdate
;
Sample Data
Expected Output
06AM_TODAY
06AM_YESTERDAY
06AM_LAST_WEEK_SAMEDAY
1234
520
5685
Here's how I'd do it:
SELECT SUM(CASE WHEN (TRUNC(SYSDATE,'HH')=TRUNC(request_time,'HH')) THEN perf_count ELSE 0 END) this_hour,
SUM(CASE WHEN (TRUNC(SYSDATE-1,'HH')=TRUNC(request_time,'HH')) THEN perf_count ELSE 0 END) yesterday_same_hour,
SUM(CASE WHEN (TRUNC(SYSDATE-7,'HH')=TRUNC(request_time,'HH')) THEN perf_count ELSE 0 END) last_week_same_hour
FROM perf_fact
Or even more concisely with DECODE:
SELECT
SUM(DECODE(TRUNC(SYSDATE,'HH'),TRUNC(request_time,'HH'),perf_count,0)) this_hour,
SUM(DECODE(TRUNC(SYSDATE-1,'HH'),TRUNC(request_time,'HH'),perf_count,0)) yesterday_same_hour,
SUM(DECODE(TRUNC(SYSDATE-7,'HH'),TRUNC(request_time,'HH'),perf_count,0)) last_week_same_hour
FROM perf_fact

How to put Case in Where Statement for Oracle SQL

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

Oracle SQL - How to retrieve the ID Count difference between today vs yesterday

I have a table that captures when a customer purchases a product. It captures a unique purchase id along with a timestamp of when the purchase was made.
I want to be able to query, the difference between how many purchases were taken today vs yesterday?
Not sure how to query this on oracle?
You can use conditional aggregation:
select sum(case when trunc(datecol) = trunc(sysdate - 1) then 1 else 0 end) as num_yesterday,
sum(case when trunc(datecol) = trunc(sysdate) then 1 else 0 end) as num_today,
sum(case when trunc(datecol) = trunc(sysdate) then 1
when trunc(datecol) = trunc(sysdate - 1) then -1
end) as diff
from t
where datecol >= trunc(sysdate - 1);
you can use the Group function to grouping the purchase day with timestamp information and count the purchase id.
select trunc(purchase_ts) Day, count(purchase_id) Count
from purchase
group by trunc(purchase_ts)
order by 1
Using TRUNC on the column will prevent Oracle from using an index on that column (instead you would need a separate function-based index); instead use a CASE statement to test whether the date is between the start of the day and the start of the next day and then COUNT the values between those ranges:
SELECT COUNT(
CASE
WHEN TRUNC( SYSDATE ) - INTERVAL '1' DAY <= your_date_column
AND your_date_coumn < TRUNC( SYSDATE )
THEN 1
END
) AS count_for_yesterday,
COUNT(
CASE
WHEN TRUNC( SYSDATE ) <= your_date_column
AND your_date_coumn < TRUNC( SYSDATE ) + INTERVAL '1' DAY
THEN 1
END
) AS count_for_today
FROM your_table
WHERE TRUNC( SYSDATE ) - INTERVAL '1' DAY <= your_date_column
AND your_date_coumn < TRUNC( SYSDATE ) + INTERVAL '1' DAY

Query to get sales from this month and previous month

We'd like to get total sales for this month and previous month. The query is:
SELECT sum(CASE
WHEN date_trunc('month', date_start)= date_trunc('month', now()) THEN sales
ELSE NULL
END) AS curr_sales,
sum(CASE
WHEN date_trunc('month', date_start)= date_trunc('month', now()- interval '1' MONTH) THEN sales
ELSE NULL
END) AS pr_sales
FROM sales
But it returned this error:
"Specified types or functions (one per INFO message) not supported on Redshift tables. We're running Postgresql 8.0.2. Any ideas? Thanks!
Use date_trunc('month', current_date) instead of date_trunc('month', now()) to get current month in redshift.
now() is not a supported function in redshift but current_date will return a date in the current session time zone (UTC by default) in the default format: YYYY-MM-DD.
UPDATE
Query w/ Sample Data >>
with sales(sales, date_start) as(
select 1 , current_date union
select 2 , current_date union
select 2 , current_date - interval '1' month union
select 3 , current_date - interval '1' month
)
SELECT sum(CASE
WHEN date_trunc('month', date_start)= date_trunc('month', current_date) THEN sales
ELSE NULL
END) AS curr_sales,
sum(CASE
WHEN date_trunc('month', date_start)= date_trunc('month', current_date - interval '1' MONTH) THEN sales
ELSE NULL
END) AS pr_sales
FROM sales;
And results are coming as expected:
curr_sales pr_sales
3 5

Is it possible to map multiple select statement into columns?

I have this table:
code(integer) |number_of_data(integer)| date (Date)
I need to:
Group by day, this for tho month of june only
Select sum of number_of_data for the day for each code != 0
Select n_data for code = 0
For the first 2 points I came up with:
select sum(number_of_data) nData, TO_CHAR(date, 'DD') dayOfMonth from T1
where to_char( date, 'mm') = 6
and code <> 0
group by TO_CHAR(date, 'DD') order by TO_CHAR(date, 'DD');
it gives me this table result:
nData | dayOfMonth
which is fine, anyway I'm missing requirement 3, whose query would be the same but with the opposite condition (code=0).
Is there a way to add it to the above query so to get this result:
nData | nDataZero | dayOfMonth
?
Whit some regards to the syntax in MS Sql. This is a way i would solve this in a oracle sql-like way:)
SELECT sum(nData) nData , sum(nDataZero) nDataZero, T1
from (
select sum(number_of_data) nData, 0 nDataZero , TO_CHAR(date, 'DD') dayOfMonth
from T1
where to_char( date, 'mm') = 6 and code <> 0
group by TO_CHAR(date, 'DD')
order by TO_CHAR(date, 'DD')
UNION
select 0 nData, sum(number_of_data) nDataZero , TO_CHAR(date, 'DD') dayOfMonth
from T1
where to_char( date, 'mm') = 6 and code == 0
group by TO_CHAR(date, 'DD')
order by TO_CHAR(date, 'DD'))
group by T1;
Rgds
Assuming that there will only be one entry with CODE = 0 for each day, then you can do:
SELECT SUM( CASE CODE WHEN 0 THEN NULL ELSE number_of_data END ) AS nData,
MAX( CASE CODE WHEN 0 THEN number_of_data END ) AS nDataZero,
EXTRACT( DAY FROM "Date" ) AS dayOfMonth
FROM T1
WHERE EXTRACT( MONTH FROM "Date" ) = 6
GROUP BY EXTRACT( DAY FROM "Date" )
ORDER BY EXTRACT( DAY FROM "Date" );
If there will be more than one entry then you will need to specify how it is to be handled (i.e. change MAX to SUM if you want the total of the CODE = 0 values).
You can use CASE to separate data into 2 slots, code=0 and code<>0:
select sum(number_of_data) nData, TO_CHAR(date, 'DD') dayOfMonth, CASE WHEN code = 0 THEN 0 ELSE 1 AS x
from T1
where to_char( date, 'mm') = 6
group by TO_CHAR(date, 'DD'), x
order by TO_CHAR(date, 'DD');