Query Optimization to reduce multiple joins statements - sql

Here is the table:
CREATE TABLE ABC
(
key NUMBER(5),
orders NUMBER(5),
cost NUMBER(5),
dat DATE
);
insert into ABC (key, orders, cost, dat) values (1, 3, 5, to_date('10-11-
2017', 'mm-dd-yyyy'));
insert into ABC (key, orders, cost,dat) values (1, 5, 2, to_date('02-10-
2017', 'mm-dd-yyyy'));
insert into ABC (key, orders, cost,dat) values (1, 6, 1, to_date('03-10-
2017', 'mm-dd-yyyy'));
insert into ABC (key, orders, cost,dat) values (1, 7, 2, to_date('05-10-
2017', 'mm-dd-yyyy'));
insert into ABC (key, orders, cost,dat) values (1, 8, 3, to_date('07-10-
2017', 'mm-dd-yyyy'));
insert into ABC (key, orders, cost,dat) values (1, 3, 4, to_date('08-10-
2017', 'mm-dd-yyyy'));
insert into ABC (key, orders, cost,dat) values (2, 3, 6, to_date('02-10-
2017', 'mm-dd-yyyy'));
insert into ABC (key, orders, cost,dat) values (2, 3, 9, to_date('01-10-
2017', 'mm-dd-yyyy'));
insert into ABC (key, orders, cost,dat) values (2, 2 ,5, to_date('03-10-
2017', 'mm-dd-yyyy'));
insert into ABC (key, orders, cost,dat) values (2, 3, 2, to_date('05-10-
2017', 'mm-dd-yyyy'));
insert into ABC (key, orders, cost,dat) values (2, 1, 1, to_date('06-10-
2017', 'mm-dd-yyyy'));
insert into ABC (key, orders, cost,dat) values (3, 4, 12, to_date('10-10-
2017', 'mm-dd-yyyy'));
insert into ABC (key, orders, cost,dat) values (3, 3, 9, to_date('01-10-
2017', 'mm-dd-yyyy'));
insert into ABC (key, orders, cost,dat) values (3, 2 ,5, to_date('05-10-
2017', 'mm-dd-yyyy'));
insert into ABC (key, orders, cost,dat) values (3, 3, 2, to_date('06-10-
2017', 'mm-dd-yyyy'));
insert into ABC (key, orders, cost,dat) values (3, 1, 1, to_date('07-10-
2017', 'mm-dd-yyyy'));
insert into ABC (key, orders, cost,dat) values (3, 4, 12, to_date('11-10-
2017', 'mm-dd-yyyy'));
insert into ABC (key, orders, cost, dat) values (1, 3, 5, to_date('10-01-
2017', 'mm-dd-yyyy'));
insert into ABC (key, orders, cost,dat) values (1, 5, 2, to_date('02-17-
2017', 'mm-dd-yyyy'));
insert into ABC (key, orders, cost,dat) values (1, 6, 1, to_date('03-18-
2017', 'mm-dd-yyyy'));
insert into ABC (key, orders, cost,dat) values (1, 7, 2, to_date('05-14-
2017', 'mm-dd-yyyy'));
insert into ABC (key, orders, cost,dat) values (1, 8, 3, to_date('07-13-
2017', 'mm-dd-yyyy'));
insert into ABC (key, orders, cost,dat) values (1, 3, 4, to_date('08-12-
2017', 'mm-dd-yyyy'));
insert into ABC (key, orders, cost,dat) values (2, 3, 6, to_date('02-11-
2017', 'mm-dd-yyyy'));
insert into ABC (key, orders, cost,dat) values (2, 3, 9, to_date('01-15-
2017', 'mm-dd-yyyy'));
insert into ABC (key, orders, cost,dat) values (2, 2 ,5, to_date('03-14-
2017', 'mm-dd-yyyy'));
insert into ABC (key, orders, cost,dat) values (2, 3, 2, to_date('05-18-
2017', 'mm-dd-yyyy'));
insert into ABC (key, orders, cost,dat) values (2, 1, 1, to_date('06-19-
2017', 'mm-dd-yyyy'));
insert into ABC (key, orders, cost,dat) values (3, 4, 12, to_date('10-11-
2017', 'mm-dd-yyyy'));
insert into ABC (key, orders, cost,dat) values (3, 3, 9, to_date('01-12-
2017', 'mm-dd-yyyy'));
insert into ABC (key, orders, cost,dat) values (3, 2 ,5, to_date('05-16-
2017', 'mm-dd-yyyy'));
insert into ABC (key, orders, cost,dat) values (3, 3, 2, to_date('06-17-
2017', 'mm-dd-yyyy'));
insert into ABC (key, orders, cost,dat) values (3, 1, 1, to_date('07-12-
2017', 'mm-dd-yyyy'));
insert into ABC (key, orders, cost,dat) values (3, 4, 12, to_date('12-21-
2017', 'mm-dd-yyyy'));
Not sure why my results are repeating.
Here is my query:
with qone as
(select a.key, a.max_price, max(t.dat) as qo_dat from ABC t
JOIN
(select key, max(cost) as max_price from ABC
where dat >= to_date('01-01-2017', 'mm-dd-yyyy') and dat < to_date('04-01-
2017', 'mm-dd-yyyy')
group by key) a on a.key = t.key and a.max_price = t.cost
group by a.key, a.max_price),
qtwo as
(select a.key, a.max_price, max(t.dat) as qt_dat from ABC t
JOIN
(select key, max(cost) as max_price from ABC
where dat >= to_date('04-01-2017', 'mm-dd-yyyy') and dat < to_date('07-01-
2017', 'mm-dd-yyyy')
group by key) a on a.key = t.key and a.max_price = t.cost
group by a.key, a.max_price),
qthree as
(select a.key, a.max_price, max(t.dat) as qth_dat from ABC t
JOIN
(select key, max(cost) as max_price from ABC
where dat >= to_date('07-01-2017', 'mm-dd-yyyy') and dat < to_date('10-01-
2017', 'mm-dd-yyyy')
group by key) a on a.key = t.key and a.max_price = t.cost
group by a.key, a.max_price),
qfour as
(select a.key, a.max_price, max(t.dat) as qf_dat from ABC t
JOIN
(select key, max(cost) as max_price from ABC
where dat >= to_date('10-01-2017', 'mm-dd-yyyy') and dat < to_date('01-01-
2018', 'mm-dd-yyyy')
group by key) a on a.key = t.key and a.max_price = t.cost
group by a.key, a.max_price)
select qo.key, qo.max_price as max_q1, qo.qo_dat, qt.max_price as max_q2,
qt.qt_dat, qth.max_price as max_q3, qth.qth_dat, qf.max_price as max_q4,
qf.qf_dat from qone qo
join qtwo qt on qt.key = qo.key
join qthree qth on qth.key = qth.key
join qfour qf on qf.key = qf.key
order by keyenter code here
I want to know if there is a way to reduce the lines.
How I did it? I find the Max Price and Max Date for each quarter, I define the quarters using where statement.
I use Divide and Conquer technique, I find the Max price and the respective date for all four quarters and join them on the key. Sample of one self-defined quarter below.
`select a.key, a.max_price, max(t.dat) as qo_dat from ABC t
JOIN
(select key, max(cost) as max_price from ABC
where dat >= to_date('01-01-2017', 'mm-dd-yyyy') and dat < to_date('04-01-
2017', 'mm-dd-yyyy')
group by key) a on a.key = t.key and a.max_price = t.cost
group by a.key, a.max_price`
Output:
Possible optimized solution: but I m figuring out a way to add a corresponding date next to it
select
t.key,
max( case when t.dat >= Tmp.Q1From and t.dat < Tmp.Q1End then t.cost
else 0 end ) as Q1Tot,
max( case when t.dat >= Tmp.Q1End and t.dat < Tmp.Q2End then t.cost else
0 end ) as Q2Tot,
max( case when t.dat >= Tmp.Q2End and t.dat < Tmp.Q3End then t.cost else
0 end ) as Q3Tot,
max( case when t.dat >= Tmp.Q3End and t.dat < Tmp.Q4End then t.cost else
0 end ) as Q4Tot
from
ABC t,
( select
to_date('01-01-2017', 'mm-dd-yyyy') Q1From,
to_date('04-01-2017', 'mm-dd-yyyy') Q1End,
to_date('07-01-2017', 'mm-dd-yyyy') Q2End,
to_date('10-01-2017', 'mm-dd-yyyy') Q3End,
to_date('01-01-2018', 'mm-dd-yyyy') Q4End
from
dual ) Tmp
where
t.dat >= to_date('01-01-2017', 'mm-dd-yyyy')
and t.dat < to_date('01-01-2018', 'mm-dd-yyyy')
group by
t.key

Instead of using JOINs or cross joins, consider using the analytical function NTH_VALUE (see documentation) for displaying the required values for the 4 quarters side by side.
NTH_VALUE returns the measure_expr value of the nth row in the window
defined by the analytic_clause.
First step: find the "max costs" and their corresponding dates for all keys (and quarters).
select *
from (
select key, dat, to_char( dat, 'Q' ) quarter
, max( cost ) over ( partition by key, to_char( dat, 'Q' ) order by cost desc ) maxcost_
, max( dat ) over ( partition by key, to_char( dat, 'Q' ) order by cost desc ) maxdat_
, row_number() over ( partition by key, to_char( dat, 'Q' ) order by cost desc ) rownum_
from abc
)
where rownum_ = 1
-- result
KEY DAT QUARTER MAXCOST_ MAXDAT_ ROWNUM_
1 17-FEB-17 1 2 17-FEB-17 1
1 14-MAY-17 2 2 14-MAY-17 1
1 12-AUG-17 3 4 12-AUG-17 1
1 01-OCT-17 4 5 11-OCT-17 1
2 10-JAN-17 1 9 15-JAN-17 1
2 10-MAY-17 2 2 18-MAY-17 1
3 10-JAN-17 1 9 12-JAN-17 1
3 10-MAY-17 2 5 16-MAY-17 1
3 10-JUL-17 3 1 12-JUL-17 1
3 10-NOV-17 4 12 21-DEC-17 1
10 rows selected.
Final query: use the first query as an INLINE VIEW, and call NTH_VALUE for retrieving the values for each quarter.
select unique key
, nth_value( maxcost_, 1 ) from first over ( partition by key ) q1max
, nth_value( maxdat_, 1 ) from first over ( partition by key ) q1date
, nth_value( maxcost_, 2 ) from first over ( partition by key ) q2max
, nth_value( maxdat_, 2 ) from first over ( partition by key ) q2date
, nth_value( maxcost_, 3 ) from first over ( partition by key ) q3max
, nth_value( maxdat_, 3 ) from first over ( partition by key ) q3date
, nth_value( maxcost_, 4 ) from first over ( partition by key ) q4max
, nth_value( maxdat_, 4 ) from first over ( partition by key ) q4date
from (
select *
from (
select key, dat, to_char( dat, 'Q' ) quarter
, max( cost ) over ( partition by key, to_char( dat, 'Q' ) order by cost desc ) maxcost_
, max( dat ) over ( partition by key, to_char( dat, 'Q' ) order by cost desc ) maxdat_
, row_number() over ( partition by key, to_char( dat, 'Q' ) order by cost desc ) rownum_
from abc
)
where rownum_ = 1
) -- inline view (no name required)
order by key
;
-- result
KEY Q1MAX Q1DATE Q2MAX Q2DATE Q3MAX Q3DATE Q4MAX Q4DATE
1 2 17-FEB-17 2 14-MAY-17 4 12-AUG-17 5 11-OCT-17
2 9 15-JAN-17 2 18-MAY-17 NULL NULL NULL NULL
3 9 12-JAN-17 5 16-MAY-17 1 12-JUL-17 12 21-DEC-17

Maybe you could rewrite it in a shorter way, as in SQL Fiddle:
select a.key, qtr, a.max_price, max(t.dat) as qo_dat
from ABC t
join (
select key, to_char(dat, 'Q') as qtr, max(cost) as max_price
from ABC
where dat >= to_date('01-01-2017', 'mm-dd-yyyy')
and dat < to_date('01-01-2018', 'mm-dd-yyyy')
group by key, to_char(dat, 'Q')
) a on a.key = t.key and a.max_price = t.cost and a.qtr = to_char(t.dat, 'Q')
group by a.key, a.qtr, a.max_price
order by a.key, a.qtr, a.max_price
The output is a little bit different, but it shows what you want. Doesn't it?

select a.key, a.q1tot, b.dat, a.q2tot, c.dat, a.q3tot, d.dat, a.q4tot, e.dat from (
select
t.key,
max( case when t.dat >= Tmp.Q1From and t.dat < Tmp.Q1End then t.cost else 0 end ) as Q1Tot,
max( case when t.dat >= Tmp.Q1End and t.dat < Tmp.Q2End then t.cost else 0 end ) as Q2Tot,
max( case when t.dat >= Tmp.Q2End and t.dat < Tmp.Q3End then t.cost else 0 end ) as Q3Tot,
max( case when t.dat >= Tmp.Q3End and t.dat < Tmp.Q4End then t.cost else 0 end ) as Q4Tot
from
ABC t,
( select
to_date('01-01-2017', 'mm-dd-yyyy') Q1From,
to_date('04-01-2017', 'mm-dd-yyyy') Q1End,
to_date('07-01-2017', 'mm-dd-yyyy') Q2End,
to_date('10-01-2017', 'mm-dd-yyyy') Q3End,
to_date('01-01-2018', 'mm-dd-yyyy') Q4End
from
dual ) Tmp
where
t.dat >= to_date('01-01-2017', 'mm-dd-yyyy')
and t.dat < to_date('01-01-2018', 'mm-dd-yyyy')
group by
t.key) a
join
( select key, cost, dat from ABC
where dat < to_date('04-01-2017', 'mm-dd-yyyy')) b
on a.key = b.key and a.Q1tot = b.cost
join
( select key, cost, dat from ABC
where dat >= to_date('04-01-2017', 'mm-dd-yyyy') and dat < to_date('07-01-2017',
'mm-dd-yyyy')) c
on a.key = c.key and a.Q1tot = c.cost
join
( select key, cost, dat from ABC
where dat >= to_date('07-01-2017', 'mm-dd-yyyy') and dat < to_date('10-01-2017',
'mm-dd-yyyy')) d
on a.key = d.key and a.Q1tot = d.cost
join
( select key, cost, dat from ABC
where dat >= to_date('10-01-2017', 'mm-dd-yyyy') and dat < to_date('01-01-2018', 'mm-dd-yyyy')) e
on a.key = e.key and a.Q1tot = e.cost
This is my code, But the above two queries perform faster

Related

Identify rows subsequent to other rows based on criteria?

I am fairly new to DB2 and SQL. There exists a table of customers and their visits. I need to write a query to find visits by the same customer subsequent and within 24hr to a visit when Sale = 'Y'.
Based on this example data:
CustomerId
VisitID
Sale
DateTime
1
1
Y
2021-04-23 20:16:00.000000
2
2
N
2021-04-24 20:16:00.000000
1
3
N
2021-04-23 21:16:00.000000
2
4
Y
2021-04-25 20:16:00.000000
3
5
Y
2021-04-23 20:16:00.000000
2
6
N
2021-04-25 24:16:00.000000
3
7
N
2021-5-23 20:16:00.000000
The query results should return:
VisitID
3
6
How do I do this?
Try this. You may uncomment the commented out block to run this statement as is.
/*
WITH MYTAB (CustomerId, VisitID, Sale, DateTime) AS
(
VALUES
(1, 1, 'Y', '2021-04-23 20:16:00'::TIMESTAMP)
, (1, 3, 'N', '2021-04-23 21:16:00'::TIMESTAMP)
, (2, 2, 'N', '2021-04-24 20:16:00'::TIMESTAMP)
, (2, 4, 'Y', '2021-04-25 20:16:00'::TIMESTAMP)
, (2, 6, 'N', '2021-04-25 23:16:00'::TIMESTAMP)
, (3, 5, 'Y', '2021-04-23 20:16:00'::TIMESTAMP)
, (3, 7, 'N', '2021-05-23 20:16:00'::TIMESTAMP)
)
*/
SELECT VisitID
FROM MYTAB A
WHERE EXISTS
(
SELECT 1
FROM MYTAB B
WHERE B.CustomerID = A.CustomerID
AND B.Sale = 'Y'
AND B.VisitID <> A.VisitID
AND A.DateTime BETWEEN B.DateTime AND B.DateTime + 24 HOUR
)

risk_score result for each of month

I want to generate highest risk_score result for each of month (Jan, Feb & Mar)
Displaying the following columns: Firm_id_1, risk_score_Jan, risk_score_Feb, risk_score_Mar
CREATE table firm_risk (
firm_id_1 INT,
assessment_date DATE,
risk_score FLOAT
);
INSERT INTO firm_risk (firm_id_1, assessment_date, risk_score)
VALUES (123, '1/01/2018', 0.43),
(123, '1/28/2018', 0.80),
(123, '2/11/2018', 0.28),
(123, '2/23/2018', 0.91),
(123, '3/11/2018', 0.08),
(123, '3/31/2018', 0.60),
(456, '1/4/2018', 0.87),
(456, '1/6/2018', 0.02),
(456, '1/20/2018', 0.39),
(456, '2/3/2018', 0.10),
(456, '3/1/2018', 0.12),
(789, '1/1/2018', 0.20),
(789, '3/1/2018', 0.17);
SELECT * FROM firm_risk;
SELECT firm_id_1, date_part('month', assessment_date) AS AD
FROM firm_risk
WHERE assessment_date = (SELECT MAX (assessment_date) FROM firm_risk)
GROUP BY firm_id_1, risk_score, assessment_date;
CREATE table latest_risk_score (
firm_id_2 integer,
latest_risk_score_Jan float,
latest_risk_score_Feb float,
latest_risk_score_Mar float
);
SELECT * FROM latest_risk_score;
INSERT INTO latest_risk_score (firm_id_2)
VALUES (123),
(456),
(789);
SELECT firm_risk.firm_id_1, date_part('month', assessment_date), firm_risk.risk_score
FROM firm_risk
INNER JOIN latest_risk_score
ON firm_risk.firm_id_1 = latest_risk_score.firm_id_2
GROUP BY firm_risk.firm_id_1, firm_risk.risk_score, assessment_date;
SELECT firm_risk.firm_id_1, date_part('month', assessment_date), firm_risk.risk_score
FROM firm_risk
WHERE assessment_date = (SELECT MAX (assessment_date) FROM firm_risk)
AND assessment_date LIKE '_%-01-2018%';
SELECT firm_risk.firm_id_1, date_part('month', assessment_date)
FROM firm_risk
WHERE assessment_date >= date_part('month', assessment_date - '3 months')
GROUP BY firm_risk.firm_id_1, ('month', assessment_date);
UPDATE latest_risk_score SET latest_risk_score_Jan = (SELECT Risk_Score FROM firm_risk.firm_id_1 WHERE Assessment_Date = (SELECT MAX(Assessment_Date)
FROM firm_risk.firm_id_1 WHERE firm_id_1 = 123 AND Assessment_Date LIKE "2018-01-%" ORDER BY Assessment_Date))
WHERE firm_id_1 = 123;
update latest_risk_score
set latest_risk_score_Feb = (select Risk_Score from firm_risk.firm_id_1 where Assessment_Date = (select max(Assessment_Date)
from firm_risk.firm_id_1 where firm_id_1 = 123 and Assessment_Date like "2018-02-%" order by Assessment_Date))
where firm_id_1 = 123;
update latest_risk_score
set latest_risk_score_Mar = (select Risk_Score from firm_risk.firm_id_1 where Assessment_Date = (select max(Assessment_Date)
from firm_risk.firm_id_1 where firm_id_1 = 123 and Assessment_Date like "2018-03-%" order by Assessment_Date))
where firm_id_1 = 123;
select * from latest_risk_score;
Assuming postgres is relevant (due to existence of "date_part" in question)
CREATE table firm_risk (
firm_id_1 INT,
assessment_date DATE,
risk_score FLOAT
);
INSERT INTO firm_risk (firm_id_1, assessment_date, risk_score)
VALUES (123, '2018-01-01', 0.43),
(123, '2018-01-28', 0.80),
(123, '2018-02-11', 0.28),
(123, '2018-02-23', 0.91),
(123, '2018-03-11', 0.08),
(123, '2018-03-31', 0.60),
(456, '2018-01-04', 0.87),
(456, '2018-01-06', 0.02),
(456, '2018-01-20', 0.39),
(456, '2018-02-03', 0.10),
(456, '2018-03-01', 0.12),
(789, '2018-01-01', 0.20),
(789, '2018-03-01', 0.17);
SELECT
firm_risk.firm_id_1
, max(case when date_part('month',assessment_date) = 1 then firm_risk.risk_score end) jan_risk
, max(case when date_part('month',assessment_date) = 2 then firm_risk.risk_score end) feb_risk
, max(case when date_part('month',assessment_date) = 3 then firm_risk.risk_score end) mar_risk
FROM firm_risk
WHERE date_part('month',assessment_date) in (1,2,3)
GROUP BY
firm_risk.firm_id_1
firm_id_1 | jan_risk | feb_risk | mar_risk
--------: | :------- | :------- | :-------
789 | 0.2 | null | 0.17
456 | 0.87 | 0.1 | 0.12
123 | 0.8 | 0.91 | 0.6
db<>fiddle here

How to merge two versioned tables?

There are two tables:
Table1(id, p1, p2, date_from, date_to):
(1, 'z', 55, '01.05.2010 12:30:20', '17.05.2010 13:10:14'),
(1, 'c', null, '17.05.2010 13:10:15', '18.01.2010 04:13:15'),
(1, 'c', 25, '18.01.2010 04:13:16', '01.01.9999 00:00:00');
Table2(id, p3, date_from, date_to):
(1, 15, '01.04.2010 12:30:20', '02.05.2010 13:10:14'),
(1, 35, '02.05.2010 13:10:15', '01.01.9999 00:00:00');
It is necessary that when merging 2 tables, there are fields containing the history of changes to each parameter. That is:
Select(id, p1, p2, p3, date_from, date_to):
(1, null, null, 10, '01.04.2010 12:30:20', '01.05.2010 12:30:19'),
(1, 'z', 55, 15, '01.05.2010 12:30:20', '02.05.2010 13:10:14'),
(1, 'z', 55, 35, '02.05.2010 13:10:15', '17.05.2010 13:10:14'),
(1, 'c', null, 35, '17.05.2010 13:10:15', '18.01.2010 04:13:15'),
(1, 'c', 25, 35, '18.01.2010 04:13:16', '01.01.9999 00:00:00');
Where records from two tables do not overlap in time(or part of time), null should be output .
So far I have done:
select
a.id,
case
when a.date_to between b.date_from and b.date_to
or a.date_from between b.date_from and b.date_to
then p1
end as p1,
case
when a.date_to between b.date_from and b.date_to
or a.date_from between b.date_from and b.date_to
then p2
end as p2,
p3,
a.date_from as af,
b.date_from as bf,
a.date_to as at,
b.date_to as bt
from
Table1 a
left join Table2 b
on
a.id=b.id
and (b.date_from between a.date_from and a.date_to
or a.date_from between b.date_from and b.date_to)
You can get your desired result by first computing all the ranges for each id value using a sequence of CTEs:
WITH dates_from AS (
SELECT id, date_from FROM Table1
UNION
SELECT id, date_to + interval '1 second' FROM Table1
UNION
SELECT id, date_from FROM Table2
UNION
SELECT id, date_to + interval '1 second' FROM Table2
),
dates_to AS (
SELECT id, date_to FROM Table1
UNION
SELECT id, date_from - interval '1 second' FROM Table1
UNION
SELECT id, date_to FROM Table2
UNION
SELECT id, date_from - interval '1 second' FROM Table2
),
ranges AS (
SELECT df.id, date_from, MIN(date_to) AS date_to
FROM dates_from df
JOIN dates_to dt ON dt.id = df.id AND dt.date_to > df.date_from
GROUP BY df.id, date_from
)
SELECT *
FROM ranges
For your sample data (with an adjustment to make the to date in row 2 of Table1 greater than the from date), this gives:
id date_from date_to
1 2010-05-17 13:10:15 2011-01-18 04:13:15
1 2010-05-01 12:30:20 2010-05-02 13:10:14
1 2010-04-01 12:30:20 2010-05-01 12:30:19
1 2010-05-02 13:10:15 2010-05-17 13:10:14
1 2011-01-18 04:13:16 9999-01-01 00:00:00
This table can then be LEFT JOINed to both tables (on overlapping ranges) to extract the appropriate p1, p2 and p3 values for each range:
SELECT r.id,
t1.p1, t1.p2, t2.p3,
r.date_from, r.date_to
FROM ranges r
LEFT JOIN Table1 t1 ON t1.id = r.id AND t1.date_from <= r.date_to and t1.date_to >= r.date_from
LEFT JOIN Table2 t2 ON t2.id = r.id AND t2.date_from <= r.date_to and t2.date_to >= r.date_from
ORDER BY r.id, r.date_from
Output:
id p1 p2 p3 date_from date_to
1 15 2010-04-01 12:30:20 2010-05-01 12:30:19
1 z 55 15 2010-05-01 12:30:20 2010-05-02 13:10:14
1 z 55 35 2010-05-02 13:10:15 2010-05-17 13:10:14
1 c 35 2010-05-17 13:10:15 2011-01-18 04:13:15
1 c 25 35 2011-01-18 04:13:16 9999-01-01 00:00:00
Demo (including values for a second id) on dbfiddle
Try this way :
select COALESCE(t1.id,t2.id) as id,
t1.p1,t1.p2,t2.p3,
COALESCE(t1.date_from,t2.date_from) as date_from,
COALESCE(t1.date_to,t2.date_to) as date_to
from Table1 t1
FULL OUTER JOIN Table2 t2 ON t1.id = t2.id and t1.date_from= t2.date_from and t1.date_to= t2.date_to;

How to Improve Performance of sql query

The Table
CREATE TABLE ABC(key number(5), orders number(5), cost number(5), dat date);
insert into ABC (key, orders, cost, dat) values (1, 3, 5, to_date('10-01-
2017', 'mm-dd-yyyy'));
insert into ABC (key, orders, cost,dat) values (1, 5, 2, to_date('02-10-
2017', 'mm-dd-yyyy'));
insert into ABC (key, orders, cost,dat) values (1, 6, 1, to_date('03-10-
2017', 'mm-dd-yyyy'));
insert into ABC (key, orders, cost,dat) values (1, 7, 2, to_date('05-10-
2017', 'mm-dd-yyyy'));
insert into ABC (key, orders, cost,dat) values (1, 8, 3, to_date('07-10-
2017', 'mm-dd-yyyy'));
insert into ABC (key, orders, cost,dat) values (1, 3, 4, to_date('08-10-
2017', 'mm-dd-yyyy'));
insert into ABC (key, orders, cost,dat) values (2, 3, 6, to_date('02-10-
2017', 'mm-dd-yyyy'));
insert into ABC (key, orders, cost,dat) values (2, 3, 9, to_date('01-10-
2017', 'mm-dd-yyyy'));
insert into ABC (key, orders, cost,dat) values (2, 2 ,5, to_date('03-10-
2017', 'mm-dd-yyyy'));
insert into ABC (key, orders, cost,dat) values (2, 3, 2, to_date('05-10-
2017', 'mm-dd-yyyy'));
insert into ABC (key, orders, cost,dat) values (2, 1, 1, to_date('06-10-
2017', 'mm-dd-yyyy'));
insert into ABC (key, orders, cost,dat) values (3, 4, 12, to_date('10-10-
2017', 'mm-dd-yyyy'));
insert into ABC (key, orders, cost,dat) values (3, 3, 9, to_date('01-10-
2017', 'mm-dd-yyyy'));
insert into ABC (key, orders, cost,dat) values (3, 2 ,5, to_date('05-10-
2017', 'mm-dd-yyyy'));
insert into ABC (key, orders, cost,dat) values (3, 3, 2, to_date('06-10-
2017', 'mm-dd-yyyy'));
insert into ABC (key, orders, cost,dat) values (3, 1, 1, to_date('07-10-
2017', 'mm-dd-yyyy'));
insert into ABC (key, orders, cost,dat) values (3, 4, 12, to_date('11-10-
2017', 'mm-dd-yyyy'));
insert into ABC (key, orders, cost, dat) values (1, 3, 5, to_date('10-01-
2017', 'mm-dd-yyyy'));
insert into ABC (key, orders, cost,dat) values (1, 5, 2, to_date('02-17-
2017', 'mm-dd-yyyy'));
insert into ABC (key, orders, cost,dat) values (1, 6, 1, to_date('03-18-
2017', 'mm-dd-yyyy'));
insert into ABC (key, orders, cost,dat) values (1, 7, 2, to_date('05-14-
2017', 'mm-dd-yyyy'));
insert into ABC (key, orders, cost,dat) values (1, 8, 3, to_date('07-13-
2017', 'mm-dd-yyyy'));
insert into ABC (key, orders, cost,dat) values (1, 3, 4, to_date('08-12-
2017', 'mm-dd-yyyy'));
insert into ABC (key, orders, cost,dat) values (2, 3, 6, to_date('02-11-
2017', 'mm-dd-yyyy'));
insert into ABC (key, orders, cost,dat) values (2, 3, 9, to_date('01-15-
2017', 'mm-dd-yyyy'));
insert into ABC (key, orders, cost,dat) values (2, 2 ,5, to_date('03-14-
2017', 'mm-dd-yyyy'));
insert into ABC (key, orders, cost,dat) values (2, 3, 2, to_date('05-18-
2017', 'mm-dd-yyyy'));
insert into ABC (key, orders, cost,dat) values (2, 1, 1, to_date('06-19-
2017', 'mm-dd-yyyy'));
insert into ABC (key, orders, cost,dat) values (3, 4, 12, to_date('10-11-
2017', 'mm-dd-yyyy'));
insert into ABC (key, orders, cost,dat) values (3, 3, 9, to_date('01-12-
2017', 'mm-dd-yyyy'));
insert into ABC (key, orders, cost,dat) values (3, 2 ,5, to_date('05-16-
2017', 'mm-dd-yyyy'));
insert into ABC (key, orders, cost,dat) values (3, 3, 2, to_date('06-17-
2017', 'mm-dd-yyyy'));
insert into ABC (key, orders, cost,dat) values (3, 1, 1, to_date('07-12-
2017', 'mm-dd-yyyy'));
insert into ABC (key, orders, cost,dat) values (3, 4, 12, to_date('12-11-
2017', 'mm-dd-yyyy'));
`
Now, The query
with
one as
(select t.key, sum(t.cost) as tot, sum(t.orders) as qty from table t
where t.date >= to_date('01-01-2017','mm-dd-yyyy') and
t.date < to_date('04-01-2017','mm-dd-yyyy')
group by t.key),
two as
(select t.key, sum(t.cost) as tot, sum(t.orders) as qty from table t
where t.date >= to_date('04-01-2017','mm-dd-yyyy') and
t.date < to_date('07-01-2017','mm-dd-yyyy')
group by t.key),
three as
(select t.key, sum(t.cost) as tot, sum(t.orders) as qty from table t
where t.date >= to_date('07-01-2017','mm-dd-yyyy') and
t.date < to_date('10-01-2017','mm-dd-yyyy')
group by t.key),
four as
(select t.key, sum(t.cost) as tot, sum(t.orders) as qty from table t
where t.date >= to_date('10-01-2017','mm-dd-yyyy') and
t.date < to_date('01-01-2018','mm-dd-yyyy')
group by t.key)
select o.key, o.tot, o.qty, s.tot, s.qty, t.tot, t.qty, f.tot,f.qty
from one o
left join two s on s.key = o.key
left join three t on t.key = o.key
left join four f on f.key = o.key
I want to know if I could improve the performance of this SQL Query?
If you notice the code, you can see that there is a 'Where' statement that is taking results from various dates groups. Most of the query is repetitive except the where clause.
Is there a way to optimize the query? In terms of performance and number of lines.
Expected Output Format: I can code the format part, but, That's the idea
Looks like you are trying to get quarterly totals/quantities. Could be simplified doing sum( case/when ).
SQL Fiddle Result
select
t.key,
sum( case when t.dat >= Tmp.Q1From and t.dat < Tmp.Q1End then t.cost else 0 end ) as Q1Tot,
sum( case when t.dat >= Tmp.Q1From and t.dat < Tmp.Q1End then t.orders else 0 end ) as Q1Qty,
sum( case when t.dat >= Tmp.Q1End and t.dat < Tmp.Q2End then t.cost else 0 end ) as Q2Tot,
sum( case when t.dat >= Tmp.Q1End and t.dat < Tmp.Q2End then t.orders else 0 end ) as Q2Qty,
sum( case when t.dat >= Tmp.Q2End and t.dat < Tmp.Q3End then t.cost else 0 end ) as Q3Tot,
sum( case when t.dat >= Tmp.Q2End and t.dat < Tmp.Q3End then t.orders else 0 end ) as Q3Qty,
sum( case when t.dat >= Tmp.Q3End and t.dat < Tmp.Q4End then t.cost else 0 end ) as Q4Tot,
sum( case when t.dat >= Tmp.Q3End and t.dat < Tmp.Q4End then t.orders else 0 end ) as Q4Qty
from
ABC t,
( select
to_date('01-01-2017', 'mm-dd-yyyy') Q1From,
to_date('04-01-2017', 'mm-dd-yyyy') Q1End,
to_date('07-01-2017', 'mm-dd-yyyy') Q2End,
to_date('10-01-2017', 'mm-dd-yyyy') Q3End,
to_date('01-01-2018', 'mm-dd-yyyy') Q4End
from
dual ) Tmp
where
t.dat >= to_date('01-01-2017', 'mm-dd-yyyy')
and t.dat < to_date('01-01-2018', 'mm-dd-yyyy')
group by
t.key
and definitely have an index on ( date, key )… The date field to optimize the WHERE clause, the KEY to optimize the group by
Doing all in one will prevent missing numbers such as an item not sold in first quarter but IS sold in second, third or fourth, it would otherwise be left out of the final results.

SQL - Start and End date based on another column

Simplified structure.
I need the two dates between a record that has an action type of 4 and an action type of 1.
The record could be in that state multiple times and I would need separate rows for their times
For example for IncidentId = 1
Row 1 - StartTime = 2017-01-01 14:00 (id:3) - End Time = 2017-01-01 20:00 (id: 5)
Row 2 - StartTime = 2017-01-01 21:00 (id:6) - End Time = 2017-01-02 11:00 (id: 9)
CREATE TABLE #returntable
(
[incidentid] INT,
[starttime] DATETIME,
[endtime] DATETIME
)
CREATE TABLE #testtableofdoom
(
[incidentlogid] INT,
[incidentid] INT,
[timestamp] DATETIME,
[actiontypeid] INT
)
INSERT INTO #testtableofdoom
( incidentlogid, incidentid, timestamp, actiontypeid )
VALUES ( 1, 1, '2017-01-01 09:00', 1 )
, ( 2, 1, '2017-01-01 11:00', 1 )
, ( 3, 1, '2017-01-01 14:00', 4 )
, ( 4, 1, '2017-01-01 16:00', 4 )
, ( 5, 1, '2017-01-01 20:00', 1 )
, ( 6, 1, '2017-01-01 21:00', 4 )
, ( 7, 1, '2017-01-02 09:00', 4 )
, ( 8, 2, '2017-01-02 10:00', 1 )
, ( 9, 1, '2017-01-02 11:00', 1 )
, ( 10, 1, '2017-01-02 14:00', 1 )
, ( 11, 2, '2017-01-02 15:00', 4 )
, ( 12, 1, '2017-01-02 16:00', 1 )
, ( 13, 1, '2017-01-02 17:00', 1 )
, ( 14, 1, '2017-01-02 18:00', 1 )
, ( 15, 2, '2017-01-02 15:00', 1 );
DROP TABLE #testtableofdoom
DROP TABLE #returntable
I used table variables instead of temp tables, and shorter column names than you, but this works:
declare #tt TABLE (
logId INT, iId INT,
dt DATETIME, atId INT
INSERT #tt (logId, iId,
dt, atId) values
(1, 1, '2017-01-01 09:00', 1),
(2, 1, '2017-01-01 11:00', 1),
(3, 1, '2017-01-01 14:00', 4),
(4, 1, '2017-01-01 16:00', 4),
(5, 1, '2017-01-01 20:00', 1),
(6, 1, '2017-01-01 21:00', 4),
(7, 1, '2017-01-02 09:00', 4),
(8, 2, '2017-01-02 10:00', 1),
(9, 1, '2017-01-02 11:00', 1),
(10, 1, '2017-01-02 14:00', 1),
(11, 2, '2017-01-02 15:00', 4),
(12, 1, '2017-01-02 16:00', 1),
(13, 1, '2017-01-02 17:00', 1),
(14, 1, '2017-01-02 18:00', 1),
(15, 2, '2017-01-02 15:00', 1)
Select s.logId startLogid, e.logId endLogId,
s.iID, s.dt startTime, e.dt endTime
from #tt s join #tt e
on e.logId =
(Select min(logId) from #tt
where iId = s.iID
and atId = 1
and logId > s.logId)
where s.aTid = 4
and ((Select atId from #tt
Where logId =
(Select Max(logId) from #tt
where logId < s.LogId
and iId = s.iId)) = 1
or Not Exists
(Select * from #tt
Where logId < s.LogId
and iId = s.iID))
This produces the following:
startLogid endLogId iID startTime endTime
----------- ----------- ---- ---------------- ----------------
3 5 1 2017-01-01 14:00 2017-01-01 20:00
6 9 1 2017-01-01 21:00 2017-01-02 11:00
11 15 2 2017-01-02 15:00 2017-01-02 15:00
it uses a self-join. s represents the first (start) record with actionType 4, and e represents end record with action type 1. Since logId increments, the end record must have higher logId than the start record, and it must be the lowest logId higher than the start records that has same iId and an atId = 1.
Select s.iID, s.dt startTime, e.dt endTime
from #tt s join #tt e
on e.logId =
(Select min(logId) from #tt -- lowest log greater than start logId
where iId = s.iID -- same iId
and atId = 1 -- with atId = 1
and logId > s.logId) -- greater than start logId
finally, the start record must be restricted to those "4" records which either have no other same incident records before it or have a "1" record immediately prior to it.
where s.aTid = 4
and ((Select atId from #tt -- atId of immed prior = 1
Where logId =
(Select Max(logId) from #tt
where logId < s.LogId
and iId = s.iId)) = 1
or Not Exists -- or there is no prior record
(Select * from #tt
Where logId < s.LogId
and iId = s.iID))
something like this?
select
d.[timestamp] as StartDate,
(select top 1 [timestamp]
from #testTableOfDoom d2
where d2.incidentid = 1 and d2.[timestamp] > d.[timestamp] and actiontypeid = 1
order by d2.[timestamp] asc
) as EndDate
from
(select
p.[timestamp],
LAG(p.actiontypeid) OVER (ORDER BY incidentlogid asc) PrevValue,
p.actiontypeid
from #testTableOfDoom p
where p.incidentid = 1) d
where d.actiontypeid = 4
and d.PrevValue <> 4