MySQL Help: Optimize Update Query that Sets Rank According to Order of Another Column -
hello stackoverflow community:
the situation
i building update query sets ranks records in table according how each record compares each other. example of table:
id | budget | cost | rank | rank_score 1 | 500 | 20 | ? | ? 2 | 400 | 40 | ? | ? 3 | 300 | 40 | ? | ?
so in table, cost
have weight in determining rank, followed budget
. thus, record #2 rank higher, while record #3 second , #1 last. can see, if 2 records have same cost
, budget
breaks tie.
now, in order keep track of such 'weight' easily, i'm creating rank_score
column, hold concatenation of cost
, budget
. rank_score
table above be:
id | budget | cost | rank | rank_score 1 | 500 | 20 | ? | 20500 2 | 400 | 40 | ? | 40400 3 | 300 | 40 | ? | 40300
this rank_score
can filled like:
update table_name set rank_score = concat(cost, budget);
the problem
everything alright far. comes problem. need integer-only rank
column several stuff sorting, above showing user rank of record. of course, this rank
column equal descending order of rank_scores. cannot find way calculate rank
column in single update query without having subqueries, loops in php, etc.
what have tried
so, @ first trying fetch rank_score
calculation like:
select id, concat(cost, budget) rank_score table_name ;
and looping in php rank_scores, build query went like:
update table_name set rank_score = case id when 1 20500 end, rank = case id when 1 3 end id in (1) ;
... of course, sample update query not complete, has more when end
clauses each record in table. needless say, ugly, when expect having thousands , thousands of records.
so, in conclusion, have way calculating rank_score
, want calculate rank
(= descending order of rank score) in same query, or @ least without doing crazy php looping , case when end
clauses.
thank reading , thinking ;)
clarifications
clarifying @sjuan76 said: cannot assign rank via php since there instances when user shown fixed quantity of records @ time (example, user page: select * user_id = 333
, fetch 1, 3 or 8 records) , needs know what's rank each record. assigning rank via php in case doesn't work because such rank relative fetched records, not in table.
first, change budget
, cost
, rank_score
integer or other numeric datatype , instead of
update table_name set rank_score = concat(cost, budget) ;
then use:
update table_name set rank_score = cost * 1000 + budget * 1 ;
it's easier won't have deal string functions , have like:
select * table_name (conditions...) order rank_score desc
(parenthesis: having 1 parameter (1000
) set higher other (1
) equivalent having order of cost, budget
. try check:
select * table_name order cost desc , budget desc
so, can drop rank_score
alltogether, unless off course plan make experiments various parameter values.
as others have pointed, it's not best practise have field stores not data calculation. it's de-normalization. instaed, keep table normalized , let database calculations every time need it:
select id, budget, cost, cost*1000 + budget*1 rank_score_calculated table_name order rank_score_calculated desc
rank_score_calculated
not stored in above example. way, won't have update calculated field every time budget or cost changed or new row added in table.
there 1 drawback. if table big , need query (and calculation) done lots of users , often, , table updated quite often, may slow database. in case, 1 should start thinking adding such field.
the other case when 1 needs absolute rank
across table rows, need. because mysql has no "window" functions, it's hard write such query in pure sql.)
the rank can calculated using mysql variables
select * , @rownum:=@rownum+1 rank_calculated table_name , (select @rownum:=0) st order rank_score desc
and if want put values rank
, use:
update table_name join ( select id , @rownum:=@rownum+1 rank_calculated table_name , (select @rownum:=0) st order rank_score desc ) r on r.id = table_name.id set table_name.rank = r.rank_calculated ;
the above 2 queries not pure sql. may examine option move daabase system supports window functions, postgres, sql-server or oracle.
Comments
Post a Comment