Reputation: 1197
I have a sql table that is an audit trail. I am trying to grab the most recent time the price was changed.
Using this query I have the following data
select id as id_1
,item_no, price
,post_dt
,post_tm
,aud_action
from iminvaud_sql
where item_no = '1-ADVENT ENGLISH'
and aud_action in ('B','C')
order by id desc
id_1 item_no price post_dt post_tm aud_action
221 1-ADVENT ENGLISH 2.000000 2014-08-18 00:00:00.000 1900-01-01 10:19:35.113 C
218 1-ADVENT ENGLISH 2.000000 2014-08-18 00:00:00.000 1900-01-01 10:19:35.110 B
217 1-ADVENT ENGLISH 2.000000 2014-08-18 00:00:00.000 1900-01-01 10:01:47.163 C
216 1-ADVENT ENGLISH 3.000000 2014-08-18 00:00:00.000 1900-01-01 10:01:46.757 B
59 1-ADVENT ENGLISH 3.000000 2013-08-19 00:00:00.000 1900-01-01 13:23:32.950 C
58 1-ADVENT ENGLISH 1.000000 2013-08-19 00:00:00.000 1900-01-01 13:23:32.890 B
The system writes a B for a before change and a C for the change so in a sense the B and C records are grouped. For this example I wanted to get the 217 record because it was the most recent price change.
Upvotes: 0
Views: 367
Reputation: 35593
To get id_1 = 217 from that list isn't TOP 1 order date/time DESC alone, because that ID is the "second most recent" C record
So, you could USE ROW_NUMBER(), or use TOP 2 then TOP 1
SELECT
*
FROM (
SELECT
id AS id_1
, item_no
, price
, post_dt
, post_tm
, aud_action
, ROW_NUMBER() OVER (ORDER BY post_dt DESC, post_tm DESC) AS Rn
FROM iminvaud_sql
WHERE item_no = '1-ADVENT ENGLISH'
AND aud_action = 'C'
ORDER BY
post_dt DESC, post_tm DESC
) sq
WHERE RN = 2
;
or
SELECT TOP 1
*
FROM (
SELECT TOP 2
id AS id_1
, item_no
, price
, post_dt
, post_tm
, aud_action
FROM iminvaud_sql
WHERE item_no = '1-ADVENT ENGLISH'
AND aud_action = 'C'
ORDER BY
post_dt DESC, post_tm DESC
) sq
ORDER BY
post_dt ASC, post_tm ASC
;
Upvotes: 1
Reputation: 1269973
Your query is essentially:
select s.*
from iminvaud_sql s
where s.item_no = '1-ADVENT ENGLISH' and s.aud_action in ('B','C')
order by id desc;
As far as I can tell, the "B" and "C" are not adding any information. Instead, let's look at the first occurrence of the most recent price. I will base this on id
. The following will work if the prices are monotonic (either always increasing or decreasing):
select top 1 s.*
from iminvaud_sql s
where exists (select 1
from iminvaud_sql s2
where s2.item_no = s.item_no and s2.aud_action in ('B','C') and s2.price = s.price
) and
s.aud_action in ('B','C') and s.item_no = '1-ADVENT ENGLISH'
order by s.id_1 asc;
If this isn't the case, you can use a trick. The trick is to take the difference between row_number()
and row_number()
partitioned by price. The largest values for the difference will be the most recent price.
select top 1 s.*
from (select s.*,
(row_number() over (order by id_1) -
row_number() over (partition by price order by id_1)
) as pricegroup
from iminvaud_sql s
where s2.aud_action in ('B','C') and s.item_no = '1-ADVENT ENGLISH'
) s
order by price_group, s.id_1 asc;
Upvotes: 1
Reputation: 337
I added more data... what if this was the case.. If I order by date and time I would get 221 but the price didn't change – user867621 4 mins ago
If price didn't change, why do the system write that:
221 1-ADVENT ENGLISH 2.000000 2014-08-18 00:00:00.000 1900-01-01 10:19:35.113 C
218 1-ADVENT ENGLISH 2.000000 2014-08-18 00:00:00.000 1900-01-01 10:19:35.110 B
Upvotes: 1
Reputation: 32713
Not sure what your requirement is - but if you just want the top row - then use top:
select select top 1 id as id_1, item_no, price, post_dt, post_tm, aud_action
from iminvaud_sql where item_no = '1-ADVENT ENGLISH '
and aud_action in ('B','C') order by post_dt desc, post_tm desc
Upvotes: 1