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

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.

Related

oracle sql get transactions between the period

I have 3 tables in oracle sql namely investor, share and transaction.
I am trying to get new investors invested in any shares for a certain period. As they are the new investor, there should not be a transaction in the transaction table for that investor against that share prior to the search period.
For the transaction table with the following records:
Id TranDt InvCode ShareCode
1 2020-01-01 00:00:00.000 inv1 S1
2 2019-04-01 00:00:00.000 inv1 S1
3 2020-04-01 00:00:00.000 inv1 S1
4 2021-03-06 11:50:20.560 inv2 S2
5 2020-04-01 00:00:00.000 inv3 S1
For the search period between 2020-01-01 and 2020-05-01, I should get the output as
5 2020-04-01 00:00:00.000 inv3 S1
Though there are transactions for inv1 in the table for that period, there is also a transaction prior to the search period, so that shouldn't be included as it's not considered as new investor within the search period.
Below query is working but it's really taking ages to return the results calling from c# code leading to timeout issues. Is there anything we can do to refine to get the results quicker?
WITH
INVESTORS AS
(
SELECT I.INVCODE FROM INVESTOR I WHERE I.CLOSED IS NULL)
),
SHARES AS
(
SELECT S.SHARECODE FROM SHARE S WHERE S.DORMANT IS NULL))
),
SHARES_IN_PERIOD AS
(
SELECT DISTINCT
T.INVCODE,
T.SHARECODE,
T.TYPE
FROM TRANSACTION T
JOIN INVESTORS I ON T.INVCODE = I.INVCODE
JOIN SHARES S ON T.SHARECODE = S.SHARECODE
WHERE T.TRANDT >= :startDate AND T.TRANDT <= :endDate
),
PREVIOUS_SHARES AS
(
SELECT DISTINCT
T.INVCODE,
T.SHARECODE,
T.TYPE
FROM TRANSACTION T
JOIN INVESTORS I ON T.INVCODE = I.INVCODE
JOIN SHARES S ON T.TRSTCODE = S.TRSTCODE
WHERE T.TRANDT < :startDate
)
SELECT
DISTINCT
SP.INVCODE AS InvestorCode,
SP.SHARECODE AS ShareCode,
SP.TYPE AS ShareType
FROM SHARES_IN_PERIOD SP
WHERE (SP.INVCODE, SP.SHARECODE, SP.TYPE) NOT IN
(
SELECT
PS.INVCODE,
PS.SHARECODE,
PS.TYPE
FROM PREVIOUS_SHARES PS
)
With the suggestion given by #Gordon Linoff, I tried following options (for all the shares I need) but they are taking long time too. Transaction table is over 32 million rows.
1.
WITH
SHARES AS
(
SELECT S.SHARECODE FROM SHARE S WHERE S.DORMANT IS NULL))
)
select t.invcode, t.sharecode, t.type
from (select t.*,
row_number() over (partition by invcode, sharecode, type order by trandt)
as seqnum
from transactions t
) t
join shares s on s.sharecode = t.sharecode
where seqnum = 1 and
t.trandt >= date '2020-01-01' and
t.trandt < date '2020-05-01';
WITH
INVESTORS AS
(
SELECT I.INVCODE FROM INVESTOR I WHERE I.CLOSED IS NULL)
),
SHARES AS
(
SELECT S.SHARECODE FROM SHARE S WHERE S.DORMANT IS NULL))
)
select t.invcode, t.sharecode, t.type
from (select t.*,
row_number() over (partition by invcode, sharecode, type order by trandt)
as seqnum
from transactions t
) t
join investors i on i.invcode = t.invcode
join shares s on s.sharecode = t.sharecode
where seqnum = 1 and
t.trandt >= date '2020-01-01' and
t.trandt < date '2020-05-01';
select t.invcode, t.sharecode, t.type
from (select t.*,
row_number() over (partition by invcode, sharecode, type order by trandt)
as seqnum
from transactions t
) t
where seqnum = 1 and
t.sharecode IN (SELECT S.SHARECODE FROM SHARE S WHERE S.DORMANT IS NULL)))
and
t.trandt >= date '2020-01-01' and
t.trandt < date '2020-05-01';
If you want to know if the first record in transactions for a share is during a period, you can use window functions:
select t.*
from (select t.*,
row_number() over (partition by invcode, sharecode order by trandt) as seqnum
from transactions t
) t
where seqnum = 1 and
t.sharecode = :sharecode and
t.trandt >= date '2020-01-01' and
t.trandt < date '2020-05-01';
For performance for this code, you want an index on transactions(invcode, sharecode, trandate).

How to group data with conditions?

I'm trying to turn my current table using sql
customer.id sale_date
15 1/12/2017
15 2/12/2017
15 7/12/2017
12 6/09/2017
12 12/09/2017
16 8/14/2017
13 6/01/2017
13 7/01/2017
into something like this.
sale_date1 is the first order date.
sale_date2 is any order date one month after sale_date1.
sale_date3 is any order date five months after sale_date1.
customer.id sale_date1 sale_date2 sale_date3(at least 5 months after sale_date1)
15 1/12/2017 2/12/2017 7/12/2017
12 6/07/2017 NULL 12/09/2017
16 8/14/2017 NULL NULL
13 6/01/2017 7/01/2017 NULL
One option here would be to use correlated sub-queries to populate each of the three columns:
WITH cte AS (
SELECT [customer.id], MIN(sale_date) AS min_sale_date
FROM yourTable
GROUP BY [customer.id]
)
SELECT
[customer.id],
min_sale_date AS sale_date1,
(SELECT MIN(t2.sale_date) FROM yourTable t2
WHERE t1.[customer.id] = t2.[customer.id] AND
t2.sale_date >= DATEADD(month, 1, t1.min_sale_date) AND
t2.sale_date < DATEADD(month, 5, t1.min_sale_date)) AS sale_date2,
(SELECT MIN(t2.sale_date) FROM yourTable t2
WHERE t1.[customer.id] = t2.[customer.id] AND
t2.sale_date >= DATEADD(month, 5, t1.min_sale_date)) AS sale_date3
FROM cte t1
ORDER BY [customer.id];
Demo
Try below using row_number() and conditional aggregation
select customerid,max(case when seq=1 then sale_date end) as date1,
max(case when seq=2 then sale_date end) as date2,
max(case when seq=3 then sale_date end) as date3
from
(
select *, row_number() over(partition by customerid order by sale_date) as seq
from tablename
)X
group by customerid
I THINK THIS IS WHAT YOU WANT
SELECT A.customer.id, SALES1.sale_date , SALES2.sale_date ,SALES3.sale_date , SALES4.sale_date,SALES5.sale_date from
(SELECT distinct customer.id ,
From yourTable)A
LEFT JOIN
(SELECT * from
(SELECT customer.id, sale_date
ROW_NUMBER() OVER(ORDER BY sale_date ASC)
AS R1,
name, recovery_model_desc
FROM yourTable)S1 where R1=1)SALES1
A.customer.id = SALES1.customer.id
LEFT JOIN
(SELECT * from
(SELECT customer.id, sale_date
ROW_NUMBER() OVER(ORDER BY sale_date ASC)
AS R1,
name, recovery_model_desc
FROM yourTable)S1 where R1=2)SALES2
A.customer.id = SALES1.customer.id
LEFT JOIN
(SELECT * from
(SELECT customer.id, sale_date
ROW_NUMBER() OVER(ORDER BY sale_date ASC)
AS R1,
name, recovery_model_desc
FROM yourTable)S1 where R1=3)SALES3
A.customer.id = SALES1.customer.id
LEFT JOIN
(SELECT * from
(SELECT customer.id, sale_date
ROW_NUMBER() OVER(ORDER BY sale_date ASC)
AS R1,
name, recovery_model_desc
FROM yourTable)S1 where R1=2)SALES2
A.customer.id = SALES1.customer.id
LEFT JOIN
(SELECT * from
(SELECT customer.id, sale_date
ROW_NUMBER() OVER(ORDER BY sale_date ASC)
AS R1,
name, recovery_model_desc
FROM yourTable)S1 where R1=4)SALES4
A.customer.id = SALES1.customer.id
LEFT JOIN
(SELECT * from
(SELECT customer.id, sale_date
ROW_NUMBER() OVER(ORDER BY sale_date ASC)
AS R1,
name, recovery_model_desc
FROM yourTable)S1 where R1=2)SALES2
A.customer.id = SALES1.customer.id
LEFT JOIN
(SELECT * from
(SELECT customer.id, sale_date
ROW_NUMBER() OVER(ORDER BY sale_date ASC)
AS R1,
name, recovery_model_desc
FROM yourTable)S1 where R1=5)SALES5
A.customer.id = SALES1.customer.id
try this:
with mindate as (
select id, min(sale_date) MinDate,
DATEADD(month, 1, min(sale_date)) MinDatePlus1Month,
DATEADD(month, 5, min(sale_date)) MinDatePlus2Month
from yourtable
group by id
)
select f1.id, f1.MinDate sale_date1, f2.sale_date sale_date2, f3.sale_date sale_date3
from mindate f1
left outer join yourtable f2 on f1.id=f2.id and f1.MinDatePlus1Month=f2.sale_date
left outer join yourtable f3 on f1.id=f3.id and f1.MinDatePlus2Month=f3.sale_date

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.

TERADATA: query optimization

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 ;

Using T-SQL, how do I generate a result that shows a range of dates

Using SQL Server, how do I generate a result set that shows a range of dates, like so:
StartDate EndDate
01/01/2014 01/04/2014
01/08/2014 01/11/2014
01/14/2014 01/15/2014
The original data had the dates in this format:
ColumnA DateColumn
blah 01/01/2014
blah 01/02/2014
blah 01/03/2014
blah 01/04/2014
blah 01/08/2014
blah 01/09/2014
blah 01/10/2014
blah 01/11/2014
blah 01/14/2014
blah 01/15/2014
Currently, I have a bunch of queries that does this, but I'm wondering if I can do something in less code:
SELECT ROW_NUMBER() OVER(ORDER BY DateColumn) AS rownum,
DateColumn
INTO #main
FROM MyTable
SELECT m1.DateColumn AS TBegin,
m2.DateColumn AS TEnd,
COALESCE(DATEDIFF(day, m2.TimePk, m1.TimePk), 0) AS Gap
INTO #Gap
FROM #main m1
LEFT OUTER JOIN #main m2
ON m1.rownum = m2.rownum + 1
ORDER BY m1.DateColumn
SELECT ROW_NUMBER() OVER(ORDER BY i_id, TBegin) AS rownum,
TBegin
INTO #Begin
FROM #Gap
WHERE Gap <> 1
ORDER BY TBegin
SELECT ROW_NUMBER() OVER(ORDER BY i_id, TEnd) AS rownum,
TEnd
INTO #End
FROM (
SELECT TEnd
FROM #Gap
WHERE Gap > 1
UNION
SELECT MAX(TBegin)
FROM #Gap
) as t
ORDER BY TEnd
SELECT b.TBegin,
e.TEnd
FROM #Begin b
INNER JOIN #End e
ON b.i_id = e.i_id
AND b.rownum = e.rownum
ORDER BY b.TBegin
Any ideas on how to simplify or approach this in an entirely different way?
My approach to these is to identify the first date that has no date preceding it. This is the beginning of a group. Then I take the cumulative sum of that as a group identifier, and do the aggregation.
SQL Server 2008 doesn't have lag or cumulative sums, so I use correlated subqueries for this:
with mt as (
select t.*,
(case when (select top 1 t2.dateColumn
from MyTable t2
where t2.ColumnA = t.ColumnA and
t2.dateColumn < t.dateColumn
order by t2.dateColumn desc
) = dateadd(day, -1, t.datecolumn)
then 0
else 1
end) as IsStart
from MyTable t
),
mtcum as (
select mt.*,
(select sum(mt2.IsStart)
from mt mt2
where mt2.ColumnA = mt.ColumnA and
mt2.dateColumn <= mt.DateColumn
) as grpId
from mt
)
select ColumnA, min(dateColumn) as StartDate, max(dateColumn) as EndDate
from mtcum
group by ColumnA, grpId;
EDIT:
An easier way of approach this is with the observation that the difference between a sequence of dates and a sequence of numbers is constant.
select columnA, min(dateColumn) as StartDate, max(dateColumn) as EndDate
from (select mt.*, row_number() over (partition by ColumnA order by datecolumn) as seqnum
from mytable mt
) t
group by columnA, dateadd(day, - seqnum, datecolumn);
This will work for you. It's still fairly complex though. It uses inner queries to find the first date that is after a gap for each date. This way all days belonging to the same group of dates can be grouped together.
select MIN(DateColumn) StartDate, MAX(DateColumn) EndDate from
(select X.DateColumn, MIN(Y.DateColumn) MinOverGap from
(select DateColumn, ROW_NUMBER() OVER (ORDER BY DateColumn) RowNumber
from MyTable) X
left join
(select DateColumn, ROW_NUMBER() OVER (ORDER BY DateColumn) RowNumber
from MyTable) Y
on DATEADD(d, Y.RowNumber - 1, X.DateColumn) <> DATEADD(d, X.RowNumber -1, Y.DateColumn) AND X.DateColumn < Y.DateColumn
group by x.DateColumn) grouped
group by MinOverGap
order by 1