Rank on each group without using derived table - sql

AccountNo Account Name Transaction time Transaction Amount
A01 Name 1 01-01-2016 04:00:00 5000
A01 Name 1 01-01-2016 07:30:00 3500
A01 Name 1 01-01-2016 09:30:00 3500
A01 Name 1 01-01-2016 12:00:00 6500
A01 Name 1 02-02-2016 06:00:00 4000
A01 Name 1 01-02-2016 08:30:00 8000
A01 Name 1 01-02-2016 09:30:00 8000
A02 Name 2 05-01-2016 04:00:00 2000
A02 Name 2 05-01-2016 07:30:00 8500
A02 Name 2 08-02-2016 06:00:00 1000
A02 Name 2 09-02-2016 08:30:00 9000
I need a query without using derived table to get the records which has latest transactions for each account. Query should support Oracle or TERADATA.
Using Derived table I am able to solve it as below:
SELECT a.accountno,
a.account_name,
a.transaction_time,
a.transaction_amount
FROM acct_details a,
(SELECT accountno,
Max(transaction_time) AS Transaction_time
FROM acct_details
GROUP BY accountno) b
WHERE a.accountno = b.accountno
AND a.transaction_time = b.transaction_time;
Thanks for Help!!

Why without Derived Table?
In Teradata there's QUALIFY, but it's no Standard SQL:
SELECT *
FROM acct_details
QUALIFY RANK() OVER (PARTITION BY accountno
ORDER BY transaction_time DESC) = 1;
In Oracle you need a Derived Table:
SELECT a.accountno,
a.account_name,
a.transaction_time,
a.transaction_amount,
FROM
(
SELECT a.accountno,
a.account_name,
a.transaction_time,
a.transaction_amount,
RANK() OVER (PARTITION BY accountno
ORDER BY transaction_time DESC) as rnk
FROM acct_details a
) a
WHERE rnk = 1
You might also try Oracle's MAX/KEEP:
SELECT a.accountno,
MAX (a.account_name) KEEP (DENSE_RANK FIRST ORDER BY a.transaction_time desc) ,
MAX(a.transaction_time),
MAX (a.transaction_amount) KEEP (DENSE_RANK FIRST ORDER BY a.transaction_time desc) FROM acct_details a
GROUP BY a.accountno

You can do it using an inline view like so:
SELECT a.accountno,
a.account_name,
a.transaction_time,
a.transaction_amount
FROM (SELECT accountno,
account_name,
transaction_time,
transaction_amount,
Max(transaction_time) OVER (PARTITION BY accountno) AS max_transaction_time
FROM acct_details) a
WHERE a.transaction_time = a.max_transaction_time;
This will work on Oracle and I think it should also work on Teradata.
I assume here that when you say you want to avoid a derived table, you mean you want to avoid joining the table to itself.

try this query:
SELECT [Transaction time],[Account Name],
lead([Transaction time ],1,1) OVER (ORDER BY [Account Name])
FROM [dbo].[acct_details]
this should help you

Related

SQL AWS Athena Group by Without a Column

I have this dataset
patient_id doctor_id status created_at
1 1 A 2020-10-01 10:00:00
1 1 P 2020-10-01 10:30:00
1 1 U 2020-10-01 10:35:00
1 2 A 2020-10-01 10:40:00
...
I want to group it by patient_id and doctor_id but without the status is grouped so the result will be like this
patient_id doctor_id status created_at
1 1 U 2020-10-01 10:35:00
1 2 A 2020-10-01 10:40:00
...
AWS Athena have to grouped all column but I need the last status
In Athena/Presto you can do this with the max_by function:
SELECT
patient_id,
doctor_id,
MAX_BY(status, created_at) AS last_status
FROM the_table
GROUP BY 1, 2
max_by(x, y) function returns the value of the column x for the row with the max value of column y of the group.
ROW_NUMBER provides one option here:
WITH cte AS (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY patient_id, doctor_id ORDER BY created_at DESC) rn
FROM yourTable
)
SELECT patient_id, doctor_id, status, created_at
FROM cte
WHERE rn = 1
ORDER BY patient_id, doctor_id;

Select newest data from id A

I have the data like this:
ID TRANS_ID CREATED_DATE STATUS
----------------------------------------------
1 AA 2017-05-19 02:00:00 WAITING
2 AA 2017-05-20 02:00:00 IN_PROCESS
3 BB 2017-05-19 02:00:00 WAITING
4 CC 2017-05-19 02:00:00 WAITING
5 CC 2017-05-20 02:00:00 IN_PROCESS
I would like to show the data in table view like this one :
ID TRANS_ID CREATED_DATE STATUS
----------------------------------------------
2 AA 2017-05-20 02:00:00 IN_PROCESS
3 BB 2017-05-19 02:00:00 WAITING
5 CC 2017-05-20 02:00:00 IN_PROCESS
I take the newest data from each trans_id and I run this query but it does not work
select id, max(created_date), trans_id, status
from table_a
group by a.transaction_id
One option is to filter with a correlated subquery:
select t.*
from mytable t
where t.created_date = (
select max(t1.created_date) from mytable t1 where t1.trans_id = t.trans_id
)
Alternatively, you can use window functions:
select id, trans_id, created_date, status
from (
select t.*, rank() over(partition by trans_id order by created_date desc) rn
from mytable t
) t
where rn = 1
This allows top ties, if any. If you want no ties, you can use row_number() instead of rank().

Get last two entries of each account in table

I've got script that gives me all transactions for day for all accounts and sub accounts. His return you can see on the image. What I want, is return result as two last transactions for each accountId and subaccountId. Ideal return would be:
AccountId| SubAccountId| AmountInDay | Date
---------------------------------------------
210 | 1 | 0.00 |2017-06-20 00:00:00.000
210 | 1 | 0.00 |2017-06-05 00:00:00.000
1234 | 1 | 0.00 |2017-06-20 00:00:00.000
1234 | 1 | 0.00 |2017-06-05 00:00:00.000
This is the code of my script:
with CTE1 as
(
select top 2 AccountId, SubAccountId, [Date], sum(Amount_Amount) as Amount
from dbo.PayoutInstallment
group by accountId, SubAccountId, [Date]
)
, CTE2 as
(
select AccountId,SubAccountId, Amount_Amount, [Date],
dense_rank() over (partition by AccountId order by [Date] desc) as rn
from dbo.PayoutInstallment
)
select a1.AccountId,a1.SubAccountId, Sum(a1.Amount_Amount) as AmountInDay, a1.[Date]
from CTE2 a1
left join CTE2 a2
on a1.AccountId = a2.AccountId and a1.[Date] > a2.[Date]
and a2.rn = a1.rn+1
group by a1.[Date], a1.AccountId, a1.SubAccountId
order by a1.[Date] desc
EDIT
Sample Data
AccountId| SubAccountId| AmountInDay | Date
---------------------------------------------
210 | 1 | 0.00 |2017-03-15 00:00:00.000
210 | 1 | 0.00 |2017-04-20 00:00:00.000
210 | 1 | 100.00 |2017-05-17 00:00:00.000
210 | 1 | 1.00 |2017-06-05 00:00:00.000
210 | 1 | 1.00 |2017-06-05 00:00:00.000
1234 | 1 | 0.00 |2017-06-05 00:00:00.000
1234 | 1 | 0.00 |2017-06-05 00:00:00.000
1234 | 1 | 1.00 |2017-06-10 00:00:00.000
1234 | 1 | 1.00 |2017-04-10 00:00:00.000
I think you can use row_number and get 2 records as below:
Select * from (
Select AccountId, SubAccountId, [Date], sum(Amount_Amount) over (partition by accountid, SubAccountId, [Date])
,RowN = Row_number() over (partition by accountid, SubAccountId, [Date] order by [date] desc)
from dbo.PayoutInstallment
) a where a.RowN <= 2
Assume one day one transaction,
;WITH cte AS(SELECT *
, ROW_NUMBER() OVER (PARTITION BY AccountId, SubAccountId ORDER BY [Date] DESC) AS Rownum
FROM PayoutInstallment
)
SELECT *
, SUM(AmountInDay) OVER (PARTITION BY AccountId, SubAccountId) AS SumLast2days
FROM cte
WHERE Rownum<=2
If you want the SUM for the last two day you need to assign a number to each day. Then bring all the data related to those days by JOIN both dataset and then perform a GROUP BY
WITH cte as (
SELECT AccountId, SubAccountId, [Date],
ROW_NUMBER() OVER (PARTITION BY AccountId, SubAccountId
ORDER BY [Date] DESC) AS rn
FROM dbo.PayoutInstallment
)
SELECT P.AccountId,
P.SubAccountId,
P.[Date],
SUM(ammount)
FROM dbo.PayoutInstallment P
JOIN cte C
ON P.[Date] = C.[Date]
AND P.AccountId = C.AccountId
AND P.SubAccountId = C.SubAccountId
WHERE rn <= 2 -- Just the last day of each account, subacount
GROUP BY P.AccountId,
P.SubAccountId,
P.[Date]
I see you are using GROUP BY, so if you want the results to be sorted after the grouping, you should use HAVING if you want otherwise you should use WHERE. Here is an example of a WHERE clause you can use in your query to get only results between the last two days.
WHERE (a1.[Date] BETWEEN GETDATE()AND GETDATE()-2)

Getting most recent distinct records

Considering the following table:
User CreatedDateTime Quantity
----- ----------------- --------
Jim 2012-09-19 01:00 1
Jim 2012-09-19 02:00 5
Jim 2012-09-19 03:00 2
Bob 2012-09-19 02:00 2
Bob 2012-09-19 03:00 9
Bob 2012-09-19 05:00 1
What query would return the most recent rows (as defined by CreatedDateTime) for each User, so that we could determine the associated Quantity.
i.e. the following records
User CreatedDateTime Quantity
----- ----------------- --------
Jim 2012-09-19 03:00 2
Bob 2012-09-19 05:00 1
We thought that we could simply Group By User and CreatedDateTime and add a Having MessageCreationDateTime = MAX(.MessageCreationDateTime. Of course this does not work because Quantity is not available following the Group By.
Since you are using SQL Server, you can use Window Function on this.
SELECT [User], CreatedDateTime, Quantity
FROM
(
SELECT [User], CreatedDateTime, Quantity,
ROW_NUMBER() OVER(PARTITION BY [User] ORDER BY CreatedDateTime DESC) as RowNum
FROM tableName
) a
WHERE a.RowNum = 1
SQLFiddle Demo
;WITH x AS
(
SELECT [User], CreatedDateTime, Quantity,
rn = ROW_NUMBER() OVER (PARTITION BY [User] ORDER BY CreatedDateTime DESC)
FROM dbo.table_name
)
SELECT [User], CreatedDateTime, Quantity
FROM x WHERE rn = 1;
If you do not have the ability to use windowing functions, then you can use a sub-query:
select t1.[user], t2.mxdate, t1.quantity
from yourtable t1
inner join
(
select [user], max(CreatedDateTime) mxdate
from yourtable
group by [user]
) t2
on t1.[user]= t2.[user]
and t1.CreatedDateTime = t2.mxdate
see SQL Fiddle with Demo
SELECT DISTINCT
User,
CreatedDateTime,
Quantity
FROM
YourTable
WHERE
CreatedDateTime =
(SELECT MAX(CreatedDateTime) FROM YourTable t WHERE t.User = YourTable.User)
select * from <table_name> where CreatedDateTime in (select max(CreatedDateTime) from <table_name> group by user) group by user;

How to determine the maximum value for each category in SQL?

My table has records like below:
ID EmpID EffectiveDate PayElement Amount ComputeType AddDeduction
42 ISIPL001 2010-04-16 00:00:00.000 Basic 8000.00 On Attendance Addition
43 ISIPL001 2010-04-01 00:00:00.000 Con 2000.00 On Attendance Addition
44 ISIPL001 2010-04-01 00:00:00.000 HRA 2000.00 On Attendance Addition
54 ISIPL001 2011-01-01 00:00:00.000 Basic 15000.00 On Attendance Addition
55 ISIPL001 2011-01-01 00:00:00.000 Con 6000.00 On Attendance Addition
57 ISIPL001 2011-01-01 00:00:00.000 HRA 6000.00 On Attendance Addition
61 ISIPL001 2010-07-10 00:00:00.000 Basic 12000.00 On Attendance Addition
66 ISIPL001 2010-07-10 00:00:00.000 HRA 4200.00 On Attendance Addition
68 ISIPL001 2010-07-10 00:00:00.000 Con 5600.00 On Attendance Addition
I want the result display below:
i.e for each pay element available in my database, I need to record which is having maximum date for each pay element.
So my output should be like given below:
54 Basic 15000
55 Con 6000
57 HRA 6000
Try this:
SELECT ID,
PayElement,
Amount
FROM (
SELECT a.*,
RANK() OVER(PARTITION BY PayElement ORDER BY EffectiveDate DESC) AS rn
FROM <YOUR_TABLE> a
) a
WHERE rn = 1
;with cte as
(
select *,
row_number() over(partition by PayElement order by EffectiveDate desc) as rn
from YourTable
)
select
ID,
PayElement,
Amount
from cte
where rn = 1
Try this.
select
T.ID,
T.PayElement,
T.Amount
from
Test T inner join (select MAX(T_DATE.EffectiveDate) as MAX_DATE, T_DATE.PayElement from Test T_DATE group by T_DATE.PayElement) T_DATE on (T.PayElement = T_DATE.PayElement) and (T.EffectiveDate = T_DATE.MAX_DATE)
order by
T.ID
Select a.Id,
a.PayElement,
a.Amount
From dbo.YourTable a
Join
(
Select PayElement,
Max(EffectiveDate) as[MaxDate]
From dbo.YourTable
Group By PayElement
)b on a.PayElement = b.PayElement
And a.EffectiveDate = b.MaxDate
try something like
Select
a.ID, a.PayElement, a.Amount
From MyTable a
Inner Join (
Select PayElement, max(EffectiveDate) as MaxDate From MyTable Group By PayElement
) sub on a.EffectiveDate = sub.MaxDate and a.PayElement = sub.PayElement
select
Id, PayElement, Amount
from
YourTable a
inner join
(select
Id, PayElement, max(EffectiveDate) as EffectiveDate
from
YourTable
group by
PayElement, Id) b
on
a.Id = b.Id