SQL - Convert ROW_NUMBER function with multiple order by - sql

I am trying to convert this query to a subquery without the ROW_NUMBER function:
SELECT InvestorFundId, AsOfDate, AddedOn FROM (
SELECT ROW_NUMBER() OVER (PARTITION BY InvestorFundId ORDER BY AsOfDate DESC, AddedOn DESC) AS HistoryIndex, *
FROM [Investor.Fund.History]
WHERE DataStatusId = 1 AND AsOfDate <= (SELECT PeriodDate FROM [Fund.Period] WHERE Id = 5461)
) G WHERE HISTORYINDEX = 1
Basically this is selecting the most recent [Investor.Fund.History] within a time period and depending on the status.
So far I have this:
SELECT InvestorFundId, MAX(AsOfDate) AS MaxAsOfDate, MAX(AddedOn) AS MaxAddedOn FROM [Investor.Fund.History]
WHERE DataStatusId = 1 AND AsOfDate <= (SELECT PeriodDate FROM [Fund.Period] WHERE Id = 5461)
GROUP BY InvestorFundId
My query gives the incorrect results and it does this because when i use the MAX function on multiple columns, it does not select the max based on the order of both columns like the ROW_NUMBER does, instead it selects the MAX no matter both columns order positions.
For example, if I have a subset of data which looks like this:
| InvestorFundId | AsOfDate | AddedOn |
| 1 | 2010-10-01 00:00:00.000 | 2012-04-18 09:29:33.277 |
| 1 | 2006-11-01 00:00:00.000 | 2013-04-18 11:25:23.033 |
The ROW_NUMBER function will return the following:
| 1 | 2010-10-01 00:00:00.000 | 2012-04-18 09:29:33.277 |
Whereas my function returns this:
| 1 | 2010-10-01 00:00:00.000 | 2013-04-18 11:25:23.033 |
Which as you can see, is not actually a row in the table.
I would like my function to correctly return the row in the table based on the MAX AsOfDATE AND AddedOn
Can anyone help?

If you have a unique id that identifies each row, then you can do:
WITH ifh as (
SELECT InvestorFundId, AsOfDate, AddedOn
FROM [Investor.Fund.History]
WHERE DataStatusId = 1 AND AsOfDate <= (SELECT PeriodDate FROM [Fund.Period] WHERE Id = 5461)
)
SELECT ifh.*
FROM ifh
WHERE ifh.? = (SELECT ?
FROM ifh ifh2
WHERE ifh2.InvestorFundId = ifh.InvestorFundId
ORDER BY AsOfDate DESC, AddedOn DESC
FETCH FIRST 1 ROW ONLY
);
The ? is for the column that uniquely identifies each row.
This is also possible to do using APPLY:
select ifh2.*
from (select distinct InvestorFundId
from ifh
) i cross apply
(select top (1) ifh2.*
from ifh ifh2
where fh2.InvestorFundId = i.InvestorFundId
order by AsOfDate DESC, AddedOn DESC
fetch first 1 row only
) ifh2;

Related

get the id based on condition in group by

I'm trying to create a sql query to merge rows where there are equal dates. the idea is to do this based on the highest amount of hours, so that i in the end gets the corresponding id for each date with the highest amount of hours. i've been trying to do with a simple group by, but does not seem to work, since i CANT just put a aggregate function on id column, since it should be based the hours condition
+------+-------+--------------------------------------+
| id | date | hours |
+------+-------+--------------------------------------+
| 1 | 2012-01-01 | 37 |
| 2 | 2012-01-01 | 10 |
| 3 | 2012-01-01 | 5 |
| 4 | 2012-01-02 | 37 |
+------+-------+--------------------------------------+
desired result
+------+-------+--------------------------------------+
| id | date | hours |
+------+-------+--------------------------------------+
| 1 | 2012-01-01 | 37 |
| 4 | 2012-01-02 | 37 |
+------+-------+--------------------------------------+
If you want exactly one row -- even if there are ties -- then use row_number():
select t.*
from (select t.*, row_number() over (partition by date order by hours desc) as seqnum
from t
) t
where seqnum = 1;
Ironically, both Postgres and Oracle (the original tags) have what I would consider to be better ways of doing this, but they are quite different.
Postgres:
select distinct on (date) t.*
from t
order by date, hours desc;
Oracle:
select date, max(hours) as hours,
max(id) keep (dense_rank first over order by hours desc) as id
from t
group by date;
Here's one approach using row_number:
select id, dt, hours
from (
select id, dt, hours, row_number() over (partition by dt order by hours desc) rn
from yourtable
) t
where rn = 1
You can use subquery with correlation approach :
select t.*
from table t
where id = (select t1.id
from table t1
where t1.date = t.date
order by t1.hours desc
limit 1);
In Oracle you can use fetch first 1 row only in subquery instead of LIMIT clause.

SQL ORACLE - get min row with sequence equal values

My have table similar to:
MY_DAT | STATUS
=========|========
1.1.2017 | A
2.1.2017 | A
3.1.2017 | A
4.1.2017 | B
5.1.2017 | B
6.1.2017 | A
7.1.2017 | C
8.1.2017 | A
9.1.2017 | A
10.1.2017| A
I want SQL query that by date(MY_DAT) return min date with equal STATUS without interruption.
Example
MY_DAT = '1.1.2017' -> '1.1.2017',A
MY_DAT = '3.1.2017' -> '1.1.2017',A
MY_DAT = '10.1.2017' -> '8.1.2017',A
MY_DAT = '5.1.2017' -> '4.1.2017',B
I don't how this sql have to look like.
EDIT
I need result to be for every date. In this example result have to be:
MY_DAT | STATUS | BEGIN
=========|========|========
1.1.2017 | A |1.1.2017
2.1.2017 | A |1.1.2017
3.1.2017 | A |1.1.2017
4.1.2017 | B |4.1.2017
5.1.2017 | B |4.1.2017
6.1.2017 | A |6.1.2017
7.1.2017 | C |7.1.2017
8.1.2017 | A |8.1.2017
9.1.2017 | A |8.1.2017
10.1.2017| A |8.1.2017
ANSWER
select my_date, status,
min(my_date) over (partition by grp, status) as begin
from (select my_date,status ,
row_number() over(order by my_date)
-row_number() over(partition by status order by my_date) as grp
from tbl ) t
Thanks to Vamsi Prabhala
Use a difference of row numbers approach to assign groups to consecutive rows with same status. (Run the inner query to see this.). After this, it is just a group by operation to get the min date.
select status,min(my_date)
from (select my_date,status
,row_number() over(order by my_date)
-row_number() over(partition by status order by my_date) as grp
from tbl
) t
group by grp,status
Please try this.
SELECT status, min(my_dat)
FROM dates
GROUP BY status
OK, then what about this?
SELECT *
FROM dates
INNER JOIN
(
SELECT status, min(my_dat)
FROM dates
GROUP BY status
) sub
ON dates.status = sub.status

Subtracting current from previous row by date and ID

I'd like to extend this answer here by including an ID as a grouping column. I tried including a 'group by' clause without success.
+----------+--------------+----------
|NAV_Date |NetAssetValue | ID |
+----------+--------------+---------+
|12/31/2012| $4,000| A |
+----------+--------------+---------+
|03/31/2013| $5,000| A |
+----------+--------------+---------+
|12/31/2012| $4,000| B |
+----------+--------------+---------+
|03/31/2013| $5,000| B |
+----------+--------------+---------+
select NAV_date, NAV_value, (NAV_value / prev_value) - 1
from (select t.*,
(select top 1 NAV_value
from YOURTABLENAMEGOESHERE as t2
where t2.NAV_date < t.NAV_date
group by Nav_ID, Nav_value
order by t2.NAV_date desc
) as prev_value
from YOURTABLENAMEGOESHERE as t
) as t
Try this:
select
NAV_date,
NAV_value,
(NAV_value /
(select top 1 NAV_value
from YOURTABLENAMEGOESHERE as t2
where t2.NAV_date < t.NAV_date
order by t2.NAV_date desc) - 1
from
YOURTABLENAMEGOESHERE as t

get max of two columns (one column stores the date and other stores the time)

tableABC has separate date and time columns:
+-----------+--------+-----------+-----------+
| AccountID | userID | date | timestamp |
+-----------+--------+-----------+-----------+
| 123 | 1 | 29-MAR-13 | 21005 |
| 123 | 1 | 29-MAR-13 | 11005 |
| 123 | 1 | 23-MAR-13 | 21005 |
+-----------+--------+-----------+-----------+
I need the max of date column and timestamp column. The query I wrote only does the max of date and not the timestamp:
select *
from tableABC rn
where userID = '1'
and accountID = '123'
and date =
(
select max(date) MaxDate
from tableABC b
where b.userID = rn.userID
and b.accountID = rn.accountID
);
First of all, Oracle won't let you name a column DATE unless you surround the column name with double quotes, but I'd recommend just not naming it DATE - you'll avoid all kinds of trouble.
Oracle doesn't mind if a column is named TIMESTAMP, but that's still a "special" Oracle word so again I'd recommend not doing it. Oracle won't be confused but anyone reading your code will be.
Let's say the columns are named my_date and my_timestamp. To query the top date/timestamp for all users do something like this:
SELECT * FROM (
SELECT
AccountID,
userID,
my_date,
my_timestamp,
ROW_NUMBER() OVER (
PARTITION BY AccountID, UserID
ORDER BY my_date DESC, my_timestamp DESC) AS DateTimestampRank
FROM TableABC
) WHERE DateTimeStampRank = 1
If you want a query for just one user, the answer from #GordonLinoff is the one to use.
Just use order by:
select *
from (select *
from tableABC
order by "date" desc, "timestamp" desc
) t
where rownum = 1
If you are trying to get the maximum for each accountID/userID combination, then use row_number():
select *
from (select *, row_number() over (partition by userId, accountId order by "date" desc, "timestamp" desc) as seqnum
from tableABC
) t
where seqnum = 1
Why dont you order by date, and then time, and take the top row?
SELECT ACCOUNTID,USERID,MAX(DATE1),MAX(TIMESTAMP) FROM tableABC
GROUP BY ACCOUNTID,USERID,DATE1;

Max (SQL-Server)

I have a table that looks like this:
BARCODE | PRICE | STARTDATE
007023819815 | 159000 | 2008-11-17 00:00:00.000
007023819815 | 319000 | 2009-02-01 00:00:00.000
How can I select so I can get the result like this:
BARCODE | PRICE | STARTDATE
007023819815 | 319000 | 2009-02-01 00:00:00.000
select by using max date.
Thanks in advance.
SELECT TOP 1 barcode, price, startdate
FROM TableName
ORDER BY startdate DESC
Or if there can be more than one rows.
SELECT barcode, price, startdate
FROM TableName A
WHERE startdate = (SELECT max(startdate) FROM TableName B WHERE B.barcode = A.barcode)
UPDATE
changed second query to view max values per barcode.
An elegant way to do that is using the analytic function row_number:
SELECT barcode, price, startdate
FROM (
SELECT *
, ROW_NUMBER() OVER (PARTITION BY barcode ORDER BY startdate DESC) as rn
FROM YourTable
) subquery
WHERE rn = 1
If performance is an issue, check out some more complex options in this blog post.