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');
Related
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
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
I am trying to create a code to join two statements each from different table and conditions, as follows:
the first statement:
select TO_CHAR(Entry_date, 'MON.YYYY') AS Months, count(Customer_id) "Count Customer"
from table1
where entry_date >= TO_DATE('01.01.1900', 'DD.MM.YYYY')
AND entry_date <= TO_DATE('31.12.2017', 'DD.MM.YYYY')
and Customer_status = 'Active'
group by TO_CHAR(entry_date,'MON.YYYY')
order by to_date(TO_CHAR(entry_date, 'MON.YYYY'),'MON.YYYY')
The second statement:
select count (order_id) "Order"
from table2
where leave_date >= TO_DATE('01.01.1900', 'DD.MM.YYYY')
AND leave_date <= TO_DATE('31.12.2017', 'DD.MM.YYYY')
group by TO_CHAR(leave_date,'MON.YYYY')
order by to_date(TO_CHAR(leave_date, 'MON.YYYY'),'MON.YYYY')
the result should look like this
Months Count Customer Order
Jan. 2017 15 0
Feb. 2017 1 8
Mar. 2017 30 10
The order should be dependent on the Months that were stated in the first statement.
Thanks for your help in advance.
I would write this as:
select yyyymm, sum(cust_count) as cust_count, sum(num_orders) as num_orders
from ((select to_char(entry_date, 'YYYY-MM') as yyyymm, count(*) as cust_count, 0 as num_orders
from table1
where entry_date >= date '1900-01-01' and
entry_date < date '2018-01-01' and
Customer_status = 'Active'
group by to_char(entry_date, 'YYYY-MM')
) union all
(select to_char(leave_date, 'YYYY-MM') as yyyymm, 0,
count(*) as num_orders
from table2
where leave_date >= date '1900-01-01' and
leave_date < date '2018-01-31'
group by to_char(leave_date, 'YYYY-MM')
)
) tt
group by yyyymm
order by yyyymm;
Notes on some changes:
The use of date rather than to_char() with date constants.
The use of the format "YYYY-MM", which orders correctly. (You don't have to use it but it recommended.)
The union all brings all the data together. In Oracle, you can also use a full outer join, but that requires more use of coalesce().
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
I have a bunch of queries that take data with a time stamp and spit out SUMS based the last few weeks, months, and year to date. It looks like this
Week1 Sum for most recent week
Week2 Sum for second most recent week
WeekN Sum for N most recent week
Jan-Dec Sum for January-December
YTD Sum for everything this year
This is how the query currently does this
SELECT TIME_PERIOD, INDEX, SUM(ITEM)
FROM (SELECT
INDEX ,
(CASE
WHEN ACTIVITY_DAY>=(TO_DATE( :end_day,
'yyyy-mm-dd' )-6)
AND ACTIVITY_DAY<=(TO_DATE( :end_day,
'yyyy-mm-dd' )-0) THEN 'WEEK1'
WHEN ACTIVITY_DAY>=(TO_DATE( :end_day,
'yyyy-mm-dd' )-13)
AND ACTIVITY_DAY<=(TO_DATE( :end_day,
'yyyy-mm-dd' )-7) THEN 'WEEK2'
ELSE NULL
END) AS TIME_PERIOD,
MAX(ITEMS) AS ITEM
FROM
SOURCE
GROUP BY
INDEX ,
DAY
UNION
ALL SELECT
INDEX ,
(CASE
WHEN ACTIVITY_DAY>=TO_DATE( :year||'-01-01',
'yyyy-mm-dd' )
AND ACTIVITY_DAY<=TO_DATE( :year||'-01-31',
'yyyy-mm-dd' ) THEN 'Jan'
WHEN ACTIVITY_DAY>=TO_DATE( :year||'-02-01',
'yyyy-mm-dd' )
AND ACTIVITY_DAY<TO_DATE( :year||'-03-01',
'yyyy-mm-dd' ) THEN 'Feb'
ELSE NULL
END) AS TIME_PERIOD ,
MAX(ITEMS) AS ITEM
FROM
SOURCE
GROUP BY
INDEX ,
DAY
UNION
ALL SELECT
INDEX ,
(CASE
WHEN ACTIVITY_DAY>=TO_DATE( :year||'-01-01',
'yyyy-mm-dd' )
AND ACTIVITY_DAY<=TO_DATE( :end_day,
'yyyy-mm-dd' ) THEN 'YTD'
ELSE NULL
END) AS TIME_PERIOD,
MAX(ITEMS) AS ITEM
FROM
SOURCE
GROUP BY
INDEX ,
DAY)
GROUP BY INDEX, TIME_PERIOD
Is there a better way in Oracle?
I think you are looking for something like this:
with data as
(
select sysdate - floor(dbms_random.value(1,400)) dt, floor(dbms_random.value(1,100)) val
from dual
connect by level <= 100
)
select
time_period,
sum(val) period_sum
from
(
select -- weeks
'Week'||(to_char(sysdate, 'WW') - to_char(dt, 'WW') + 1) time_period,
val,
(to_char(sysdate, 'WW') - to_char(dt, 'WW') + 1) ord
from data
where dt >= trunc(sysdate,'YY')
union all
select -- months
to_char(dt, 'Mon') time_period,
val,
100+to_char(dt,'MM') ord
from data
where dt >= trunc(sysdate,'YY')
union all
select -- months
'YTD' time_period,
val,
200
from data
where dt >= trunc(sysdate,'YY')
)
group by
time_period, ord
order by
ord;
Note that you won't need the WITH block, I was just using it to create some dummy data. The Ord column might be unnecessary for you, I was just using it to order the data in a logical fashion.