Picking which data to view in group by - php

So yesterday, I am trying to sort data in groups made by Group by
I must select which data I want to show in those group
There is list of debts and each person may be in debt in the past but never have more than 1 unpaid debt
I need to know how many times how many times each user have been in debt before this last debt.
This is the column in the data base
Table "Users"
uid | name | date_of_birth
Table "Debt"
uid | debt_duration | paid_count | created_date
I end up with a hack like this in php
$res = mysql_query( "
SELECT * FROM Debt
JOIN Users
WHERE Users.uid = Debt.uid
ORDER BY created_date
GROUP BY Debt.uid");
while( $row = mysql_fetch_array( $res ) ){
$uid = $row['uid'];
$r = mysql_fetch_array( mysql_query("SELECT COUNT(*) FROM Debt WHERE uid = $uid") );
$previous_debts_count = $r[0];
}
This script is quite heavy but fortunately my client doesn't complain.
The script run at around 3 seconds top
But I need to know better ways to do this
sorry for the strange formatting, I am new here ...

I think the query you want is this:
SELECT Users.uid, COUNT(*) as cnt
FROM Debt JOIN
Users
ON Users.uid = Debt.uid
GROUP BY Debt.uid
ORDER BY created_date ;
Just loop through the results and don't use multiple queries for this. Check that Users.uid is the primary key on the users table. And add an index on debt(uid) to improve performance.

First of all, you should never use * in an SQL statement.
It makes the query highly vulnerable to SQL injection.
And I recommend you to use a PDO or a PHP framework.
Try this:
SELECT COUNT(Debt.uid) AS users
FROM Debt
LEFT JOIN Users
ON Users.uid = Debt.uid
GROUP BY Debt.uid

Related

PHP SQL Query to get the most common value in the table

I'm trying to have my query count the rows and have it return the most common name in that list, then from that it counts how many times that name appears and outputs the name and the amount of times its there
This is the code I'm using:
$vvsql = "SELECT * FROM votes WHERE sid=? ORDER BY COUNT(*) DESC LIMIT 1";
$vvresult = $db->prepare($vvsql);
$vvresult->execute(array($_GET['id']));
$vvcount = $vvresult->rowCount();
foreach ($vvresult->fetchAll(PDO::FETCH_ASSOC) as $row) {
echo $row['username'];
echo $vvcount;
}
However, it just displays the first username in the table and counts up the entire table. I'm pretty new to this so I'm sorry if this is a bad post or if it didn't make much sense.
You would seem to want:
SELECT name, COUNT(*) as cnt
FROM votes
GROUP BY name
ORDER BY COUNT(*) DESC
LIMIT 1;
Note the GROUP BY. You may also want to filter by sid but your question makes no mention of that.
select username, count(*) as c
FROM votes
GROUP BY username
ORDER BY c DESC

codeigniter poll module using hmvc

I'm trying create a poll module within codeigniter using hmvc
I'm trying to return the count as percentages against the amount of user_id's that have voted into the candidate_id
I'm using a so called perfectcontroller and perfectmodel
where I can return the count on the number of rows each candidate has a user_id
but I have seven candidate_id's so getting the percentages is a little tricky
but its also tricky trying to get the count to match up against the id its counting
I do not know if this is the right way to go about it or if I should be doing it another way any suggestions would help
thanks
<?php
function get_result(){
$query = $this->get_where_custom('candidate_id',$candidate_id);
$num_rows = $query->num_rows();
//returns the amount of user votes
foreach($query->result() as $row){
$candidate_id = $row->candidate_id;
}
}
?>
No matter if you have 7 or 77 candidates. Votes are counting. So if you have only one vote in DB, it means 100% for one candidate and 0% for rest of them. I don't know what do you consider by perfectcontroller and perfectmodel. But your table could be like
election_id | voter_id | candidate_id
your query would be like
$this->db->query("SELECT `e`.`candidate_id`, COUNT(1) AS `total`, COUNT(1) / `t`.`cnt` * 100 AS `percentage` FROM `elections` `e` CROSS JOIN (SELECT COUNT(1) AS `cnt` FROM `elections`) `t` GROUP BY `e`.`candidate_id`;");

SQL Order By id and Count star not working

I would like to get number of all records and get last record :
$sql_count_sms = "SELECT count(*) as total,content,id FROM android_users_sms WHERE user_id=$id ORDER BY id DESC";
$result_count_sms = mysql_query($sql_count_sms);
$row_num_sms = mysql_fetch_assoc($result_count_sms);
$num_sms = $row_num_sms['total'];
$last_my_sms = $row_num_sms['content'];
I can get number of records but I can't get last content record .
It returns first record !
Where is my wrong ?
Below codes works fine, but I think count(*) is faster than mysql_num_rows .
$sql_count_sms = "SELECT content,id FROM android_users_sms WHERE user_id=$id ORDER BY id DESC";
$result_count_sms = mysql_query($sql_count_sms);
$row_num_sms = mysql_fetch_assoc($result_count_sms);
$num_sms = mysql_num_rows($result_count_sms);
$last_my_sms = $row_num_sms['content'];
Any solution?
The grain of the two results you want is not the same. Without using a sub-query you can't combine an aggregate and a single row into the same result.
Think of the grain as the base unit of the result. The use of GROUP BY and aggregate functions can influence that "grain"... one result row per row on table, or is it grouped by user_id etc... Think of an aggregate function as a form of grouping.
You could break it out into two separate statements:
SELECT count(*) as total FROM android_users_sms WHERE user_id = :id;
SELECT * FROM android_users_sms WHERE user_id = :id ORDER BY id DESC LIMIT 1;
Also, specific to your question, you probably want a LIMIT 1 in combination with the ORDER BY to get just the last row.
Now, counter intuitively perhaps, this should also work:
SELECT count(*), content, id
FROM android_users_sms
WHERE user_id = :id
GROUP BY id, content
ORDER BY id
LIMIT 1;`
This is because we've changed the "grain" with the GROUP BY. This is the real nuance and I feel like this could probably be explained better than I am doing now.
You could also do this with a sub query like so:
SELECT aus.*,
(SELECT count(*) as total FROM android_users_sms WHERE user_id = :id) AS s1
FROM android_users_sms AS aus
WHERE user_id = :id ORDER BY id DESC LIMIT 1;

How to select all from MySQL table where column between dates and order by column

I am working on a project to build an assessment leader board for a learning centre. I've got a table that looks like this
I am trying to write a query which will select all from the table by CLASS, within DATE range, add score per individual STUDENT_ID and then order in descending order by the added score to create the leader board. I've read a little on sub queries but can't quite understand the examples or exactly how they work, I also think I would need a SELECT DISTINCT student_id in my query but my knowledge here is also limited as I have only used it once.
Anyway this is what I have so far.
$classcheck = mysql_query("SELECT *
FROM assessment
WHERE class = '$class_info'
order by score DESC")
or die(mysql_error());
if(mysql_num_rows($classcheck) > 0){
while($row = mysql_fetch_array($classcheck)){
if(strtotime($row["date"]) > strtotime($fromdate) && strtotime($row["date"]) < strtotime($todate)){
echo $row['score'].'<p>';
}
}
}
But I need it to add SCORE and order by the added SCORE in the query somewhere which I cannot achieve with what I have written.
I know should start using PDO rather than mysql_query, knowledge limited again and I am running out of time. All feedback would be greatly appreciated. OH, and the score is really a percentage.
You don't need a subquery, you just need SUM and GROUP BY to total the scores by student, and a WHERE clause to restrict the dates.
SELECT student_name, SUM(score) AS total_score
FROM assessment
WHERE date BETWEEN '$fromdate' AND '$todate'
AND class = '$class_info'
GROUP BY student_id
ORDER BY total_score DESC
I think a GROUP BY might do the trick since you are trying to add up all the scores of an individual STUDENT_ID.
Please feel free to correct me if I'm wrong but the following SQL should get you what you are looking for.
SELECT SUM(score) AS ttl_score, student_name
FROM assessment
WHERE class='$class_info'
AND date>='$start' AND date<='$end'
GROUP BY student_id ORDER BY ttl_score DESC;
$classcheck = mysql_query("Select Student_id, sum(Score) as SummedScore from assessment where class='$class_info' and
date between '$fromdate' and '$todate' Group by Student_ID Order By SummedScore"
or die(mysql_error());
if(mysql_num_rows($classcheck) > 0){
while($row = mysql_fetch_array($classcheck)){
echo $row['SummedScore'].'<p>';}
}

Slow queries with MySQL large database

I have a table ipaddresses that contains IP ranges. Columns are: ipStart, ipEnd
Example of
ipStart: 3579374832
ipEnd: 3579374839
Now I want to select 300ish IP addresses from another table: visit_count. And based on those 300 addresses I want to check if the ip address is in the range of any of IPs in the table ipaddresses
My current code:
$results = $database->query("SELECT user_id,
DATE_FORMAT(last_visit, '%Y-%m-%d') as datumet,
cookieId, ipaddress
FROM `visit_count`
WHERE last_visit BETWEEN
'$firstDayOfMonth' AND '$lastDayOfMonth'
AND user_id = '$userId'
GROUP BY cookieId
ORDER BY last_visit ASC");
$rows = $database->loadObjectList($results);
foreach ( $rows as $row ) {
$ipaddressLong = ip2long($row->ipaddress);
// This is where its very very slow
$selO = $database->query("SELECT ipStart, ipEnd FROM `ipaddresses`
WHERE '$ipaddressLong' BETWEEN `ipStart` AND `ipEnd`");
$rowO = $database->getrow($selO);
}
The result is a really slow query that consumes a lot of cpu.
ipaddresses has indexes for both ipStart and ipEnd and contains around 50k rows.
How can I make this go faster?
A quick and dirty change to a single query using a JOIN:-
SELECT a.ipStart, a.ipEnd
FROM `ipaddresses` a
INNER JOIN
(
SELECT user_id, DATE_FORMAT(last_visit, '%Y-%m-%d') as datumet, cookieId, inet_aton(ipaddress ) AS ipaddressLong
FROM `visit_count`
WHERE last_visit BETWEEN '$firstDayOfMonth' AND '$lastDayOfMonth'
AND user_id = '$userId'
GROUP BY cookieId
) b
ON b.ipaddressLong BETWEEN a.ipStart AND a.ipEnd
Note that this could probably be simplified. However it depends on your use of GROUP BY cookieId . At the moment this is going to get one row per cookie id, but which row is undefined (ie, could be any user_id, ip address and last visit date that goes with that cookie id).
You can simplify the first select. You don't need to select any fields beyond ipaddress. You also don't need to order or group the result. If you want unique ipaddresses, just use distinct
select distinct ipaddress
from visit_count
where last_visit between '$firstDayOfMonth' and '$lastDayOfMonth'
and user_id = '$userId'
300 selects don't seem to be much, even with the second table having 50k rows. On my machine, even without indexes, it takes only a fraction of a second.
If you want to simplify it anyway, you can join the two tables with
select ipstart, ipend
from ipaddresses i
join visit_count v on inet_aton(v.ipaddress) between i.ipstart and i.ipend
where v.last_visit between '$firstDayOfMonth' and '$lastDayOfMonth'
and v.userid = '$userId'
1) Use better query like other suggested to you
2) Have you defined indexes for the field you need ?
3) Dont know if you use mysql or mysqli...anyway use fetch_assoc that is faster then fetch_array
with this 3 trick you should be ok

Categories