Need some help please.
I have the following table:
Accountno
TrackNo
PercentInc
CreateDate
Lastdate
123456
396
3
01/03/2019
24/05/2021
123456
516
0.9
20/01/2020
25/06/2020
123456
516
3
01/07/2013
29/10/2021
123456
396
1
12/12/2018
12/05/2018
123456
396
2
05/09/2019
08/11/2019
123456
516
0.4
08/09/2018
30/12/2020
I need to show results one row per TrackNo - I have to display the PercentInc where the CreateDate is the latest but show the LastDate for that row with the max (LastDate)
Results:
Accountno
TrackNo
PercentInc
CreateDate
Lastdate
123456
396
2
05/09/2019
24/05/2021
123456
516
0.9
20/01/2020
29/10/2021
Thanks
You can use window functions:
select Accountno, TrackNo, PercentInc, CreateDate, max_Lastdate
from (select t.*,
row_number() over (partition by accountno, trackno order by createdate desc) as seqnum,
max(lastdate) over (partition by accountno, trackno) as max_lastdate
from t
) t
where seqnum = 1;
There are many ways to skin a cat with SQL. One approach that I find intuitive:
Get the maximum dates for each TrackNo from a query like this:
SELECT TrackNo, MAX(CreateDate) MaxCreateDate, MAX(Lastdate) MaxLastDate
FROM Table
GROUP BY TrackNo
Then link this to the two records you want with a sub-query and two inner joins:
SELECT maxLastDate.Accountno, maxCreateDate.TrackNo, maxCreateDate.PercentInc, maxCreateDate.CreateDate, maxLastDate.Lastdate
FROM (
SELECT TrackNo, MAX(CreateDate) MaxCreateDate, MAX(Lastdate) MaxLastDate
FROM Table
GROUP BY TrackNo
) AS maxDates
INNER JOIN Table AS maxCreateDate
ON maxCreateDate.TrackNo = maxDates.TrackNo AND maxCreateDate.CreateDate = maxDates.MaxCreateDate
INNER JOIN Table AS maxLastDate
ON maxLastDate.TrackNo = maxDates.TrackNo AND maxLastDate.Lastdate = maxDates.MaxLastDate
Related
I have two tables Patient and PatientStatusLog using SQL tables
1.Patient
sno patientid status createdby Reegion
1 481910 D 1222 India
2 476795 D 1222 India
2.PatientStatusLog
sno patientid status comments createdby CreatedDate(dd/mm/yyyy)
1 481910 A mycommnet 1222 01/01/2000
2 481910 A mycommnet 1222 02/01/2000
3 481910 B mycommnet 1222 01/01/2000
4 481910 C mycommnet 1222 01/01/2000
I need output like below who have status A and createddate should recent pass date from PatientStatusLog table using patientid of both tables
Region status CreatedDate
India A 02/01/2000
You can use window functions:
select . . . -- whatever columns you want
from Patient p join
(select psl.*,
rank() over (partition by patientid order by createddate desc) as seqnum
from PatientStatusLog psl
) psl
on p.patientid = psl.patientid
where psl.seqnum = 1 and psl.status = 'A';
Note that this uses rank() because the creation date appears to have duplicates.
Try this:
SELECT TOP 1
p.Region,
psl.Status,
psl.CreatedDate
FROM
Patient [p]
JOIN PatientStatusLog [psl] ON psl.patientid = p.patientid
AND psl.status = 'A'
ORDER BY
psl.CreatedDate DESC
For the subquery request:
SELECT
p.Region,
x.status,
x.CreatedDate
FROM
(
SELECT TOP 1
PatientId,
Status,
CreatedDate
FROM
PatientStatusLog
WHERE
status = 'A'
ORDER BY
CreatedDate DESC
) AS x
JOIN Patient [p] ON p.PatientId = x.PatientId
Note that this is definitely not the best way to handle this, both from a code readability and optimization perspective.
for my SQL Server 2016 project I have an Orders table looks like the one below and I want to create a SQL query that shows the oldest order for each customer / product. There are thousands of orders in the Orders table today and I should expect this to grow in size so I want this to perform well.
The goal is the output to look like this:
OrderID
CustomerID
ProductID
OrderDt
OrderAmt
123
1
1
1/1/2021
$50
456
1
2
1/2/2021
$20
345
2
1
1/1/2021
$30
The data in the Orders table today look like this:
OrderID
CustomerID
ProductID
OrderDt
OrderAmt
123
1
1
1/1/2021
$50
758
1
1
1/2/2021
$80
563
1
2
1/3/2021
74
684
1
2
1/4/2021
23
456
1
2
1/2/2021
$20
345
2
1
1/1/2021
$30
The canonical method is to use row_number():
select t.*
from (select t.*,
row_number() over (partition by customerid, productid order by orderdt, orderid) as seqnum
from t
) t
where seqnum = 1;
With an index on (customerid, productid, orderdt), then a correlated subquery might be a smidgen faster:
select t.*
from t
where t.orderdt = (select min(t2.orderdt)
from t t2
where t2.productid = t.productid and t2.customerid = t.customerid
);
Or a slightly less performance method without subqueries:
select top (1) with ties t.*
from t
order by row_number() over (partition by productid, customerid order by orderdt);
I have a table named trades like this:
id trade_date trade_price trade_status seller_name
1 2015-01-02 150 open Alex
2 2015-03-04 500 close John
3 2015-04-02 850 close Otabek
4 2015-05-02 150 close Alex
5 2015-06-02 100 open Otabek
6 2015-07-02 200 open John
I want to sum up trade_price grouped by seller_name when last (by trade_date) trade_status was 'open'. That is:
sum_trade_price seller_name
700 John
950 Otabek
The rows where seller_name is Alex are skipped because the last trade_status was 'close'.
Although I can get desirable output result with the help of nested select
SELECT SUM(t1.trade_price), t1.seller_name
WHERE t1.seller_name NOT IN
(SELECT t2.seller_name FROM trades t2
WHERE t2.seller_name = t1.seller_name AND t2.trade_status = 'close'
ORDER BY t2.trade_date DESC LIMIT 1)
from trades t1
group by t1.seller_name
But it takes more than 1 minute to execute above query (I have approximately 100K rows).
Is there another way to handle it?
I am using PostgreSQL.
I would approach this with window functions:
SELECT SUM(t.trade_price), t.seller_name
FROM (SELECT t.*,
FIRST_VALUE(trade_status) OVER (PARTITION BY seller_name ORDER BY trade_date desc) as last_trade_status
FROM trades t
) t
WHERE last_trade_status <> 'close;
GROUP BY t.seller_name;
This should perform reasonably with an index on seller_name
select
sum(trade_price) as sum_trade_price,
seller_name
from
trades
inner join
(
select distinct on (seller_name) seller_name, trade_status
from trades
order by seller_name, trade_date desc
) s using (seller_name)
where s.trade_status = 'open'
group by seller_name
There is a table tbl_products that contains data as shown below:
Id Name
----------
1 P1
2 P2
3 P3
4 P4
5 P5
6 P6
And another table tbl_inputs that contains data as shown below:
Id Product_Id Price Register_Date
----------------------------------------
1 1 10 2010-01-01
2 1 20 2010-10-11
3 1 30 2011-01-01
4 2 100 2010-01-01
5 2 200 2009-01-01
6 3 500 2011-01-01
7 3 270 2010-10-15
8 4 80 2010-01-01
9 4 50 2010-02-02
10 4 92 2011-01-01
I want to select all products(id, name, price, register_date) with maximum date in each group.
For Example:
Id Name Price Register_Date
----------------------------------------
3 P1 30 2011-01-01
4 P2 100 2010-01-01
6 P3 500 2011-01-01
10 P4 92 2011-01-01
select
id
,name
,code
,price
from tbl_products tp
cross apply (
select top 1 price
from tbl_inputs ti
where ti.product_id = tp.id
order by register_date desc
) tii
Although is not the optimum way you can do it like:
;with gb as (
select
distinct
product_id
,max(register_date) As max_register_date
from tbl_inputs
group by product_id
)
select
id
,product_id
,price
,register_date
from tbl_inputs ti
join gb
on ti.product_id=gb.product_id
and ti.register_date = gb.max_register_date
But as I said earlier .. this is not the way to go in this case.
;with cte as
(
select t1.id, t1.name, t1.code, t2.price, t2.register_date,
row_number() over (partition by product_id order by register_date desc) rn
from tbl_products t1
join tbl_inputs t2
on t1.id = t2.product_id
)
select id, name, code, price, register_date
from cte
where rn = 1
Something like this..
select id, product_id, price, max(register_date)
from tbl_inputs
group by id, product_id, price
you can use the max function and the group by clause. if you only need results from the table tbl_inputs you even don't need a join
select product_id, max(register_date), price
from tbl_inputs
group by product_id, price
if you need field from the tbl_prducts you have to use a join.
select p.name, p. code, i.id, i.price, max(i.register_date)
from tbl_products p join tbl_inputs i on p.id=i.product_id
grooup by p.name, p. code, i.id, i.price
Try this:
SELECT id, product_id, price, register_date
FROM tbl_inputs T1 INNER JOIN
(
SELECT product_id, MAX(register_date) As Max_register_date
FROM tbl_inputs
GROUP BY product_id
) T2 ON(T1.product_id= T2.product_id AND T1.register_date= T2.Max_register_date)
This is, of course, assuming your dates are unique. if they are not, you need to add the DISTINCT Keyword to the outer SELECT statement.
edit
Sorry, I didn't explain it very well. Your dates can be duplicated, it's not a problem as long as they are unique per product id. if you can have duplicated dates per product id, then you will have more then one row per product in the outcome of the select statement I suggested, and you will have to find a way to reduce it to one row per product.
i.e:
If you have records like that (when the last date for a product appears more then once in your table with different prices)
id | product_Id | price | register_date
--------------------------------------------
1 | 1 | 10.00 | 01/01/2000
2 | 1 | 20.00 | 01/01/2000
it will result in having both of these records as outcome.
However, if the register_date is unique per product id, then you will get only one result for each product id.
I have the following table:
**TABLE1**
RecordID UserID UserName Balance TranDate
---------------------------------------------------------------
100 10001 John Doe 10213.00 2013-02-12 00:00:00.000
101 10001 John Doe 1932.00 2013-04-30 00:00:00.000
102 10001 John Doe 10213.00 2013-03-25 00:00:00.000
103 10001 John Doe 14514.00 2013-04-12 00:00:00.000
104 10001 John Doe 5430.00 2013-02-19 00:00:00.000
105 10001 John Doe 21242.00 2010-02-11 00:00:00.000
106 10001 John Doe 13342.00 2013-05-22 00:00:00.000
Now what i'm trying to do is to query the two most recent transactions and arrive at this data:
RecordID UserID UserName Balance TranDate
---------------------------------------------------------------
106 10001 John Doe 13342.00 2013-05-22 00:00:00.000
101 10001 John Doe 1932.00 2013-04-30 00:00:00.000
Then using the data above I would like to compare the balances to show the difference:
UserID UserName Difference
---------------------------------------------------------------
10001 John Doe -11410.00
This just shows the difference between the two previous balances (the latest and the balance before the latest)
Now I have the following query below. This works okay to show the two most recent transactions.
SELECT
TOP 2 *
FROM Table1
WHERE UserID = '1001'
ORDER
BY TranDate DESC
Now my issues are:
Is the sql above safe to use? I am just relying on the sorting of the TranDate by the ORDER BY DESC keyword and I am not so sure if this is very much reliable or not.
How do I select the difference between the two Balances (Row 2 - Row 1 )? I was looking for some answers online and I find stuff about self-joining. I tried it but it doesn't show me my desired output.
EDIT:
This is the closest I can get to my desired result. Can someone help me out on this please? Thanks!
DECLARE #SampleTable TABLE
(
UserID INT,
UserName VARCHAR(20),
Balance DECIMAL(9,2) DEFAULT 0
)
INSERT
INTO #SampleTable
(UserID, UserName, Balance)
SELECT
TOP 2 UserID,
UserName,
Balance
FROM Table1
WHERE UserID = '1001'
ORDER
BY TranDate DESC
SELECT A.UserID,
A.UserName,
B.Balance - A.Balance AS Difference
FROM #SampleTable A
JOIN #SampleTable B
ON A.UserID = B.UserID
Thanks a lot!
You should be able to use something like the following assuming SQL Server as the RDBMS:
;with cte as
(
select recordid, userid, username, balance, trandate,
row_number() over(partition by userid order by trandate desc) rn
from table1
)
select c1.userid, c1.username,
c1.balance - c2.balance diff
from cte c1
cross apply cte c2
where c1.rn = 1
and c2.rn = 2;
See SQL Fiddle with demo.
Or this could be done using an INNER JOIN on the row_number value:
;with cte as
(
select recordid, userid, username, balance, trandate,
row_number() over(partition by userid order by trandate desc) rn
from table1
)
select c1.userid, c1.username,
c1.balance - c2.balance diff
from cte c1
inner join cte c2
on c1.rn + 1 = c2.rn
where c1.rn = 1
See SQL Fiddle with Demo