I have a query below. On executing it, it returns all the results where i can see multiple records for each single memberid.
I want to select the profiles of two members randomly chosen without any performance issue.
SELECT
a.memberid,
a.category_id,
a.content,
a.count_cid,
a.importance
FROM tb_profilingdata a,
tb_member b
WHERE a.memberid = b.memberid
AND a.category_id IN($catstr)
AND a.memberid NOT IN('$mid',$seen)
AND b.gender = 'male'
ORDER BY a.memberid, a.category_id
I tried some queries for choosing one random record
SELECT
r1.memberid
FROM tb_profilingdata AS r1
JOIN (SELECT
(RAND() * (SELECT MAX(DISTINCT(memberid)) FROM tb_profilingdata)) AS memberid) AS r2
WHERE r1.memberid >= r2.memberid
ORDER BY r1.memberid ASC
LIMIT 1
But it chooses in total 1 record out of tb_profilingdata whereas i want records of one randomly chosen member.
I tried the same query with tb_member, but it is possible that a member present in tb_member might not have its entries in tb_profilingdata..
Please suggest me a good way out with least performance issues.
May be that will help you:
SELECT [something] FROM [source] WHERE [conditions] ORDER BY RAND() LIMIT 2
This query is untested unless you provide some sample data
SELECT
a.memberid,
a.category_id,
a.content,
a.count_cid,
a.importance
FROM tb_profilingdata a
left join (select
memberid
from tb_member
group by memberid
order by rand()) b
on a.memberid = b.memberid
WHERE a.category_id IN($catstr)
AND a.memberid NOT IN('$mid',$seen)
AND b.gender = 'male'
ORDER BY a.memberid, a.category_id
Related
I'm trying to create a leaderboard but i'm not sure how to do the mysql query.
I would like to count all the levels from a player in the skills table and get the total Level and count all the experience from a player in the experience table and get the Total Exp along with displaying the persons name from the users column.
There is 3 tables factions_mcmmo_users, factions_mcmmo_experience, factions_mcmmo_skills.
This is what i have so far but it doesn't work:
$sql = ("SELECT a.id,
(SELECT COUNT(*) FROM factions_mcmmo_experience WHERE user_id = a.id) as TotalXP,
(SELECT COUNT(*) FROM factions_mcmmo_skills WHERE user_id = a.id) as TotalLevel
FROM (SELECT DISTINCT id FROM factions_mcmmo_users) a LIMIT 10;");
Any help would be very appreciated
EDIT: I have it working now but i'm unsure if its the most efficient way to do things so if anyone could help me out if theres a better way, it would mean a lot.
I would also like to know if it's possible to display the total exp and level with commas if the number is in the thousands for example: total level 5,882 and total xp 582,882
EDIT 2:
I have figured out how to format the numbers but still don't know if my code is efficient
$sql = ("SELECT id, user,
(SELECT FORMAT(Sum(taming)+Sum(mining)+Sum(woodcutting)+Sum(repair)+Sum(unarmed)+Sum(herbalism)+Sum(excavation)+Sum(archery)+Sum(swords)+Sum(axes)+Sum(acrobatics)+Sum(fishing)+Sum(alchemy),0) FROM factions_mcmmo_skills b WHERE b.user_id = a.id) as TotalLevel,
(SELECT FORMAT(Sum(taming)+Sum(mining)+Sum(woodcutting)+Sum(repair)+Sum(unarmed)+Sum(herbalism)+Sum(excavation)+Sum(archery)+Sum(swords)+Sum(axes)+Sum(acrobatics)+Sum(fishing)+Sum(alchemy),0) FROM factions_mcmmo_experience c WHERE c.user_id = a.id) as TotalXP
FROM (SELECT id, user FROM factions_mcmmo_users) a group by id ORDER BY TotalLevel DESC, TotalXP DESC LIMIT 10;");
EDIT 3
Updated code from scaisEdge but was displaying everyones level as 1 and XP as 1, so i changed count(*) changed to sum, added an order By TotalLevel in Descending order and that seems to have worked but i can't get it to display the persons name (user column) in the user table? not sure if i was supposed to change to sum because it didn't work the other way.
$sql = ("SELECT a.id, b.TotalXP, c.TotalLevel
FROM (SELECT DISTINCT id FROM factions_mcmmo_users) a
INNER JOIN (
SELECT user_id, Sum(taming)+Sum(mining)+Sum(woodcutting)+Sum(repair)+Sum(unarmed)+Sum(herbalism)+Sum(excavation)+Sum(archery)+Sum(swords)+Sum(axes)+Sum(acrobatics)+Sum(fishing)+Sum(alchemy) as TotalXP
FROM factions_mcmmo_experience
GROUP By user_id
) b on b.user_id = a.id
INNER JOIN (
SELECT user_id, Sum(taming)+Sum(mining)+Sum(woodcutting)+Sum(repair)+Sum(unarmed)+Sum(herbalism)+Sum(excavation)+Sum(archery)+Sum(swords)+Sum(axes)+Sum(acrobatics)+Sum(fishing)+Sum(alchemy) as TotalLevel
FROM factions_mcmmo_skills
GROUP by user_id
) c on c.user_id = a.id
ORDER BY TotalLevel DESC
LIMIT 10;");
EDIT 4
Everything working but when i try to format the totals using "FORMAT(Sum(Columns), 0) on the inner joins, the EXP Total appears to work but the main Total Level is not displaying results that are over 1,000 and it breaks the leaderboard positioning, it should be sorting them on total level but it appears to be random, when u remove the format,0 it goes back to working
I would like it to display commas if the number number is the thousands for example: Total Level: 5,532 and Total EXP 5882,882
See live demo: http://mcbuffalo.com/playground/leaderboards/server/factions-mcmmo.php
Updated Code trying to use Format:
$sql = ("SELECT a.id, a.user, b.TotalXP, c.TotalLevel
FROM (SELECT id, user FROM factions_mcmmo_users) a
INNER JOIN (
SELECT user_id, FORMAT(Sum(taming)+Sum(mining)+Sum(woodcutting)+Sum(repair)+Sum(unarmed)+Sum(herbalism)+Sum(excavation)+Sum(archery)+Sum(swords)+Sum(axes)+Sum(acrobatics)+Sum(fishing)+Sum(alchemy), 0) as TotalXP
FROM factions_mcmmo_experience
GROUP By user_id
) b on b.user_id = a.id
INNER JOIN (
SELECT user_id, FORMAT(Sum(taming)+Sum(mining)+Sum(woodcutting)+Sum(repair)+Sum(unarmed)+Sum(herbalism)+Sum(excavation)+Sum(archery)+Sum(swords)+Sum(axes)+Sum(acrobatics)+Sum(fishing)+Sum(alchemy), 0) as TotalLevel
FROM factions_mcmmo_skills
GROUP by user_id
) c on c.user_id = a.id
ORDER BY TotalLevel DESC;");
EDIT 5
Changed number with PHP, everything works
Original Images
you could use an couple of inner join
$sql = ("SELECT a.id, a.name, b.TotalXP, c.TotalLevel
FROM (SELECT DISTINCT id, name FROM factions_mcmmo_users) a
INNER JOIN (
SELECT user_id, COUNT(*) as TotalXP
FROM factions_mcmmo_experience
GROUP By user_id
) b on b.user_id = a.id
INNER JOIN (
SELECT user_id, COUNT(*) as TotalLevel
FROM factions_mcmmo_skills
GROUP by user_id
) c on c.user_id = a.id
LIMIT 10
I have researched other questions and tried their solutions, but none helped me. How can I lower the execution time of this query?
SELECT c.id,c.title,c.time,u.username, (SELECT time FROM comments_read as io WHERE io.id=c.id AND io.uid=$logged_userid LIMIT 1) as read_time
FROM comments as c
LEFT JOIN users u ON u.uid=c.uid
WHERE c.deleted=0
ORDER by c.time DESC
LIMIT 20
Thank you so much.
This is your query:
SELECT c.id, c.title, c.time, u.username,
(SELECT io.time
FROM comments_read as io
WHERE io.id = c.id AND
io.uid = $logged_userid
LIMIT 1
) as read_time
FROM comments as c LEFT JOIN
users u
ON u.uid = c.uid
WHERE c.deleted = 0
ORDER by c.time DESC
LIMIT 20;
Indexes can be used to improve performance. I would start with comments(deleted, time, uid, id), users(uid), and comments_read(id, uid, time).
Note: using LIMIT without an ORDER BY is usually discouraged, because the results are unstable. You can get any matching row.
EDIT:
If the above indexes do not help, they might be getting confused by the JOIN. You can do the same thing as:
SELECT c.id, c.title, c.time,
(SELECT u.username FROM users u WHERE u.uid = c.uid) as username,
(SELECT io.time
FROM comments_read as io
WHERE io.id = c.id AND
io.uid = $logged_userid
LIMIT 1
) as read_time
FROM comments as c
WHERE c.deleted = 0
ORDER by c.time DESC
LIMIT 20;
This should take advantage of the above indexes and have no "file sort" step to slow things down.
I am facing a really wierd issue, that i cannot GROUP by. If i do, it won't show the "best time". It's kinda wierd.
Here is the SQL String which works (But it shows the same quiz 4 times, which it shouldn't!)
SELECT distinct a.quiz_id, a.time, b.end, b.images, b.prize_title
FROM entries a, quiz b
WHERE a.quiz_id = b.id AND a.member = '".$_SESSION['id']."'
ORDER BY a.time ASC
Screen of output: http://imgur.com/mOfBK3q
This show NOT the best time (Fastest time => ASC)
SELECT distinct a.quiz_id, a.time, b.end, b.images, b.prize_title
FROM entries a, quiz b
WHERE a.quiz_id = b.id AND a.member = '".$_SESSION['id']."'
GROUP BY a.quiz_id
ORDER BY a.time ASC
Screen of output: http://imgur.com/QiNQZY4
The column "a.time" is miliseconds and the column is DOUBLE.
Try using HAVING instead of WHERE.
Avoid DISTINCT and GROUP BY in the same query (you only need one or the other).
Try something like this.
SELECT MIN(a.time), a.quiz_id, a.time, b.end, b.images,
b.prize_title FROM entries a, quiz b WHERE a.quiz_id = b.id AND
a.member = '".$_SESSION['id']."' GROUP BY a.quiz_id
The query doesn't work as expected because GROUP BY is applied before ORDER BY. It would just sort the results after the grouping -- and there's only one result left.
Try the following:
SELECT a.quiz_id, a.time, b.end, b.images, b.prize_title
FROM entries a
JOIN quiz b ON a.quiz_id = b.id
ORDER BY a.time ASC
LIMIT 1
I currently have:
SELECT tbl_review.*, users.first_name, users.last_name, (
SELECT order_ns.tran_date
FROM order_ns
LEFT JOIN product_2_order_ns.external_order_id = order_ns.order_id
WHERE product_2_order_ns.bkfno IN :id
ORDER BY order_ns.trandate ASC
LIMIT 1
) as purchase_date
FROM tbl_review
LEFT JOIN users ON users.sequal_user_id = tbl_review.user_id
WHERE tbl_review.product_id IN :id AND tbl_review.approved = 1
Which, in its sub query, selects an order the user has which has a product in question (defined in :id) get the the oldest transaction date on file for one of the found orders.
I would really like to keep this to one call of the database (don't really want to call again for each returned user for just one field, or even do a range query of all users) but obviously this particular query isn't working.
What can I do, if anything, to get this working?
I cannot make the sub query into a join since they are two distinct pieces of data, the sub query needs to return detail for each row in the main query.
I think you just want a correlated subquery. It is unclear exactly what the relationship is between the inner query and the outer one. My guess is that it is on users and orders:
SELECT tbl_review.*, users.first_name, users.last_name,
(SELECT order_ns.tran_date
FROM order_ns LEFT JOIN
product_2_order_ns
on product_2_order_ns.external_order_id = order_ns.order_id and
product_2_order_ns.bkfno = tbl_review.product_id and
WHERE order_ns.user_id = tbl_review.user_id
ORDER BY order_ns.trandate ASC
LIMIT 1
) as purchase_date
FROM tbl_review LEFT JOIN
users
ON users.sequal_user_id = tbl_review.user_id
WHERE tbl_review.product_id IN :id AND tbl_review.approved = 1;
EDIT:
Oh, the inner query has no relationship to the outer query. Then it is easier. Move it to the from clause using cross join:
SELECT tbl_review.*, users.first_name, users.last_name,
innerquery.tran_date as purchase_date
FROM tbl_review LEFT JOIN
users
ON users.sequal_user_id = tbl_review.user_id cross join
(SELECT order_ns.tran_date
FROM order_ns LEFT JOIN
product_2_order_ns
on product_2_order_ns.external_order_id = order_ns.order_id
WHERE product_2_order_ns.bkfno IN :id
ORDER BY order_ns.trandate ASC
LIMIT 1
) innerquery
WHERE tbl_review.product_id IN :id AND tbl_review.approved = 1;
#Gordons answer is really close but I wanted it to return even if no data was found for tran_date so I changed my query to:
SELECT tbl_review.*, users.first_name, users.last_name, order_ns.tran_date
FROM tbl_review
LEFT JOIN users ON users.sequal_user_id = tbl_review.user_id
LEFT JOIN order_ns ON order_ns.order_id = (
SELECT order_ns.order_id
FROM order_ns
LEFT JOIN product_2_order_ns on product_2_order_ns.external_order_id = order_ns.order_id
WHERE product_2_order_ns.bkfno IN :id
ORDER BY order_ns.tran_date ASC
LIMIT 1
)
WHERE tbl_review.product_id IN :id AND tbl_review.approved = 1;
This will return the distinct data of tran_date irrespective of whether it is found or not.
I have a query
SELECT SQL_CALC_FOUND_ROWS a.memberid, a.category_id, a.content, a.count_cid, a.importance
FROM tb_profilingdata a, tb_member b
WHERE a.memberid = b.memberid AND a.category_id IN ($catstr) AND a.memberid NOT IN ( $seen_txt) AND b.gender != '$gender'
ORDER BY a.memberid, a.category_id LIMIT $offset, 4500
Since my table is very large, i want to limit my query result to a certain limit.
An also choose a dynamic offset, so that i can get random set of values everytime i run the query.
Till now i was calculating random offset based on total number of rows in the table through PHP.
But if the offset value is larger than the total number of rows returned by the query, than the result would be empty.
So is there any way through which i don't have to load the entire table as well as set an appropriate random offset so that i can get random values?
Try this nested query:
SELECT c.* FROM (
SELECT a.memberid, a.category_id, a.content, a.count_cid, a.importance
FROM tb_profilingdata a, tb_member b
WHERE a.memberid = b.memberid AND a.category_id IN ($catstr) AND a.memberid NOT IN ($seen_txt) AND b.gender != '$gender'
ORDER BY RAND() LIMIT 4500
) c ORDER BY c.memberid, c.category_id
Note: you will be unable to get total count of rows in the table using SQL_CALC_FOUND_ROWS.
There exist several ways to optimize the ORDER BY RAND().
Use Turnery operator to get the offset from the url Like following
(isset($_REQUEST['offset']))?$offset=$_REQUEST['offset']:$offset=0;
for random offset use rand function
$offset=rand(0,totalrecord/4500);
and the pass this offset in your query
SELECT SQL_CALC_FOUND_ROWS a.memberid, a.category_id, a.content, a.count_cid, a.importance
FROM tb_profilingdata a, tb_member b
WHERE a.memberid = b.memberid AND a.category_id IN ($catstr) AND a.memberid NOT IN ( $seen_txt) AND b.gender != '$gender'
ORDER BY a.memberid, a.category_id LIMIT $offset, 4500