So, I thought I was getting pretty good at MySQL until I ran into this idea:
I have a table logging "votes" (aptly named votes) with these fields:
id: The vote's unique ID.
user: Unique User ID of the person who voted
item: ID of item they're voting on
vote: The vote they cast SET('up','down')
Now, I'm trying to come up with an SQL way to find users whose only votes are downvotes. I know of a way to write it procedurally in php after querying most of the data out of the table but it seems really, really inefficient to do that way when only a few queries could find this out.
Ideally I want my result to just be a list of users who have 0 upvotes (as being in the table means they have voted, so they only downvote) and maybe the number of downvotes they've cast.
Any ideas on how I should approach this?
SELECT user, SUM(IF(vote='down',1,0)) AS numDownVotes
FROM votes
GROUP BY user
HAVING SUM(IF(vote='up',1,0))=0 -- 0 upvotes
AND SUM(IF(vote='down',1,0))>0 -- at least 1 downvotes
I can't help but feel there's a neat GROUP BY user, vote way to do this though.
I haven't checked the syntax or anything, but this comes to mind...
SELECT user
FROM votes
GROUP BY user
HAVING SUM(IF(vote = 'up', 1, 0)) = 0
AND SUM(IF(vote = 'down', 1, 0)) > 0
select user, count(user)
from votes
where user not in (
select distinct user
from votes
where votes = 'up')
select v.user from votes v where
0=(select count(a.vote) from votes a where a.user=v.user and a.vote='up' group by user) u
and
0 <(select count(a.vote) from votes a where a.user=v.user and a.vote='down' group by user) d
group by user;
Anything wrong with doing it on the database call? Something like:
$query = "SELECT id FROM votes WHERE vote = 'down'";
$result = mysql_query($query);
while ($rows = mysql_fetch_assoc($result))
{
$curID = $rows['id'];
}
<?php
// Make a MySQL Connection
$query = "SELECT user, COUNT(user) FROM votes where vote == 'down' GROUP BY user";
$result = mysql_query($query) or die(mysql_error());
// Print out result
while($row = mysql_fetch_array($result)){
echo "User : ". $row['type'] ." has ". $row['COUNT(user)'] ." downvote/s.";
echo "<br />";
}
?>
Related
I have 2 tables - users and articles.
users:
user_id (int)
name (varchar)
articles:
article_id (int)
user_id (int)
title (varchar)
description (text)
In my application I need to display 20 RANDOM articles on a page.
My query is like this:
SELECT a.title
, a.description
, u.name
FROM articles a
JOIN users u
USING (user_id)
ORDER
BY RAND()
LIMIT 20
A user can have any number of articles in the database.
Now the problem is sometimes out of 20 results, there are like 9-10 articles from one single user.
I want those 20 records on the page to not contain more than 3 (or say 4) articles from a particular user.
Can I achieve this through SQL query. I am using PHP and MySQL.
Thanks for your help.
You could try this?
SELECT * FROM
(
SELECT B.* FROM
(
SELECT A.*, ROW_NUMBER() OVER (PARTITION BY A.USER_ID ORDER BY A.R) USER_ROW_NUMBER
FROM
(
SELECT a.title, a.description, u.name, RND() r FROM articles a
INNER JOIN users u USING (user_id)
) A
) B
WHERE B.USER_ROW_NUMBER<=4
) C
ORDER BY RAND() LIMIT 20
Mmm, intresting I don't think this is possible through a pure sql query.
My best idea would be to have an array of the articles that you'll eventually display query the database and use the standard SELECT * FROM Articles ORDER BY RAND() LIMIT 20
The go through them, making sure that you have indeed got 20 articles and no one has breached the rules of 3/4 per user.
Have another array of users to exclude, perhaps using their user id as an index and value of a count.
As you go through add them to your final array, if you find any user that hits you rule add them to the array.
Keep running the random query, excluding users and articles until you hit your desired amount.
Let me try some code (it's been a while since I did php)
$finalArray = [];
$userArray = [];
while(count($finalArray) < 20) {
$query = "SELECT * FROM Articles ";
if(count($finalArray) > 0) {
$query = $query . " WHERE articleID NOT IN(".$finalArray.")";
$query = $query . " AND userID NOT IN (".$userArray.filter(>4).")";
}
$query = $query . " ORDER BY Rand()";
$result = mysql_query($query);
foreach($row = mysql_fetch_array($result)) {
if(in_array($finalArray,$row) == false) {
$finalArray[] = $row;
}
if(in_array($userArray,$row[userId]) == false) {
$userArray[$row[userId]] = 1;
}
else {
$userArray[$row[userId]] = $userArray[$row[userId]] + 1;
}
}
Thanks for helping, first I will show code:
$dotaz = "Select * from customers JOIN contracts where customers.user_id ='".$_SESSION['user_id']."' and contracts.customer_contract = ".$_SESSION['user_id']." order by COUNT(contracts.customer_contract) DESC limit $limit, $pocetZaznamu ";
I need to get the lists of users (customers table) ordered by count of contracts(contracts table)
I tried to solve this by searching over there, but I can't... if you help me please and explain how it works, thank you! :) $pocetZanamu is Number of records.
I need get users (name, surname etc...) from table customers, ordered by number of contracts in contracts table, where is contract_id, customer_contract (user id)..
This should do it where is the column name you are counting.
$id = $_SESSION['user_id'] ;
$dotaz = "Select COUNT(`customer_contract`) AS CNT, `customer_contract` FROM `contracts` WHERE `user_id`=$id GROUP BY `customer_contract` ORDER BY `CNT` DESC";
Depending on what you are doing you may want to store the results in an array, then process each element in the array separately.
while ($row = mysqli_fetch_array($results, MYSQL_NUM)){
$contracts[$row[1]] = $row[0];
}
foreach ($contracts AS $customer_contract => $count){
Process each user id code here
}
Not sure what you are counting. The above counts the customer_contract for a table with multiple records containing the same value in the customer_contract column.
If you just want the total number of records with the same user_id then you'd use:
$dotaz = "Select 1 FROM `contracts` WHERE `user_id`=$id";
$results = $mysqli->query($dotaz);
$count = mysql_num_rows($results);
I got a little problem, I've got a database, in that database are different names, id, and coins. I want to show people their rank, so your rank has to be 1 if you have the most coins, and 78172 as example when your number 78172 with coins.
I know I can do something like this:
SELECT `naam` , `coins`
FROM `gebruikers`
ORDER BY `coins` DESC
But how can I get the rank you are, in PHP :S ?
You can use a loop and a counter. The first row from MySql is going the first rank,I.e first in the list.
I presume you want something like:
1st - John Doe
2nd - Jane Doe
..
..
right?
See: http://www.if-not-true-then-false.com/2010/php-1st-2nd-3rd-4th-5th-6th-php-add-ordinal-number-suffix
Helped me a while ago.
You could use a new varariable
$i = "1";
pe care o poti folosi in structura ta foreach,while,for,repeat si o incrementezi mereu.
and you use it in structures like foreach,while,for,repeat and increment it
$i++;
this is the simplest way
No code samples above... so here it is in PHP
// Your SQL query above, with limits, in this case it starts from the 11th ranking (0 is the starting index) up to the 20th
$start = 10; // 0-based index
$page_size = 10;
$stmt = $pdo->query("SELECT `naam` , `coins` FROM `gebruikers` ORDER BY `coins` DESC LIMIT {$start}, {$page_size}");
$data = $stmt->fetchAll();
// In your template or whatever you use to output
foreach ($data as $rank => $row) {
// array index is 0-based, so add 1 and where you wanted to started to get rank
echo ($rank + 1 + $start) . ": {$row['naam']}<br />";
}
Note: I'm too lazy to put in a prepared statement, but please look it up and use prepared statements.
If you have a session table, you would pull the records from that, then use those values to get the coin values, and sort descending.
If we assume your Session table is sessions(session_id int not null auto_increment, user_id int not null, session_time,...) and we assume that only users who are logged in would have a session value, then your SQL would look something like this: (Note:I am assuming that you also have a user_id column on your gebruikers table)
SELECT g.*
FROM gebruikers as g, sessions as s WHERE s.user_id = g.user_id
ORDER BY g.coins DESC
You would then use a row iterator to loop through the results and display "1", "2", "3", etc. The short version of which would look like
//Connect to database using whatever method you like, I will assume mysql_connect()
$sql = "SELECT g.* FROM gebruikers as g, sessions as s WHERE s.user_id = g.user_id ORDER BY g.coins DESC";
$result = mysql_query($sql,$con); //Where $con is your mysql_connect() variable;
$i = 0;
while($row = mysql_fetch_assoc($result,$con)){
$row['rank'] = $i;
$i++;
//Whatever else you need to do;
}
EDIT
In messing around with a SQLFiddle found at http://sqlfiddle.com/#!2/8faa9/6
I came accross something that works there; I don't know if it will work when given in php, but I figured I would show it to you either way
SET #rank = 0; SELECT *,(#rank := #rank+1) as rank FROM something order by coins DESC
EDIT 2
This works in a php query from a file.
SELECT #rank:=#rank as rank,
g.*
FROM
(SELECT #rank:=0) as z,
gebruikers as g
ORDER BY coins DESC
If you want to get the rank of one specific user, you can do that in mysql directly by counting the number of users that have more coins that the user you want to rank:
SELECT COUNT(*)
FROM `gebruikers`
WHERE `coins` > (SELECT `coins` FROM `gebruikers` WHERE `naam` = :some_name)
(assuming a search by name)
Now the rank will be the count returned + 1.
Or you do SELECT COUNT(*) + 1 in mysql...
I have this loop in PHP to select all my scores from my game from a MySQL table.
My query is:
$sql = "SELECT * FROM $table WHERE ";
switch($type) {
case "global":
$sql .= "1 ";
break;
case "device":
$sql .= "udid = '$udid' ";
break;
case "name":
$sql .= "name = '$name' ";
break;
}
$sql .= "ORDER BY $sort ";
$sql .= "LIMIT $offset,$count ";
$result = mysql_query($sql,$conn);
The udid is a unique identifier. And the loop:
while ($row = mysql_fetch_object($result)) {
echo '<tr>
<td>
'.$rank.'
</td>
<td>
'.$row->name.'
</td>
<td>
'.$row->score.'
</td>
<td>
'.$row->udid.'
</td>
</tr>';
$rank++;
}
My question is simple, so a person can see inside the game at which rank he is.
"How to select a rank from a udid, outside of the loop".
Perhaps making a new query which selects the rank from a udid, or set up a variable from in the loop?
Here's how to query the database for the rank for player udid 1:
SELECT COUNT(*) AS rank
FROM tablename
WHERE score > (SELECT score FROM tablename WHERE udid = 1)
I'm assuming a higher score is better and you're only storing the highest score for each player.
With an index on score and udid, this should be pretty speedy.
What the query does is count the number of users who have a greater score than the selected user.
If a lower score is better, simply change it to this:
SELECT COUNT(*) AS rank
FROM tablename
WHERE score < (SELECT score FROM tablename WHERE udid = 1)
Which counts the number of players with a lower score.
Read all about MySQL indexes.
One option is writing a function that calculate the user's rank based on all the other's scores and returning it.
Then , calling it when the user wants to see his rank.
As far as I know , MMORPG games (for instance) have a rank field in their DB for each
player and they use a daily or hourly cron-job to update that rank.
So instead of running the 'calculate-the-rank' function each and every time a player
just want to see his rank, you'll just need to fetch the value of the field.
EDIT: code for the function (don't forget to add the rank field to your DB table)
File:
cronjob_update_rank.php:
require 'config.php'; //Or whatever contains your config and DB connection.
$rank = 1; //The best player ranked as 1
$getMembers = mysql_query("SELECT id FROM members ORDER BY `score` DESC");
while($mem = mysql_fetch_array($getMembers))
{
mysql_query("UPDATE members SET rank='$rank' WHERE id='{$mem['id']}'");
$rank++;
}
Please notice , it's just a sample code , you'll need to customize it for your needs.
Use your server panel to create a daily cronjob on that file.
(or ask for your hosting provider support with it).
I just need help refining this script to give me the values from both tables joined on the ID.
Basically I want the ID from both tables and then be able to get the other values from both tables based on the IDs (if need be) and display them in a loop.
The code I have is below but won't work.
$select = myQ("SELECT * FROM users a WHERE EXISTS (SELECT 1 FROM `videos` b WHERE a.id = b.id GROUP BY b.id HAVING count(*) > 1) ");
$i=0;
while ($row = myF($select)) {
$resultsLoopArray[$i]["videos.id"] = $row["id"];
$resultsLoopArray[$i]["videos.vid"] = $row["vid"];
$resultsLoopArray[$i]["users.username"] = $row["username"];
$i++;
}
if (isset($resultsLoopArray)) {
$tpl->Loop("searchResultsLoop", $resultsLoopArray);
}
For now all I need is the username from the users table, the id and video id from the video table.
Can someone help by chance?
you question is bit confusing me..
As for my understanding I am posting this soultion..
If you have two tables users , videos then .
$sql = "SELECT users.username , videos.* from users, videos where users.user_id = videos.user_id";
this query will fetch all record from users and videos table where user id is present in videos tables ...