Mysql - calculate difference between two columns in 2 different tables and insert - mysql

I have the following statement which need to add 1 more subquery to calculate the difference between the number of followers in the tweeps table and the number in the current column in the ranking table and insert the difference in column called latest in ranking table sure PK is screenname ,
Like number in follower coulm in tweeps table is 10 current coulmn n ranking table for the same screenname is 5 the value will be added to latest is +5
mysql_query ("
INSERT INTO ranking
SELECT #rank := #rank + 1, tweeps.* FROM tweeps
JOIN( SELECT #rank := 0 ) AS init
ORDER BY followers DESC
ON DUPLICATE KEY UPDATE
ranking.ranking = #rank,
ranking.name = tweeps.name,
ranking.followers = tweeps.followers,
ranking.tweets = tweeps.tweets,
ranking.location = tweeps.location,
ranking.`join date` = tweeps.join_date,
ranking.avatar = tweeps.avatar;");
mysql_close($con);

Try this:
INSERT INTO ranking
SELECT #rank := #rank + 1, tweeps.* FROM tweeps
JOIN( SELECT #rank := 0 ) AS init
ORDER BY followers DESC
ON DUPLICATE KEY
UPDATE ranking set
ranking = #rank,
name = tweeps.name,
followers = tweeps.followers - followers,
tweets = tweeps.tweets,
location = tweeps.location,
`join date` = tweeps.join_date,
avatar = tweeps.avatar;
I changed the syntax to ON DUPLICATE KEY UPDATE ranking set...

Related

how to calculate user ranking from 2 different tables

I have a users table with phase1 and phase2 columns that i need to calculate the users rank in each phase and store it in these fields.
the ranking is calculated based on a different table points where i have the points by phase for each user.
what i am trying to do is
sum all points for each user by phase and calculate his rank based on that
in case the user points are equal compare the sum of grade1 in case that is also equal compare the sum of grade2
update users table with his rank in each phase
here is how my new table look like with some demo data
sql fiddle demo
currently I use the below code to calculate the ranking from my old table where both rank and user info are in the same table
old sql fiddle demo
update users a
join (
select id,
(
select count(distinct total)
from users d
where c.total < d.total
) +1 rank
from users c
) b on a.id = b.id
set a.rank = b.rank
there are analytics function in oracle called as rank() and dense_rank() which can be useful to get your result.
As you are using mysql, I tried to convert those function in mysql equivalent.
You can get the desired result with following query which you can use to update users table. You may have to change it further if for the logic when there is tie on grades as well.
set #pk1 ='';
set #rn1 =1;
set #tot ='';
set #val =1;
SELECT id,
name,
phase,
phasetotal,
denseRank
FROM
(
SELECT id,
name,
phase,
phasetotal,
#rn1 := if(#pk1=phase, #rn1+#val,1) as denseRank,
#val := if(#pk1=phase, if(#tot=phasetotal, #val+1, 1),1) as value,
#pk1 := phase,
#tot := phasetotal
FROM
(
select users.id,users.name, points.phase, sum(points.points)
as phasetotal from users,points where users.id = points.userid
group by users.id, points.phase order by points.phase, phasetotal desc, points.grade1 desc, points.grade2 desc
) A
) B;
Here's the update query
set #pk1 ='';
set #rn1 =1;
set #tot ='';
set #val =1;
UPDATE users u join (
SELECT id,
name,
phase,
phasetotal,
denseRank
FROM
(
SELECT id,
name,
phase,
phasetotal,
#rn1 := if(#pk1=phase, #rn1+#val,1) as denseRank,
#val := if(#pk1=phase, if(#tot=phasetotal, #val+1, 1),1) as value,
#pk1 := phase,
#tot := phasetotal
FROM
(
select users.id,users.name, points.phase, sum(points.points)
as phasetotal from users,points where users.id = points.userid
group by users.id, points.phase order by points.phase, phasetotal desc, points.grade1 desc, points.grade2 desc
) A
) B ) C on u.id = C.id
SET u.phase1 = CASE WHEN C.phase = 1 and u.phase1 = 0 THEN C.denseRank ELSE u.phase1 END,
u.phase2 = CASE WHEN C.phase = 2 and u.phase2 = 0 THEN C.denseRank ELSE u.phase2 END;

How to get 100 record of each customer in a single query

I have a location table now I have to keep only 100 record for each customer and have to move rest of them to another server.
using this query We can get customers list
SELECT user_id FROM user_location_history WHERE (TIMESTAMPDIFF(DAY,FROM_UNIXTIME(location_date/1000),SYSDATE()) < 30) GROUP BY user_id HAVING COUNT(1) < 100
Now suppose we have a list of customer like
I am trying to write a single query to get all the record for each customer which is greater then 100.
125452
412555
554114
258471
Please suggest ....
This will get the latest 100 records of each customer
SELECT user_id, substring_index(group_concat(sample_id ORDER BY sample_id DESC SEPARATOR ','), ',', 100) limit100, FROM user_location_history GROUP BY user_id ;
Try this query it generates a row number for each record :-
set #num := 0, #group := '';
select *
from
(
select user_id,
#num := if(#group = `user_id`, #num + 1, 1) as row_number,
#group := `user_id` as dummy
from user_location_history
WHERE (TIMESTAMPDIFF(DAY,FROM_UNIXTIME(location_date/1000),SYSDATE()) < 30)
) as x
where x.row_number <= 100;

Find all NULL values and set them to lowest unused number using MySQL query

I want to find all NULL values in column parameter_id and set them to lowest unused parameter_id.
I have query which will find lowest unused parameter_id, I also know how to get list of NULL values.
SELECT MIN(t1.parameter_id)+1 FROM table AS t1 WHERE NOT EXISTS (SELECT * FROM table AS t2 WHERE t2.parameter_id = t1.parameter_id+1)
I can get list of all rows with parameter_id=NULL, then make query to find current lowest unused parameter_id and then update parameter_id to that lowest unused number. Since table has 50.000 rows, this approach would create thousands of queries (50.000 * 2 per row).
Is there way to run "single query" which will find all parameter_id=NULL and update them all to current lowest unused parameter_id?
Here is table decrtiption (MySQL 5.5):
id (INT) primary key, auto_increment
parameter_id (INT) default NULL
Sample data:
# id, parameter_id
1, NULL
2, 1
3, NULL
4, 5
5, 3
Desired result:
# id, parameter_id
1, 2
2, 1
3, 4
4, 5
5, 3
EDIT:
I distilled what I want to single query. I simply need to run this query until there is 0 rows affected by UPDATE.
UPDATE `table`
SET parameter_id=
(SELECT *
FROM
(SELECT MIN(t1.parameter_id)+1
FROM `table` AS t1
WHERE NOT EXISTS
(SELECT *
FROM `table` AS t2
WHERE t2.parameter_id = t1.parameter_id+1)) AS t4)
WHERE parameter_id IS NULL LIMIT 1
The following enumerates the unused parameter ids:
select t.*, (#rn := #rn + 1) as seqnum
from table t cross join
(select #rn := 0) params
where not exists (select 1 from table t2 where t2.parameter_id = t.id)
order by t.id;
(You might want to put this in a temporary table with an index on seqnum for the subsequent query.)
The problem is getting a join key for the update. Here is a bit of a kludge: I'm going to add a column, enumerate it, and then drop it:
alter table `table` add column null_seqnum;
update `table` t cross join (select #rn1 := 0) params
set null_seqnum = (#rn1 := #rn1 + 1)
where parameter_id is null;
update `table` t join
(select t.*, (#rn := #rn + 1) as seqnum
from `table` t cross join
(select #rn := 0) params
where not exists (select 1 from `table` t2 where t2.parameter_id = t.id)
order by t.id
) tnull
on t.null_seqnum = tnull.seqnum
set t.parameter_id = tnull.id;
alter table `table` drop column null_seqnum;

Limit result amount for each ID to x

I am trying to do something like this in MYSQL, but without making query multiple times (50 times, in my case) through a PHP foreach.
foreach($this->map_ids as $key => $val) {
$this->db->query("SELECT scores.profile_number, scores.score FROM scores
LEFT JOIN players ON scores.profile_number = players.profile_number
WHERE scores.map_id = {'$val'}
AND scores.profile_number IN (SELECT profile_number FROM players WHERE banned = 0) LIMIT 10");
}
This is how it looks approximately when I retrieve all scores without LIMIT.
profile score map_id
76561198026851335 2478 47455
76561198043770492 2480 47455
... ... ...
76561198043899549 1340 47452
76561198048179892 1345 47452
... ... ...
I want only 10 entries (scores) from each unique map_id.
This is surprisingly difficult to do but I've ended up using user variables to do the job, check out the following demo. Obviously my data structure is much simplified but it should be enough to get you going:
SQL Fiddle example
Here is the SQL for anyone who may be interested in skipping the demo (hideous, I know)
SELECT *
FROM (
SELECT profile_number, score, map_id
FROM (
SELECT
profile_number, score, map_id,
IF( #prev <> map_id, #rownum := 1, #rownum := #rownum+1 ) AS rank,
#prev := map_id
FROM scores
JOIN (SELECT #rownum := NULL, #prev := 0) AS r
ORDER BY map_id
) AS tmp
WHERE tmp.rank <= 10
) s
JOIN players p
ON s.profile_number = p.profile_number
Basically, what is happening is this:
ORDER BY map_id
Orders your table by map_id so that all the same ones are together.
Next we assign a rownumber to each row by using the following logic:
IF( #prev <> map_id, #rownum := 1, #rownum := #rownum+1 )
If the previous row's map_id is not equal to the current row's ID, set the row number = 1, otherwise increase the rownumber by 1.
Finally, only return the rows who have a rownumber less than or equal to 10
WHERE tmp.rank <= 10
Hope that makes it a little clearer for you.
You can use the limit directive.
SELECT * FROM `your_table` LIMIT 0, 10
This will display the first 10 results from the database.
SELECT * FROM `your_table` LIMIT 5, 5
This will show records 6, 7, 8, 9, and 10

MySQL update statement to store ranking positions

I'm trying to get my head around a query and I just can't figure it out. I would appreciate if someone give me a pointer. As a simple example of what I'm trying to achieve, I have these records in the database
Score|Ranking
-------------
100 |0
200 |0
300 |0
And I would like the Ranking field to contain 1,2,3 based on who's got the highest score so the result should be:
Score|Ranking
-------------
100 |3
200 |2
300 |1
At the moment, I'm doing a for next loop for all these records but given that in reality that could be a few thousand - that could take forever! Does anyone have an idea on a magic query which would do this in one go?
Here's a way to do it:
SET #r=0;
UPDATE table SET Ranking= #r:= (#r+1) ORDER BY Score DESC;
/* use this if you just want to pull it from the db, but don't update anything */
SET #r=0;
SELECT *, #r:= (#r+1) as Ranking FROM table ORDER BY Score DESC;
In MySQL, you can use row_number.
Here's an example of using it in a SELECT:
select #rownum:=#rownum+1 ‘rank’, p.*
from player p, (SELECT #rownum:=0) r
order by score desc;
If you INSERT INTO using a SELECT like this, you will get your rankings.
This creates an inline update statement that will rank your players incrementing by the variable #rc. I've used it many times in very similar cases, it works well and keeps it all on the DB side.
SET #rc = 0;
UPDATE players JOIN (SELECT #rc := #rc + 1 AS rank, id FROM players ORDER BY rank DESC)
AS order USING(id) SET players.rank = order.rank;
id is assumed to be the primary key for your players table.
If you are using MySQL 8 so can you use the new function RANK()
SELECT
score,
RANK() OVER (
ORDER BY score DESC
) ranking
FROM
table;
Depending on how you want to display the ranking with even score so can you also check out DENSE_RANK()
And as an UPDATE:
WITH
ranking AS(
SELECT
score,
RANK() OVER (
ORDER BY score DESC
) ranking
FROM
table
)
UPDATE
table,
ranking r
SET
table.ranking = r.ranking
WHERE
table.score = r.score
SET #r = 0;
UPDATE players JOIN (SELECT #r := #r + 1 AS rank, id FROM players ORDER BY rank DESC)
AS sorted USING(id) SET players.rank = sorted.rank;
i'm showing you my way of doing it [for interval sql update functions]
select:
set #currentRank = 0,
#lastRating = null,
#rowNumber = 1;
select
*,
#currentRank := if(#lastRating = `score`, #currentRank, #rowNumber) `rank`,
#rowNumber := #rowNumber + if(#lastRating = `score`, 0, 1) `rowNumber`,
#lastRating := `score`
from `table`
order by `score` desc
update:
set #currentRank = 0,
#lastRating = null,
#rowNumber = 1;
update
`table` r
inner join (
select
`primaryID`,
#currentRank := if(#lastRating = `score`, #currentRank, #rowNumber) `rank`,
#rowNumber := #rowNumber + if(#lastRating = `score`, 0, 1) `rowNumber`,
#lastRating := `score`
from `table`
order by `score` desc
) var on
var.`primaryID` = r.`primaryID`
set
r.`rank` = var.`rank`
i did not make any performance checks on this one except for testing that it works