简体   繁体   中英

How do I refer to a record in sql that immediately precedes another record in a group?

I have a weird update query to write.

Here's the table

PK-ID (int) --- FK-ID (int) --- Value (int)

In my data set, if I group by FK-ID and order by PK-ID, suppose this is an example of one group:

5 --- 10 --- 23
7 --- 10 --- 49
8 --- 10 --- 81

Due to a bug in some old software, records 7 and 8 have incorrect values. The correct value for 7 is (49-23) = 26 and the correct value for 8 is (81-49) = 32. Record 5 is correct.

I need to update each record to subtract the value of the record immediately preceding it when it is grouped by FK-ID and ordered by PK-ID. If there is no preceding record I do not need to change the value.

Is there a way to write a general sql update query to accomplish this? How would I (conditionally) retrieve the value of the preceding record in the group? I'm using SQL server 2008.

Thanks!

with ordered as (
    select *, rn = row_number() over (partition by fk_id order by pk_id)
      from tbl
    )
update cur
   set value = cur.value - prior.value
  from ordered cur
  join ordered prior on prior.fk_id = cur.fk_id
                and prior.rn = cur.rn-1;

This is what I believe to be the correct answer, using a similar idea to the previous one. The toupdate subquery calculates the values, based on the rules in the question (update records with the same foreign key and consecutive primary keys). It does assume that the ids are nuemric values of some sort.

with toupdate as (
    select t.pkid, t.value - tprev.value as newval
    from t join
         t tprev
         on t.pkid = tprev.pkid+1 and t.fkid = tprev.fkid
   )
update t
    set value = newvalue
    from toupdate
    where t.pkid = toupdate.pkid

I hope it should return what you want(sorry, I cannot try it the moment); you just need to incorporate it with UPDATE

 WITH cte1 AS 
 (SELECT pk_id, fk_id, value, ROW_NUMBER() OVER (PARTITION BY fk_id ORDER BY pk_id DESC)
 as num 
 FROM your_table 
 )

 SELECT a.*, 
 --CASE 
 --  WHEN b.pk_id IS NOT NULL THEN a.value-b.value 
 --  ELSE 0 END 
 a.value-b.value as valid_number
 FROM cte1 a
 --LEFT JOIN cte1 b ON (b.fk_id = a.fk_id AND b.num = a.num-1)
 INNER JOIN cte1 b ON (b.fk_id = a.fk_id AND b.num = a.num-1)  
update t set value = value -
              isnull((select top 1 value 
                                 from t t2 
                                     where t2.FKID=t.FKID 
                                       and t2.PKID<t.PKID 
                        order by PKID desc),0);

Here is a SQLFiddle demo

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM