Previous month difference and last year-end difference in oracle sql - oracle

i need to write a query that will calculate difference between last month-end and month-end and difference between last year-end and month-end. I created sample database in sqlfiddle http://sqlfiddle.com/#!4/b9749
In my database the most important date is always the month-end but as you can see in the sample there there are other dates as well but i can't use values from these dates. When i run this query with condidtion that date ='2014-04-30' the result should be like this:
date product amount last_month_diff last_year_end_diff
2014-04-30 a1 350 -150 650
2014-04-30 b1 123 -123 1877
when i run this query with condidtion that date ='2014-05-31' the result should be like this
date product amount last_month_diff last_year_end_diff
2014-05-31 a1 400 -50 600
2014-05-31 b1 500 -377 1500
2014-05-31 c1 200 0 0
and when i run this query with condidtion that date ='2014-06-30' the result should be like this
date product amount last_month_diff last_year_end_diff
2014-06-30 b1 780 -280 1220
2014-06-30 c1 100 100 0
At first i thought i use analytical functions (lag) but i may have many dates between two month-ends and i don't know how to achieve the expected result.

Try something like the bellow.
with input_date as (select to_date('2014-04-30', 'YYYY-MM-DD') d from dual),
sot_tot as (select product,
sum(case when extract(month from date_) = extract(month from d) then amount else 0end) amount,
sum(case when extract(month from date_) = extract(month from last_day(add_months(d, -1))) then amount else 0 end) previous_month_amount
from sot, input_date
where date_ <= d
group by product)
select product, amount, previous_month_amount - amount as previos_month_diff
from sot_tot
I was not able understand what you mean by difference between last month-end and month-end and difference between last year-end and month-end. However the solution will be very similar to the one above and you can play with the sum and case combination to achieve the result you want.

Related

How to achieve below code using Oracle PL/SQL

How I can achieve below,
I have to check last 30 days from the latest statement date w.r. to account using following formula
if ((sum of credits for 30 days from the latest statement date w.r. to account/
sum debits for 30 days from the latest statement date w.r. to account)
-sum debits for 30 days from the latest statement date w.r. to account)>0 then YES else NO
below are the two columns from two different tables based on which i need to derived formula for each account. And accounts present in both tables A and B.
STATEMENT_DATE_LATEST; --from TableA
LATEST_BAL_IN_USD; -- from TableB
Note: "/" is Divided by and "-" is Minus sign in above statement
If I understood it correctly, would something like this help?
with temp as
(select a.account,
sum(case when b.transaction_amount >= 0 then b.transaction_amount end) sum_credits,
sum(case when b.transaction_amount < 0 then b.transaction_amount end) sum_debits
from table_a a join table_b b on a.account = b.account
where b.statement_date > a.statement_date_latest + 30
group by s.account
)
select t.account,
case when (t.sum_credits / t.sum_debits) - t.sum_debits > 0 then 'YES'
else 'NO'
end result
from temp t;

Referancing value from select column in where clause : Oracle

My tables are as below
MS_ISM_ISSUE
ISSUE_ID ISSUE_DUE_DATE ISSUE_SOURCE_TYPE
I1 25-11-2018 1
I2 25-12-2018 1
I3 27-03-2019 2
MS_ISM_SOURCE_SETUP
SOURCE_ID MODULE_NAME
1 IT-Compliance
2 Risk Assessment
I have written following query.
with rs as
(select
count(ISSUE_ID) as ISSUE_COUNT, src.MODULE_NAME,
case
when ISSUE_DUE_DATE<sysdate then 'Overdue'
when ISSUE_DUE_DATE between sysdate and sysdate + 90 then 'Within 3 months'
when ISSUE_DUE_DATE>sysdate+90 then 'Beyond 90 days'
end as date_range
from MS_ISM_ISSUE issue, MS_ISM_SOURCE_SETUP src
where issue.Issue_source_type = src.source_id
group by src.MODULE_NAME, case
when ISSUE_DUE_DATE<sysdate then 'Overdue'
when ISSUE_DUE_DATE between sysdate and sysdate + 90 then 'Within 3 months'
when ISSUE_DUE_DATE>sysdate+90 then 'Beyond 90 days'
end)
select ISSUE_COUNT,MODULE_NAME, DATE_RANGE,
(select count(ISSUE_COUNT) from rs where rs.MODULE_NAME=MODULE_NAME) as total from rs;
The output of the code is as below.
ISSUE_COUNT MODULE_NAME DATE_RANGE Total
1 IT-Compliance Overdue 3
1 IT-Compliance Within 3 months 3
1 Risk Assessment Beyond 90 days 3
The result is correct till 3rd column. In 4th column what I want is, total of Issue count for given module name. Hence in above case Total column will have value as 2 for first and second row (since there are 2 Issues for IT-Compliance) and value 1 for the third row (since one issue is present for Risk Assessment).
Essentially, I want to achieve is to replace current row's MODULE_NAME in last where clause. How do I achieve this using query?
OK, this condition
where rs.MODULE_NAME=MODULE_NAME
is essentially the same as if you wrote
where MODULE_NAME = MODULE_NAME
which is simply always true (if there are no nulls in module_name).
Try using different table alias for inner query and outer query, e.g.
select count(ISSUE_COUNT) from rs rs2 where rs2.MODULE_NAME=rs.MODULE_NAME
You can also try to use analytic function here, something like
select ISSUE_COUNT,
MODULE_NAME,
DATE_RANGE,
COUNT(ISSUE_COUNT) OVER (PARTITION BY RS.MODULE_NAME) AS TOTAL
from rs
instead of your subquery

How to generate each day of backlog for a ticket

Hi I'm trying to create a procedure for calculating the backlog for each day.
For example: I have a ticket with ticket_submitdate on 12-sep-2015 and resolved_date on 15-sep-2015 in one table. This ticket should come as a backlog in the backlog_table because it was not resolved on the same day as the ticket_submitdate.
I have another column date_col in the backlog_table where the date on which the ticket was a backlog is displayed,i.e, it should be there in the ticket_backlog table for dates 13-sep-2015 and 14-sep-2015 and the date_col column should have this ticket for both these dates.
Please help.
Thanks in advance.
Here is some test data:
create table backlog (ticket_no number, submit_date date, resolved_date date);
insert into backlog values (100, date '2015-09-12', date '2015-09-15');
insert into backlog values (200, date '2015-09-12', date '2015-09-14');
insert into backlog values (300, date '2015-09-13', date '2015-09-15');
insert into backlog values (400, date '2015-09-13', date '2015-09-16');
insert into backlog values (500, date '2015-09-13', date '2015-09-13');
This query generates a list of dates which spans the range of BACKLOG records, and joins them to the BACKLOG.
with dt as ( select min(submit_date) as st_dt
, greatest(max(resolved_date), max(submit_date)) as end_dt
from backlog)
, dt_range as ( select st_dt + (level-1) as date_col
from dt
connect by level <= ( end_dt - st_dt ))
select b.ticket_no
, d.date_col
from backlog b
cross join dt_range d
where d.date_col between b.submit_date and b.resolved_date
and b.submit_date != b.resolved_date
order by b.ticket_no
, d.date_col
/
Therefore it produces a list of TICKET_NOs with all the dates when they are live:
TICKET_NO DATE_COL
---------- ---------
100 12-SEP-15
100 13-SEP-15
100 14-SEP-15
100 15-SEP-15
200 12-SEP-15
200 13-SEP-15
200 14-SEP-15
300 13-SEP-15
300 14-SEP-15
300 15-SEP-15
400 13-SEP-15
400 14-SEP-15
400 15-SEP-15
14 rows selected.
SQL>
The result set does not include ticket #500 because it was resolved on the day of submission. You will probably need to tweak the filters to fit your actual business rules.
I m not sure I understood your question, if you are looking for all dates between two date range then you can use below query -
select trunc(date_col2+lv) from
(select level lv from dual connect by level < (date_col1-date_col2-1) )
order by 1

Oracle query that count connections by minute with start and end times provided

I have connections into a system and in the database I have the username, starttime_of_connection, and endtime_of_connection. I want to be able to check by minute, how many connections were active at each minute. This is using Oracle DB. Any suggestions or help?
Assuming that starttime_of_connection and endtime_of_connection are date or timestamp columns, you can do something like this. I'm dynamically generating dates with every minute today in the each_minute common table expression (CTE). You can extend this to generate dates across multiple days or to substitute in some other table that you already have that gives you the data for each minute.
WITH each_minute AS (
SELECT trunc(sysdate) + numtodsinterval( level, 'minute' ) dt
FROM dual
CONNECT BY level <= 24*60
)
SELECT em.dt, count(*) num_active_connections
FROM each_minute em
LEFT OUTER JOIN your_table yt
ON( em.dt BETWEEN yt.starttime_of_connection AND yt.endtime_of_connection )
I'm also assuming that endtime_of_connection is always populated-- you may use a NULL endtime_of_connection to indicate a connection that has not yet ended in which case your join would be something like
ON( em.dt BETWEEN yt.starttime_of_connection AND yt.endtime_of_connection
OR (em.dt >= yt.starttime_of_connection AND yt.endtime_of_connection IS NULL) )
This query worked for me:
with period as (select date '2015-04-01' d1,
date '2015-04-30' + 1 - interval '1' minute d2 from dual),
tmp as (
select d1, d2, trunc(starttime_of_connection, 'mi') soc,
trunc(nvl(endtime_of_connection, greatest(starttime_of_connection, d2)), 'mi') eoc
from connections, period),
cn as (select greatest(soc, d1) cd1, least(eoc, d2) cd2
from tmp where soc<=d2 and eoc>=d1),
mt as (select d1 + (level-1)/(24*60) m
from period connect by d1 + (level-1)/(24*60) <= d2)
select to_char(m, 'mm-dd hh24:mi') minute, count(cd1) cnt
from mt /*left*/ join cn on m between cd1 and cd2
group by m order by m
SQLFiddle demo
In first line please change dates for analyzed period. If you want to show minutes with no connections, for instance to make graph then change join at bottom to left join.
For big number of connections and long period query may execute very long. If so you can divide period to weekends/days and union results.
Subquery tmp truncates dates to minutes and fills endtime of connections for null values.
cn filters previous results to interesting period. mt is hierarchical minute generator.
Main query joins connections cn and minutes mt, groups them and orders by time.
If there was connection which started at hour 23:34:07 and ended at 23:34:45 it is counted for minute 23:34.

Grouping data by date ranges

I wonder how do I select a range of data depending on the date range?
I have these data in my payment table in format dd/mm/yyyy
Id Date Amount
1 4/1/2011 300
2 10/1/2011 200
3 27/1/2011 100
4 4/2/2011 300
5 22/2/2011 400
6 1/3/2011 500
7 1/1/2012 600
The closing date is on the 27 of every month. so I would like to group all the data from 27 till 26 of next month into a group.
Meaning to say I would like the output as this.
Group 1
1 4/1/2011 300
2 10/1/2011 200
Group 2
1 27/1/2011 100
2 4/2/2011 300
3 22/2/2011 400
Group 3
1 1/3/2011 500
Group 4
1 1/1/2012 600
It's not clear the context of your qestion. Are you querying a database?
If this is the case, you are asking about datetime but it seems you have a column in string format.
First of all, convert your data in datetime data type (or some equivalent, what db engine are you using?), and then use a grouping criteria like this:
GROUP BY datepart(month, dateadd(day, -26, [datefield])), DATEPART(year, dateadd(day, -26, [datefield]))
EDIT:
So, you are in Linq?
Different language, same logic:
.GroupBy(x => DateTime
.ParseExact(x.Date, "dd/mm/yyyy", CultureInfo.InvariantCulture) //Supposed your date field of string data type
.AddDays(-26)
.ToString("yyyyMM"));
If you are going to do this frequently, it would be worth investing in a table that assigns a unique identifier to each month and the start and end dates:
CREATE TABLE MonthEndings
(
MonthID INTEGER NOT NULL PRIMARY KEY,
StartDate DATE NOT NULL,
EndDate DATE NOT NULL
);
INSERT INTO MonthEndings VALUES(201101, '27/12/2010', '26/01/2011');
INSERT INTO MonthEndings VALUES(201102, '27/01/2011', '26/02/2011');
INSERT INTO MonthEndings VALUES(201103, '27/02/2011', '26/03/2011');
INSERT INTO MonthEndings VALUES(201112, '27/11/2011', '26/01/2012');
You can then group accurately using:
SELECT M.MonthID, P.Id, P.Date, P.Amount
FROM Payments AS P
JOIN MonthEndings AS M ON P.Date BETWEEN M.StartDate and M.EndDate
ORDER BY M.MonthID, P.Date;
Any group headings etc are best handled out of the DBMS - the SQL gets you the data in the correct sequence, and the software retrieving the data presents it to the user.
If you can't translate SQL to LINQ, that makes two of us. Sorry, I have never used LINQ, so I've no idea what is involved.
SELECT *, CASE WHEN datepart(day,date)<27 THEN datepart(month,date)
ELSE datepart(month,date) % 12 + 1 END as group_name
FROM payment

Resources