MySQL - Select Rows in a Certain Sequence - php

I'm trying to build a people directory like LinkedIn has:
http://www.linkedin.com/directory/people-a
I don't want to fetch all the rows with name field starting with a and build the link list like LinkedIn. Is there any way in MySQL so I can only fetch rows in this sequence:
1st, 100th,101st,200th,201st,300th,301st,400th,401st, Last
That means I am trying to get two consecutive rows after a certain gap including the first and last item. The ids are not in nice uniformly increasing order, so I can't use this answser. Any help or hint is appreciated.
Say my query SELECT * FROMbusinesseswhere name like 'a%' order by name returns id like this:
1,3,5,6,8, 9,12,33,45,66,77,88,100,103,120,133,155,166,177,178,198
Above is if I want to get all the rows. But what I want is to get only the items after a certain distance. For example if I want to pick after every 5 items:
1,9,12, 88,100,166,177,198
So skip 4 items and take next two. Is that even possible in mysql?

you can order by whatever you want
create some function that gets a number and return 0,1 (all the numbers you specified will be 0 for example), then you need to pass the rank of the row to your function.
example
select first_name,rank
from (
SELECT first_name,
#curRank := #curRank + 1 AS rank
FROM person p, (SELECT #curRank := 0) r ) x
order by myfunc(x.rank);
myfunc being the function you wrote
if you want to only get those rows, not just sort by it and get them first you can do the following :
select first_name,rank
from (
SELECT first_name,
#curRank := #curRank + 1 AS rank
FROM person p, (SELECT #curRank := 0) r ) x
where rank in (1,100,101,102...)

You can select a range with the limit clause
SELECT ...... LIMIT 0,99
This will give the first 100 records
SELECT ...... LIMIT 100,199
will give the next & so on

Related

Leaderboard/Highscore neighbouring scores issue

I've got a database with 120k players. Each entry contains id, score (and more).
The goal is to get a highscore-list of not the top players, but instead of the N players above and and below a player, given his ID.
I currently try to solve this using two queries.
Query 1:
SELECT (
SELECT COUNT(*)
FROM players p2
WHERE p2.score > p1.score
) AS rank
FROM players p1
WHERE id = ID
returns the rank RANK of the player with an offset of -1. (for the best player it'll return 0)
Query 2:
SELECT id, score
FROM players
ORDER BY score DESC
LIMIT X OFFSET RANK;
returns a list with X=2*N+1 entries. I shift the $rank by -n to have the player that is doing the request in the middle (n players higher, current player, n players below).
So far, so good.
The actual issue now is, that for some scores there are more players with this score than X is big, which sometimes results in the player that should be in the middle of the list not even being contained in the X entries, but in some entries above or below.
To me it seems like a consistency problem, that query 1 returns a rank Y for player Z, but query 2 doesn't have player Z at it's Y'th position.
Can these queries be merged, or is there any other nice solution to this?
If the above stated is not clear, here's a minimalistic example:
n=1, requesting player called: C
database: A:123, B:123, C:123, D:123
Query 1 returns rank 3 for player C
Query 2 returns A:123, B:123, D:123 (being ranks 2-4)
C:123 should be in the middle, but the sorting of query 2 had C as rank 1.
The order of the elements with the same score in query 2 seems randomly
You can get the rank (position) in the highscore-list with something similar to the following query:
select * from (
select #rank:=#rank+1 rank, p.id
from players p
order by score desc
) t, (select #rank:= 0) t2
where id = :UID
After this query you can change the outer select to only get rank in the range of "rank" +- N

Join multiple tables and order by sum of row

Recently began working on a matchmaking system, where in this system there are 2 tables. One table is for matches and one table is for ranks.
The table for ranks starts off like this
table_ranks:
date
playeruid
rank
table_matches:
date
playeruid
playerpoints
I'm trying to now order the player by their points. However, in order to do this, I have made a query:
SELECT * FROM table_ranks ORDER by rank DESC
But now what I realize, is that I need to add the playerpoints on top of the rank. So basically, I need to add playerpoints to the player's current ranking. So, if I had this for an example row:
table_ranks:
2/20/15
Player1
56
table_matches:
2/27/15
Player1
5
I would need to build a query that takes the 56 of player1, looks for player1 in the matches and anywhere it sees it, it would need to add his 5 points making it a sum of 56. Once this is determined it would ORDER by this value in order to determine who is ranked with what. How do I begin my query? I understand that in order to join the tables, I need to start off like this:
"SELECT table_ranks., table_matches. from table_ranks, table_matches ORDER by RANK..."
Then to finish it,I would have to take the current value of the rank, then grab the specific player it's referring to and take all the matches and add up all the playerpoints to his rank then to determine how to order it by.
Try this:
SELECT r.playeruid, r.date AS rank_date, m.date AS macthes_date,
(r.rank + m.playerpoints) AS total_points
FROM table_ranks r INNER JOIN table_matches m ON r.playeruid = m.playeruid
ORDER BY total_points DESC
This query assumes that playeruid is unique in both tables.
Try the following query. I tested on a similartable structure and it should work
SELECT * , playeruid AS player_id, (
SELECT SUM( playerpoints )
FROM `table_matches`
WHERE playeruid = player_id
) AS points
FROM table_ranks
ORDER BY points DESC

MySQL select statement - How to calculate the current ranking

I have a table called user_rankings where votes (voted) are stored for each user. I want to display the current ranking of users (this week) that depends on how much votes the user got.
example to clarify:
RANK-NR, USERNAME, VOTED,
1, name1, 18 times
2, name1, 16 times
(my ranking here), myname, 13 times
In this example my ranking should be 3. If I'd have 17 votes, I would be number 2. If there would be five users above me, I would be number 8. I guess you get the point.
Now I can display the ranking number easily with an incrementing $i in PHP. But I only want to show a list limited to ten users (a top ten list) and directly after that my current ranking, if I'm not already in that top ten list. So I'm just wondering how to get my exact ranking number using MySQL.
I'm assuming to have hundreds of users in this list with a different amount of votes.
This is my statement at the moment:
SELECT
`voted`
FROM `users_ranking`
WHERE
`uid`='".$_SESSION['uid']."'
AND
WEEKOFYEAR(`date`)=WEEKOFYEAR(NOW())
LIMIT 1
I can't give you the exact code, but i think the following can give you some idea
select 'RANK-NR', 'USERNAME', 'VOTED' from
(
select 'RANK-NR', 'USERNAME', 'VOTED', rank() over (order by 'voted' desc) as rank
from users_ranking
where
uid='".$_SESSION['uid']."'
AND
WEEKOFYEAR(date)=WEEKOFYEAR(NOW())
) as abc
where
rank<11
i think rank() over (order by<>) should work
I just found out myself that this solution works:
SELECT *
FROM
(
SELECT #ranking:= #ranking + 1 rank,
a.`uid`
FROM `users_ranking` a, (SELECT #ranking := 0) b
ORDER BY a.`votes` DESC
) s
WHERE `uid`='".$_SESSION['uid']."'
AND
WEEKOFYEAR(`date`)=WEEKOFYEAR(NOW())
LIMIT 1
OK, example to go with my comment. What you have will often work, but there is nothing to force MySQL to do the sort before it applies the ranking.
As such using an extra level of sub query would give you this (not tested). The inner sub query is getting all the user ids for the relevant week in the right order, while the next outer sub query applies the ranking to this ordered result set. The outer query just gets the single returned row you require.
SELECT c.rank, c.uid
FROM
(
SELECT #ranking:= #ranking + 1 rank, a.uid
FROM
(
SELECT uid, votes
FROM `users_ranking`
WHERE WEEKOFYEAR(`date`) = WEEKOFYEAR(NOW())
ORDER BY votes DESC
) a,
(SELECT #ranking := 0) b
) c
WHERE c.uid = '".$_SESSION['uid']."'
LIMIT 1
Another possibility avoiding the sub query and also avoiding the need for a variable is to do a join. This is (mis)using HAVING to slim down the result to the single row you are interested in. Down side of this solution is that if multiple users have the same score they will each get the same ranking.
SELECT b.uid, COUNT(a.uid)
FROM users_ranking a
LEFT OUTER JOIN users_ranking b
ON WEEKOFYEAR(a.`date`) = WEEKOFYEAR(b.`date`)
AND a.votes >= b.votes
GROUP BY b.uid
HAVING b.uid = '".$_SESSION['uid']."'
EDIT
To give the top 10 rankings:-
SELECT b.uid, COUNT(a.uid) AS rank
FROM users_ranking a
LEFT OUTER JOIN users_ranking b
ON WEEKOFYEAR(a.`date`) = WEEKOFYEAR(b.`date`)
AND a.votes >= b.votes
GROUP BY b.uid
ORDER BY rank
LIMIT 10
Although in this case it might be quicker to use a sub query. You could then put the LIMIT clause in the sub query with the ORDER BY, hence it would only need to use the variables to add a rank to 10 rows.
I am not sure how to combine that with the query for a single user, mainly as I am not sure how you want to merge the 2 results together.

Pagination on with mysql queries

I have a system that automatically paginates all mysql queries with LIMIT and OFFSET.
Example:
// Page 1
SELECT * FROM tbl_products WHERE category_id = 1 LIMIT 20 OFFSET 0
// Page 2
SELECT * FROM tbl_products WHERE category_id = 1 LIMIT 20 OFFSET 20
// Page 3
SELECT * FROM tbl_products WHERE category_id = 1 LIMIT 20 OFFSET 40
etc.
Now the questions itself.
A) Is this the best way to do it? Are there any alternatives?
B) When I land on a product page (products.php?id=12345) and there will be a list of products on the same page, how can I choose the correct page on the product list? If this product happens to be on the 105th page on the product list?
This is to highlight the "selected product" on the list. Now it only works if the product happens to be on the first page, which is always automatically loaded to the products.php page.
For example if your first page has a get variable in the url www.tst.com?page=1
off = (page - 1)*20 ;
row = 20;
select * from table limit (off, row);
optimally you would use something like this. The code is much faster than two queries where you would have to do a count on the table and then use that count result as your new parameters because of the potential for n amount of rows in your table... The larger the table the less efficient queries will be.
A) Is this the best way to do it? Is there any alternatives?
It probably is the best way as pagination is performed by the server and only the desired rows are returned.
B) [...] how can I choose the correct page on the product list [...]
I can think of two possible solutions:
In your detail pages use a query that selects all ids:
SELECT id
FROM tbl_products
WHERE category_id = 1
ORDER BY id
Note the ORDER BY clause... both queries (the one with LIMIT and the one that selects all ids) must be ordered in exactly the same way. You can then loop through all rows using PHP code and locate the index at which the product id exists. Then divide this number by the page size to determine the page number.
An alternate solution is to se a MySQL query that numbers the rows such as the following:
SELECT #row_number := #row_number + 1 AS row_number, id
FROM tbl_products, (SELECT #row_number := 0) AS temp1
WHERE category_id = 1
ORDER BY id
Nest this query inside another query to determine the row number of the specific id:
SELECT row_number FROM (
SELECT #row_number := #row_number + 1 AS row_number, id
FROM tbl_products, (SELECT #row_number := 0) AS temp1
WHERE category_id = 1
ORDER BY id
) AS temp2 WHERE id = 1234
Divide the row number by page size to get page number.

Find the rank position of the list of stores in MySQL

In my database I have couple of stores and each store has couple of coupons sold and I need to find the rank as per the maximum coupons sold per store. I am using the following SQL code
set #rownum := 0;
SELECT #rownum := #rownum + 1 AS rank, sum(couponscount) as count, restaurant from coupons group by restaurant order by count desc
But, the output doesn't print the rank as per the maximum sum(couponscount) but its printing rank as per the store name alphabetical order.
How can I make it working?
I also need to retrieve the rank of a particular record and I am using the following SQL query for that
set #rownum := 0;
select rank from (SELECT #rownum := #rownum + 1 AS rank, sum(couponscount) as count, restaurant from coupons group by restaurant order by count desc) `storerank` where restaurant='some store name'
So, I need a first SQL query should be compatible with this one as well.
Thanks.
This works in mysql. The temporary table is removed at the end of the session and unique to the session so there are no issues with two different people running the same process colliding. Add the "Drop" if you need to run this more than once in your session (and be sure that you use a name not in your current database)
create temporary table temp select userid, sum(casesin) as CS from cases where csdate between '2014-07-25' and '2014-07-31' group by userid order by 2 ;
select #x:=#x+1 rrn, userid, CS from temp, (SELECT #x:= 0) AS s order by CS;
Drop table temp;
This works on two files I have joined on ticket number to determine who sold the most cases on a ticket each day.
SELECT IDENTITY(int, 1,1) AS Ranking, sum(qtydelivered) as QtyDelivered, orderticketnumber into #Temp from orderlines join orders on orderticketnumber = ticketnumber WHERE shippeddate = '2014-09-15' group by orderticketnumber order by 1 desc;
select * from #Temp;
drop table #Temp;

Categories