How to select users with most votes, from a database? - mysql

I have a small system that accepts votes, on my query I'm selecting the vote(name of user) and the total of votes for this user.
My Idea is to only display the user with more votes, with the query that i have I know that i can add ORDER BY and get the first value which will be the one with the highest umber of votes.
But i'm trying to identifie if two persons have the number of votes. any ideas how can i do that?
I'm using cgi.
my $connection = $MY_CONNECTION->prepare("SELECT voto, COUNT(*) total FROM votos WHERE mes = 12 GROUP BY vote HAVING COUNT(*) > 0 ORDER BY COUNT(*)");
$connection->execute || print "ERROR 1";
my #resultados;
my $info = '';
while ($info = $connection->fetchrow_hashref()) {
my $nombre = $info->{voto};
my $totalVotos = $info->{total};
my $winner = "";
my $temp2 = "";
my $temp = $totalVotos ;
if ($temp2 => $temp) {
$temp2 = $totalVotos ;
$winner = $nombre ;
}
print "<p><strong>Winner: </strong> $winner </p> ";
print "<hr>";
}

You can return everyone who has the highest number of votes (and how many votes) with:
select voto, count(*) as total from votos
where mes = 12
group by voto -- I assume "vote" in your question was typo
having total = (
select max(ct) from (
select voto,count(*) as ct from votos group by voto
)
)
and total > 0 -- from your original but not sure it can fail
;
You may also be able to use a rank() function.
Without modifying your SQL, you could change the perl to something like:
# ...
# initialisation shouldn't be inside while loop
my $winner = "";
my $temp2 = "";
while ($info = $connection->fetchrow_hashref()) {
my $nombre = $info->{voto};
my $totalVotos = $info->{total};
if ($temp2 < $totalVotos) { # your original "=>" seems wrong
$temp2 = $totalVotos;
#winner = $nombre;
elsif ($temp2 == $totalVotos) {
push #winner, $nombre;
}
}
# postprocessing shouldn't be inside while loop
$winner = join(", ", #winner); # or whatever
print "<p><strong>Winner: </strong> $winner </p> ";
print "<hr>";

use subquery then apply order by, to limit the result by 2 add limit 2.
select t1.voto, t1.ct as total from (
SELECT voto, count(1) ct FROM votos
WHERE mes = 12
GROUP BY voto
HAVING COUNT(1) > 1) t1
order by t1.total desc limit 2

I would recommend rank():
SELECT voto, total
FROM (SELECT voto, COUNT(*) as total,
RANK() OVER (ORDER BY COUNT(*) DESC) as seqnum
FROM votos v
WHERE mes = 12
GROUP BY voto
) v
WHERE seqnum = 1;
Note that HAVING COUNT(*) > 0 does nothing. In your query, it is not possible for COUNT(*) to be equal to or less than 0.

Related

Why integer cast is not working with integer group_concat() list?

I'm stuck at the query where I need to concat IDs of the table. And from that group of IDs, I need to fetch that rows in sub query. But when I try to do so, MySQL consider group_concat() as a string. So that condition becomes false.
select count(*)
from rides r
where r.ride_status = 'cancelled'
and r.id IN (group_concat(rides.id))
*************** Original Query Below **************
-- Daily Earnings for 7 days [Final]
select
group_concat(rides.id) as ids,
group_concat(ride_category.name) as rideType,
group_concat(ride_cars.amount + ride_cars.commission) as rideAmount ,
group_concat(ride_types.name) as carType,
count(*) as numberOfRides,
(
select count(*) from rides r where r.ride_status = 'cancelled' and r.id IN (group_concat(rides.id) )
) as cancelledRides,
(
select count(*) from rides r where r.`ride_status` = 'completed' and r.id IN (group_concat(rides.id))
) as completedRides,
group_concat(ride_cars.status) as status,
sum(ride_cars.commission) + sum(ride_cars.amount) as amount,
date_format(from_unixtime(rides.requested_at/1000 + rides.offset*60), '%Y-%m-%d') as requestedDate,
date_format(from_unixtime(rides.requested_at/1000 + rides.offset*60), '%V') as week
from
ride_cars,
rides,
ride_category,
ride_type_cars,
ride_types
where
ride_cars.user_id = 166
AND (rides.ride_status = 'completed' or. rides.ride_status = 'cancelled')
AND ride_cars.ride_id = rides.id
AND (rides.requested_at >= 1559347200000 AND requested_at < 1561852800000)
AND rides.ride_category = ride_category.id
AND ride_cars.car_model_id = ride_type_cars.car_model_id
AND ride_cars.ride_type_id = ride_types.id
group by
requestedDate;
Any solutions will be appreciated.
Try to replace the sub-query
(select count(*) from rides r where r.ride_status = 'cancelled' and r.id IN (group_concat(rides.id) )) as cancelledRides,
with below to count using SUM and CASE, it will make use of the GROUP BY
SUM(CASE WHEN rides.ride_status = 'cancelled' THEN 1 ELSE 0 END) as cancelledRides
and the same for completedRides
And move to using JOIN instead of implicit joins

How to group customers into 4 ranks using MYSQL query

We store the customer data like name, userId and Total Amount they spent on different orders they place, now I want to group a customer into ranks 1 to 4 based on Total Amount he spent till now. Below is the script I am using but it takes a lot of time, is there any better way to do this?? There is no index on dateCreate Field.
public function getBiggestSpenders($customerUserId){
global $db, $database;
$sql = "SELECT userId, SUM(total) AS Total, ORD.dateCreate
FROM $database.`order` ORD
WHERE year(ORD.dateCreate) >= '2013'
group by ORD.userId
order by Total DESC";
$result = $db->getTable($sql);
$numRows = count($result);
$flag=0;
for($i=0;$i<$numRows && $flag==0;$i++){
$userId = $result[$i]['userId'];
if($userId==$customerUserId){
$position = $i;
$Total = $result[$i]['Total'];
$flag=1;
}
}
$quartile = $this->getQuartiles($numRows, $position);
if($quartile==1)
return $quartile;
else
return 0;
}
public function getQuartiles($numRows, $position){
$total = $numRows;
$segment = round($total / 4);
$Quartile = floor($position / $segment) + 1;
return $Quartile;
}
Thanks!
To improve the speed, you can create an index on dateCreate column and use the following condition to make MySQL use it:
WHERE ORD.dateCreate >= '2013-01-01'
As far as grouping is concerned, you can use CASE statement to define groups based on spending, e.g.:
SELECT userId, SUM(total) AS Total,
CASE
WHEN Total >= 2000 then 1
WHEN Total >= 1000 AND Total <2000 THEN 2
WHEN Total >=500 AND Total < 1000 THEN 3
ELSE 4
END as `rank`,
ORD.dateCreate
FROM $database.`order` ORD
WHERE ORD.dateCreate >= '2013-01-01'
group by ORD.userId
order by Total DESC

How do I determine the difference between values in database column and return the smallest difference?

I've got a column of DATETIME values in MySQL and I'm looking to return the smallest difference between any two of those values in the column; I don't need to know what values the difference was between or when it was, just the difference between these two values alone.
My table looks similar to this.
id | launch_date_time
----------------------------
1 | 2012-01-02 18:42:00
2 | 2012-04-05 07:23:50
...
x | 2014-08-07 22:19:11
Would anyone be able to point me in the correct direction for constructing such a query?
My first idea is, if your table name is table do this
select min( abs( datediff(t1.launch_date_time, t2.launch_date_time) ) )
from table t1
, table t2
where t1.id <> t2.id
it depends how big those tables are, the above is O(N^2) solution, in O(N * log N) you can do this by sorting and result is min of consecutive elements
// pseudo code
list = fromDb();
sort(list);
min = list[1] - list[0];
for i in 2 to list.size()
min = min( min, list[i] - list[i-1] )
select id,FROM_UNIXTIME(launch_date_time) as uTime,max(FROM_UNIXTIME(launch_date_time)) as max from table;
//do db stuff
//set the lowest to highest possible number so first comparison it will become the lowest.
$lowest = $result['max'];
// iterate through array to get each time
foreach ($result['uTime'] as $v)
{
//iterate again to compare each time u got from first loop to each in this loop
foreach ($result['uTime as $x)
{
//subtract your first value from the current
$diff = $v - $x;
//if its lower then any previous, and its positive set the new lowest.
if ($diff < $lowest and $diff > 0)
{
$lowest = $diff;
}
}
}
echo $lowest;
havent tested this....
SQL method of doing it in a single statement (difference here in days):-
SELECT MIN(DATEDIFF(Sub1.launch_date_time, Sub2.launch_date_time))
FROM
(
SELECT id, launch_date_time, #aCnt1 := #aCnt1 + 1 AS SeqCnt
FROM SomeTable
CROSS JOIN
(
SELECT #aCnt1:=0
) Deriv1
ORDER BY launch_date_time
) Sub1
INNER JOIN
(
SELECT id, launch_date_time, #aCnt2 := #aCnt2 + 1 AS SeqCnt
FROM SomeTable
CROSS JOIN
(
SELECT #aCnt2:=1
) Deriv2
ORDER BY launch_date_time
) Sub2
ON Sub1.SeqCnt = Sub2.SeqCnt
SQL Fiddle here:-
http://www.sqlfiddle.com/#!2/6dc399/1
Try this query -
SELECT
t1.launch_date_time, MIN(t2.launch_date_time) launch_date_time2
FROM launch t1
JOIN launch t2
ON t2.launch_date_time > t1.launch_date_time
GROUP BY
t1.launch_date_time
ORDER BY
DATE(MIN(t2.launch_date_time)) * 86400 + TIME_TO_SEC(TIME(MIN(t2.launch_date_time))) - DATE(t1.launch_date_time) * 86400 + TIME_TO_SEC(TIME(t1.launch_date_time))
LIMIT 1

Check order of rows in mysql

Need some help with mysql.
I have a table full of bids, i need to check if there are more than 3 bids from the same user that are one after another.
Here is some code for example:
$all = sql_get('select all bids from bids where auction_id = 1 order by amount asc');
$total = 0;
foreach ($all as $bid) {
if ($bid->user_id == $user->id) {
if (++$total <= 3) continue;
$bid->burned = true;
sql_store($bid);
show_error('you cant have more than 4 bids one after another, the last bid was burned.');
} else {
$total = 0;
}
}
Is there a way to do that in a single query ?
SELECT user_id, CASE WHEN #user_id = user_id
THEN #rownum := #rownum + 1
ELSE ((#user_id := user_id) * 0) + (#rownum := 1) END AS rownum
FROM ( SELECT user_id
FROM bids, (SELECT #rownum := 0, #user_id := NULL) AS vars
WHERE auction_id = 1
ORDER BY created DESC) AS h
HAVING user_id = '{$user->id}' AND rownum >= 3
Simply parse user_id into query and you will know if there are 3 in a row. This asumes that you save the time the rows were created with the created column.
Why not just do a count query and pass the user_id in the WHERE clause?
$query = "SELECT COUNT(bids) AS total_bids FROM bids WHERE auction_id = 1 AND user_id = '$user->id' ORDER BY amount";

Get last X rows using LIMIT and ORDER BY col ASC

This query is to retrieve some messages, it retuns all of them:
$q = "
SELECT *
FROM pms
WHERE
(
(id_to = $id and id_from = ".sesion().")
OR
(id_from = $id and id_to = ".sesion().")
)
AND (id > $from)
ORDER by fecha ASC";
The thing is i would like to get the last 50 elements, but I think its ony posible using DESC ordering..
how can i do it?
Do i need to count first how many rows so then can I use LIMIT $many-$ipp,$many ? or is there a way to invert the result order?
Just make your query a subquery:
SELECT * FROM
(
SELECT *
FROM pms
WHERE
(
(id_to = $id AND id_from = ".sesion().")
OR (id_from = $id and id_to = ".sesion().")
)
AND id > $from
ORDER BY fecha DESC
LIMIT 50
) q1
ORDER BY fecha ASC