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

Popular posts from this blog

c# - SharpSVN - How to get the previous revision? -

c++ - Is it possible to compile a VST on linux? -

url - Querystring manipulation of email Address in PHP -