TERADATA: query optimization - sql

This query is working but it seems to take longer time than usual to retrieve the data. Is there a better solution to optimize this query? I need to get all PRD_ID from T1 and T2 even if there is no match with S1 and S2.
SELECT DISTINCT T.PRD_ID T.AMOUNT, T.DATE, T.REGION
FROM
(
SELECT DISTINCT T1.PRD_ID, T1.PRD_CODE, S1.ORDER_DATE AS DATE, T1.REGION
FROM
(
(SELECT PRD_ID, PRD_CODE,AMOUNT,REGION
FROM PRODUCT
WHERE REGION='CA') T1
LEFT JOIN SERVICE_1 S1
ON S1.PRD_ID = T1.PRD_ID
AND S1.PRD_CODE= T1.PRD_CODE
AND S1.AMT = T1.AMOUNT
AND S1.ORDER_DATE >= '01/01/2015'
AND S1.ORDER_DATE <= '02/28/2015'
)
UNION ALL
SELECT DISTINCT T2.PRD_ID, T2.PRD_CODE, S2.ACCT_CALENDAR_DT AS DATE, T2.REGION
FROM
(
(SELECT PRD_ID, PRD_CODE,AMOUNT,REGION
FROM PRODUCT
WHERE REGION='IL') T2
LEFT JOIN SERVICE_2 S2
ON S2.PRD_ID = T2.PRD_ID
AND S2.PRD_CODE= T2.PRD_CODE
AND S2.AMT = T2.AMOUNT
AND S2.ACCT_CALENDAR_DT >= '20150101'
AND S2.ACCT_CALENDAR_DT <= '20150228'
)
) T
ORDER BY REGION, ORDER_DATE DESC, PRD_ID

I can't see why you need all these (3!) levels of nested tables. The following should be equivalent:
SELECT DISTINCT
T1.PRD_ID, T1.PRD_CODE, S1.ORDER_DATE AS DATE, T1.REGION
FROM
PRODUCT T1
LEFT JOIN SERVICE_1 S1
ON S1.PRD_ID = T1.PRD_ID
AND S1.PRD_CODE= T1.PRD_CODE
AND S1.AMT = T1.AMOUNT
AND S1.ORDER_DATE >= DATE '2015-01-01' -- converted '01/01/2015'
AND S1.ORDER_DATE <= DATE '2015-02-28' -- converted '02/28/2015'
WHERE T1.REGION = 'CA'
UNION ALL -- No need for DISTINCT here. The Region
-- is different between the 2 parts.
SELECT DISTINCT
T2.PRD_ID, T2.PRD_CODE, S2.ACCT_CALENDAR_DT AS DATE, T2.REGION
FROM
PRODUCT T2
LEFT JOIN SERVICE_2 S2
ON S2.PRD_ID = T2.PRD_ID
AND S2.PRD_CODE= T2.PRD_CODE
AND S2.AMT = T2.AMOUNT
AND S2.ACCT_CALENDAR_DT >= DATE '2015-01-01'
AND S2.ACCT_CALENDAR_DT <= DATE '2015-02-28'
WHERE T2.REGION = 'IL'
ORDER BY REGION, DATE DESC, PRD_ID ;
or:
SELECT DISTINCT
T1.PRD_ID, T1.PRD_CODE, S1.ORDER_DATE AS DATE, 'CA' AS REGION
FROM
( SELECT PRD_ID, PRD_CODE, AMOUNT
FROM PRODUCT
WHERE REGION = 'CA'
) T1
LEFT JOIN SERVICE_1 S1
ON S1.PRD_ID = T1.PRD_ID
AND S1.PRD_CODE= T1.PRD_CODE
AND S1.AMT = T1.AMOUNT
AND S1.ORDER_DATE >= DATE '2015-01-01'
AND S1.ORDER_DATE <= DATE '2015-02-28'
UNION ALL
SELECT DISTINCT
T2.PRD_ID, T2.PRD_CODE, S2.ACCT_CALENDAR_DT AS DATE, 'IL' AS REGION
FROM
( SELECT PRD_ID, PRD_CODE, AMOUNT
FROM PRODUCT
WHERE REGION = 'IL'
) T2
LEFT JOIN SERVICE_2 S2
ON S2.PRD_ID = T2.PRD_ID
AND S2.PRD_CODE= T2.PRD_CODE
AND S2.AMT = T2.AMOUNT
AND S2.ACCT_CALENDAR_DT >= DATE '2015-01-01'
AND S2.ACCT_CALENDAR_DT <= DATE '2015-02-28'
ORDER BY REGION, DATE DESC, PRD_ID ;

Related

Find the sum of amount for particular transasaction type for 2 consecutive days

I have two tables 1. Transactions and 2. Transaction type
e.g. of transaction table with dummy data
account_key Transaction_key date amount
1 11 03/22/0219 5000
1 12 03/23/2019 6000
1 13 03/22/2019 4000
1 14 03/23/2019 3000
e.g. of Transaction_type table with dummy data
transaction_key transaction_type
11 in
12 in
13 out
14 out
I have to find the ratio of sum of amount for 2 consecutive days of similar transaction type for the same account key. for eg (5000+6000)/(4000+3000)
the database is oracle and datatype is datetime
This is what I have tried
select t1.account_key,
trunc(t1.date),
sum(t1.amount) as in,
sum(t2.amount) as out
from transactions t1
inner join transactions t2 on t1.accountkey=t2.accountkey
where t1.date between '01-jun-2017' and '30-nov-2017'
and t2.date between '01-jun-2017' and '30-nov-2017'
and t1.transaction_key in (select a.transaction_key
from transaction_type a
where a.transaction type in 'in')
and t2.transaction_key in (select b.transaction_key
from transaction_type b
where b.transaction type in 'out')
group by t1.account_key,
trunc(t1.date)
having max(trunc(t1.date))-min(trunc(t1.date)) = 1
and max(trunc(t2.date))-min(trunc(t2.date)) = 1
You can use a WITH clause, also called a "common table expression" or "CTE" to break your problem down into manageable chunks.
Here is one way to do that:
WITH txn_by_date AS (
SELECT t.account_key,
t.txn_date,
tt.transaction_type,
sum(t.amount) amount
FROM txns t
INNER JOIN txn_types tt on tt.transaction_key = t.transaction_key
GROUP BY t.account_key, t.txn_date, tt.transaction_type )
SELECT tin.account_key,
tin.txn_date,
tin.amount amount_in,
tout.txn_date,
tout.amount amount_out,
tin.amount / tout.amount ratio
FROM txn_by_date tin
INNER JOIN txn_by_date tout ON tout.txn_date = tin.txn_date + 1
AND tout.transaction_type = 'out'
AND tout.account_key = tin.account_key
WHERE tin.transaction_type = 'in';
The CTE first computes the total txn amount by account, transaction type, and day. Once you have that, the main SELECT gets each 'in' total, joins it with the 'out' total from the consecutive day, and the computes the ratio.
Here is a full example, with test data (also expressed using the WITH clause):
with txns (account_key, Transaction_key, txn_date, amount) AS
(
SELECT 1, 11, TO_DATE('03/22/2019','MM/DD/YYYY'), 5000 FROM DUAL UNION ALL
SELECT 1, 12, TO_DATE('03/23/2019','MM/DD/YYYY'), 6000 FROM DUAL UNION ALL
SELECT 1, 13, TO_DATE('03/22/2019','MM/DD/YYYY'), 4000 FROM DUAL UNION ALL
SELECT 1, 14, TO_DATE('03/23/2019','MM/DD/YYYY'), 3000 FROM DUAL ),
txn_types ( transaction_key, transaction_type ) AS
(
SELECT 11, 'in' FROM DUAL UNION ALL
SELECT 12, 'out' FROM DUAL UNION ALL
SELECT 13, 'in' FROM DUAL UNION ALL
SELECT 14, 'out' FROM DUAL ),
txn_by_date AS (
SELECT t.account_key,
t.txn_date,
tt.transaction_type,
sum(t.amount) amount
FROM txns t
INNER JOIN txn_types tt on tt.transaction_key = t.transaction_key
GROUP BY t.account_key, t.txn_date, tt.transaction_type )
SELECT tin.account_key,
tin.txn_date,
tin.amount amount_in,
tout.txn_date,
tout.amount amount_out,
tin.amount / tout.amount ratio
FROM txn_by_date tin
INNER JOIN txn_by_date tout ON tout.txn_date = tin.txn_date + 1
AND tout.transaction_type = 'out'
AND tout.account_key = tin.account_key
WHERE tin.transaction_type = 'in';
+-------------+-----------+-----------+------------+------------+-------+
| ACCOUNT_KEY | TXN_DATE | AMOUNT_IN | TXN_DATE_1 | AMOUNT_OUT | RATIO |
+-------------+-----------+-----------+------------+------------+-------+
| 1 | 22-MAR-19 | 9000 | 23-MAR-19 | 9000 | 1 |
+-------------+-----------+-----------+------------+------------+-------+
ALTERNATE VERSION, FOR ANCIENT VERSIONS OF ORACLE
For really old versions of Oracle, you may need to avoid both the WITH clause and ANSI-style joins. Here is the above query rewritten to avoid those features.
SELECT tin.account_key,
tin.txn_date,
tin.amount amount_in,
tout.txn_date,
tout.amount amount_out,
tin.amount / tout.amount ratio
FROM ( SELECT t.account_key,
t.txn_date,
tt.transaction_type,
sum(t.amount) amount
FROM txns t,
txn_types tt
WHERE tt.transaction_key = t.transaction_key
GROUP BY t.account_key, t.txn_date, tt.transaction_type ) tin,
( SELECT t.account_key,
t.txn_date,
tt.transaction_type,
sum(t.amount) amount
FROM txns t,
txn_types tt
WHERE tt.transaction_key = t.transaction_key
GROUP BY t.account_key, t.txn_date, tt.transaction_type ) tout
WHERE tin.transaction_type = 'in'
AND tout.transaction_type(+) = 'out'
AND tout.account_key(+) = tin.account_key;
I would try to use to Temporary Tables. Store the sums of each in Transaction type in two temp tables, then use them to calculate your ratio. I have not tested this query but this is basically what you are looking for:
CREATE PRIVATE TEMPORARY TABLE as In_TempTable
Select T2.Transaction_Type, sum(T1.amount) In_Total
from Transactions T1
left join Transaction_Type T2 on T2.Transaction_key = T1.Transaction_Key
where T1.date between '01-jun-2017' and '30-nov-2017'
AND T2.Transactio_Type = 'In'
group by T2.Transaction_Type;
CREATE PRIVATE TEMPORARY TABLE as Out_TempTable
Select T2.Transaction_Type, sum(T1.amount) Out_Total
from Transactions T1
left join Transaction_Type T2 on T2.Transaction_key = T1.Transaction_Key
where T1.date between '01-jun-2017' and '30-nov-2017'
AND T2.Transactio_Type = 'Out'
group by T2.Transaction_Type;
Select Sum(a.In_Total)/Sum(b.Out_Total)
from In_TempTable a
Full Outer Join Out_TempTable b on b.Transaction_Type = a.Transaction_Type
I would personally change this:
T1.date between '01-jun-2017' and '30-nov-2017'
To something like this
T1.date between startdate and startdate+2
Depending on your needs of course and you will have to declare your startdate

Find minimum and maximum with where clause inside a query- SQL DB2

I am trying to get minimum and maximum date for area 12. I do not know how to use where clause inside a query.
SELECT A1.*, Ticket_ID,
min(StartDate) OVER(PARTITION BY Ticket_ID) AS Min_StartDate,
MAX(EndDate) OVER(PARTITION BY Ticket_ID) AS Max_EndDate
WHERE area = '12'
FROM (SELECT T1.Ticket_ID, T1.StartDate,T1.StartDate, t2.title, t1.area,T2.Ticket_TYPE
FROM Ticket1 t1
LEFT JOIN Ticket2 t2 on t1.Ticket_ID = t2.Ticket_ID
) A1
WHERE Ticket_TYPE = 1 AND StartDate <= '9/30/2017'
AND (EndDate >= '10/01/2017' OR EndDate IS NULL)
ORDER BY Ticket_ID
Try to run it as:
SELECT A1.*, A1.Ticket_ID,
min(StartDate) OVER(PARTITION BY Ticket_ID) AS Min_StartDate,
MAX(EndDate) OVER(PARTITION BY Ticket_ID) AS Max_EndDate
FROM (SELECT T1.Ticket_ID, T1.StartDate,T1.EndDate, t2.title, t1.area,T2.Ticket_TYPE
FROM Ticket1 t1
LEFT JOIN Ticket2 t2 on t1.Ticket_ID = t2.Ticket_ID
WHERE t2.Ticket_TYPE = 1 AND t1.StartDate <= '9/30/2017'
AND (t1.EndDate >= '10/01/2017' OR t1.EndDate IS NULL)
AND t1.area = '12'
) A1
ORDER BY A1.Ticket_ID
*You have a typo of specifying StartDate twice, corrected as EndDate in subquery.

Need to find out date when account become

If summ is positive + that means account own money, if record has negative - that means account has a payment.
I need to find out which account don't own any money on today date and I have this query :
SELECT a.Customer
,a.Deal
,(a.positive + b.negative) AS own_to_the_bank
FROM (
SELECT SUM(Summ) AS positive
,Customer
,Deal
FROM #test
WHERE Summ > 0
GROUP BY Customer
,Deal
) AS a
JOIN (
SELECT SUM(Summ) AS negative
,Customer
,Deal
FROM #test
WHERE Summ < 0
GROUP BY Customer
,Deal
) AS b ON a.Customer = b.Customer
AND a.Deal = b.Deal
WHERE a.positive + b.negative >0
and its working fine so now I have to find-out when account stops owning any money to the bank when a.positive + b.negative = 0 from my query.
stuck with this problem for few hours, any help?
I started with creating the balance per day, customer, deal and currency
SELECT t1.Customer, t1.Deal, t1.Currency, t1.Date, Balance = (SELECT SUM(Summ) FROM #test as hist WHERE hist.Customer = t1.Customer and hist.Deal = t1.Deal and hist.Currency = t1.Currency and hist.Date <= t1.Date)
FROM #test as t1
Added condition for positive balance and a rownum (ordered by date)
SELECT Customer, Deal, Currency, Date, Balance, RowNum = ROW_NUMBER() OVER(PARTITION BY Customer, Deal, Currency ORDER BY Date)
FROM
(
select t1.Customer, t1.Deal, t1.Currency, t1.Date, Balance = (SELECT SUM(Summ) FROM #test as hist WHERE hist.Customer = t1.Customer and hist.Deal = t1.Deal and hist.Currency = t1.Currency and hist.Date <= t1.Date)
FROM #test as t1
) as inn
WHERE Balance > 0
At last picked the first one.
SELECT Customer, Deal, Currency, Date, Balance
FROM ( SELECT Customer, Deal, Currency, Date, Balance, RowNum = ROW_NUMBER() OVER(PARTITION BY Customer, Deal, Currency ORDER BY Date)
FROM
(
SELECT t1.Customer, t1.Deal, t1.Currency, t1.Date, Balance = (SELECT SUM(Summ) FROM #test as hist WHERE hist.Customer = t1.Customer and hist.Deal = t1.Deal and hist.Currency = t1.Currency and hist.Date <= t1.Date)
FROM #test as t1
) as t
WHERE Balance > 0 ) as t2
WHERE t2.RowNum = 1
You may have several dates for a customer when he stopped owing
for example here we have two dates:
+1000
+500
-500
+500
-500
This query shows the last one:
select distinct a.customer, a.date
from test as a
left join test as b
on a.Date > b.Date and a.Customer = b.Customer
where a.summ < 0 and b.summ > 0
group by a.customer order by a.date, b.date desc
The clue is ordering joined tables in different directions by date and then taking just the first line per customer.

Selecting the Max Date From Multiple Tables

any help would be so incredibly appreciated. I am trying to select the last activity date from a group of tables. The tables include Entry Date, Note date, payment date, and Claim Date. I would like to return only the max value from all these dates. Furthermore I only want records where there has been no activity for over 45 days. I am currently using the following SQL to bring all the dates in then using calculated fields in EXCEL to figure the rest out. Is it possible to do this all with SQL?
Thanks in advance.
SELECT xrxTrnLgr.PatId, xrxTrnLgr.Balance,
Max(xrxPatNotes.NoteDate) AS 'Max of NoteDate',
Max(xrxTrnIcf.PostDate) AS 'Max of IcfPostDate',
Max(xrxPat.EntryDate) AS 'Entry Date',
Max(xrxPat.Coverage) AS 'Coverage',
Max(xrxTrnPay.PostDate) AS 'Last Payment'
FROM xrxTrnLgr
LEFT OUTER JOIN xrxPatNotes ON xrxTrnLgr.PatId = xrxPatNotes.PatId
LEFT OUTER JOIN xrxTrnIcf ON xrxTrnLgr.PatId = xrxTrnIcf.PatId
LEFT OUTER JOIN xrxPat ON xrxTrnLgr.PatId = xrxPat.PatId
LEFT OUTER JOIN xrxTrnPay ON xrxTrnLgr.PatId = xrxTrnPay.PatId
GROUP BY xrxTrnLgr.PatId, xrxTrnLgr.Balance
HAVING (xrxTrnLgr.Balance>$.01)
I think this might do it all in SQL:
select t.patid, t.balance,
max(case when which = 'note' then thedate end) as note,
max(case when which = 'post' then thedate end) as post,
max(case when which = 'entry' then thedate end) as entry,
max(case when which = 'coverage' then thedate end) as coverage,
max(case when which = 'lastPayment' then thedate end) as lastPayment
from xrxTrnLgr t left join
((select patid, notedate as thedate, 'note' as which
from xrxPatNotes
) union all
(select patid, postdate, 'post'
from xrxtrnIcf
) union all
(select patid, EntryDate, 'entry'
from xrxPat
) union all
(select paid, Coverage, 'coverage'
from xrxPat.Coverage
) union all
(select patid, PostDate, 'LastPayment'
from xrxTrnPay.PostDate
)
) d
on t.patid = d.patid
group by t.patid, t.balance
having min(now() - thedate) >= 45

Converting a correlated sub-query to JOIN

I have a correlated subquery which is creating performance issues. Being a correlated subquery it doesn't accept index hints either. So I am trying to convert it into a JOIN instead. Please find below the original query and the modified query. The modified query doesn't return any row but the original returns me 224 rows.
Appreciate any insight on what is wrong with my JOIN query, if it even makes sense to use JOIN instead of subquery in this case. Thanks.
select Area_CODE,
due_DATE,
RATE,
from SCHED S
where (s.Area_CODE = 11001 and
(s.COMP_CODE = 'a'
or
(s.COMPANY_CODE = 'b'
and s.due_DATE <
(
select
nvl( min(s1.due_DATE), to_date ( '31-DEC-2999', 'DD-MM-YYYY') )
from SCHED s1
where s1.AREA_CODE = s.AREA_CODE
and s1.COMP_CODE = 'c'
)
)
)
)
order by a.EFF_DATE asc, s.due_DATE asc
Modified Query:
SELECT
Area_CODE,
due_DATE,
RATE
from SCHED S
LEFT JOIN
(
SELECT
NVL( MIN(s1.due_DATE), to_date ( '31-DEC-2999', 'DD-MM-YYYY') ) AS
min_date,
s1.AREA_CODE AS a_code
FROM
SCHED s1
WHERE
s1.COMPANY_CODE = 'c'
GROUP BY
s1.AREA_code
)
s2
ON
s2.A_CODE = s.area_code
WHERE
(
s.area_code = 11001
AND
(
s.COMP_CODE = 'a'
OR
(
s.COMP_CODE = 'b'
and s.due_DATE < s2.min_date
)
)
)
order by s.EFF_DATE asc, s.due_DATE asc
The difference is where there are no dates for a given area code
Change the left join (s2) to an inner join on
inner JOIN
(
SELECT
min(s1.due_DATE) AS min_date,
s1.AREA_CODE AS a_code
FROM
(
select Area_Code, due_date from SCHED WHERE COMPANY_CODE = 'c'
union
select Area_Code, to_date('31-dec-2999','DD-MM-YYYY') from SCHED
) s1
GROUP BY
s1.AREA_code
) s2