GROUP BY not working well - php

Hello i have a table with:
id_order | id_order_history | date_add
With this query i can get all orders that have id_order_history = 3 (in preparation)
SELECT
`oh`.`id_order`
FROM (
SELECT MAX( `date_add` ) AS date_add
FROM `'._DB_PREFIX_.'order_history`
GROUP BY `id_order`
) AS h
INNER JOIN
'._DB_PREFIX_.'order_history AS oh
ON
`h`.`date_add` = `oh`.`date_add`
INNER JOIN
'._DB_PREFIX_.'orders AS o
ON
`oh`.`id_order` = `o`.`id_order`';
$sql .= 'WHERE
oh.`id_order_state` = '.$status.'';
$sql .= ' AND
oh.date_add >= "'.$start.'"';
$sql .= ' AND
oh.date_add <= "'.$end.'"
ORDER BY
`oh`.`id_order` ASC
The problem is that for some order the SELECT MAX don't get the correct value of date_add
id_order | id_order_history | date_add
1 | 5 | 25-09-2014 18:07:59 ---> this is the correct value
1 | 4 | 25-09-2014 18:07:51
1 | 3 | 25-09-2014 18:06:42 ---> but query return this
If I add on date 1 second (18:06:43) the query return the correct value 25-09-2014 18:07:59
I just can not figure out how to solve the problem.
I have read around that sometimes GROUP BY takes results higgledy-piggledy ...
how can I fix it?

Convert your column date_add to a ISO datetimestring and/or use TO_SECONDS (see MySQL doc here)
...( SELECT FROM_UNIXTIME(MAX(TO_SECONDS(`date_add`)) ) AS date_add
FROM `'._DB_PREFIX_.'order_history`
GROUP BY `id_order` ) AS h

Related

do not print duplicate entries from php array [duplicate]

I have a MySQL table which contains the following type of information:
Date product
2011-12-12 azd
2011-12-12 yxm
2011-12-10 sdx
2011-12-10 ssdd
Here is an example of a script I use to get data from this table:
<?php
$con = mysql_connect("localhost","username","password");
if (!$con)
{
die('Could not connect: ' . mysql_error());
}
mysql_select_db("db", $con);
$sql=mysql_query("SELECT * FROM buy ORDER BY Date");
while($row = mysql_fetch_array($sql))
{
echo "<li><a href='http://www.website/". $row['Date'].".html'>buy ". date("j, M Y", strtotime($row["Date"]))."</a></li>";
}
mysql_close($con);
?>
This script displays every date from the table, e.g.
12.dec 2011
12.dec.2011
10.dec.2011
10.dec.2011
I would like to only display unique dates, e.g.
12.dec.2011
10.dec.2011
Use the DISTINCT operator in MySQL:
SELECT DISTINCT(Date) AS Date FROM buy ORDER BY Date DESC;
use
SELECT DISTINCT Date FROM buy ORDER BY Date
so MySQL removes duplicates
BTW: using explicit column names in SELECT uses less resources in PHP when you're getting a large result from MySQL
Use this query to get values
SELECT * FROM `buy` group by date order by date DESC
The rest are almost correct, except they should order by Date DESC
SELECT DISTINCT(Date) AS Date FROM buy ORDER BY Date DESC;
DISTINCT is always a right choice to get unique values. Also you can do it alternatively without using it. That's GROUP BY. Which has simply add at the end of the query and followed by the column name.
SELECT * FROM buy GROUP BY date,description
Another DISTINCT answer, but with multiple values:
SELECT DISTINCT `field1`, `field2`, `field3` FROM `some_table` WHERE `some_field` > 5000 ORDER BY `some_field`
Use something like this in case you also want to output products details per date as JSON and the MySQL version does not support JSON functions.
SELECT `date`,
CONCAT('{',GROUP_CONCAT('{\"id\": \"',`product_id`,'\",\"name\": \"',`product_name`,'\"}'),'}') as `productsJSON`
FROM `buy` group by `date`
order by `date` DESC
product_id product_name date
| 1 | azd | 2011-12-12 |
| 2 | xyz | 2011-12-12 |
| 3 | ase | 2011-12-11 |
| 4 | azwed | 2011-12-11 |
| 5 | wed | 2011-12-10 |
| 6 | cvg | 2011-12-10 |
| 7 | cvig | 2011-12-09 |
RESULT
date productsJSON
2011-12-12T00:00:00Z {{"id": "1","name": "azd"},{"id": "2","name": "xyz"}}
2011-12-11T00:00:00Z {{"id": "3","name": "ase"},{"id": "4","name": "azwed"}}
2011-12-10T00:00:00Z {{"id": "5","name": "wed"},{"id": "6","name": "cvg"}}
2011-12-09T00:00:00Z {{"id": "7","name": "cvig"}}
Try it out in SQL Fiddle
If you are using a MySQL version that supports JSON functions then the above query could be re-written:
SELECT `date`,JSON_OBJECTAGG(CONCAT('product-',`product_id`),JSON_OBJECT('id', `product_id`, 'name', `product_name`)) as `productsJSON`
FROM `buy` group by `date`
order by `date` DESC;
Try both in DB Fiddle
Depends on what you need.
In this case I suggest:
SELECT DISTINCT(Date) AS Date FROM buy ORDER BY Date DESC;
because there are few fields and the execution time of DISTINCT is lower than the execution of GROUP BY.
In other cases, for example where there are many fields, I prefer:
SELECT * FROM buy GROUP BY date ORDER BY date DESC;
There is a specific keyword for the achieving the same.
SELECT DISTINCT( Date ) AS Date
FROM buy
ORDER BY Date DESC;

How to join and sum a column in two tables based on another column using mysqli and php (just using query)

I have 2 tables about blood bank:
donates
orders
in donates table I have 2 fields showing how many donations we have:
------------------------
| blood_group | amount |
------------------------
| A+ | 2 |
| B- | 3 |
| O+ | 4 |
| A+ | 3 |
| O+ | 1 |
in orders table I have 2 column that how many requests we submit based on blood group:
------------------------
| blood_group | amount |
------------------------
| A+ | 4 |
| B- | 3 |
| O+ | 4 |
| AB- | 6 |
My problem is I want to use mysqli query to get an array that show me this result based on these conditions:
show how many we need group by blood_group
if we don't need any blood_group or we don't have any request for that blood type show zero (not showing null)
not showing negative number for our blood shortage
I manage to do this so far:
<?php
$con = mysqli_connect("localhost", "root", "", "test");
// Check connection
if (mysqli_connect_errno()) {
echo "Failed to connect to MySQL: " . mysqli_connect_error();
}
$sql ="SELECT donates.blood_group as blood_group,
donates.amount as donates_amount,
orders.amount as orders_amount,
FROM `donates`
LEFT JOIN `orders`
ON donates.blood_group = orders.blood_group
GROUP BY donates.blood_group";
// Perform queries
$result = mysqli_query($con, $sql);
if (!$result = mysqli_query($con, $sql)) {
echo "SQLSTATE error: " . mysqli_sqlstate($con);
echo "<br>";
echo "SQLSTATE error: " . mysqli_error($con);
exit;
}
$result = mysqli_fetch_all($result, MYSQLI_ASSOC);
var_dump($result);
mysqli_close($con);
That query shows me sum of blood_groups but here is the main question:
So here are the main questions:
how to subtract (donates_amount and orders_amount)
how to make them positive (subtract which one first)
how to show the result even if one blood group is not presented on the other (full join)
Use union all and group by:
select blood_group, sum(donate_amount) as donate_amount,
sum(order_amount) as order_amount
from ((select blood_group, amount as donate_amount, 0 as order_amount
from donates
) union all
(select blood_group, 0 as donate_amount, amount as order_amount
from orders
)
) od
group by blood_group;
The only caveat is that a blood group needs to be in one of the tables. If you have a separate table of all of them, you should use that. For instance:
select bg.*,
coalesce(donate_amount, 0) as donate_amount,
coalesce(order_amount, 0) as order_amount
from blood_groups bg left join
(select blood_group, sum(amount) as donate_amount
from donates
group by blood_group
) d
on d.blood_group = bg.blood_group left join
(select blood_group, sum(amount) as order_amount
from donates
group by blood_group
) o
on o.blood_group = bg.blood_group ;
In either of these queries, you can get the difference using - and show negative numbers as 0 using greatest(). For instance:
greatest(sum(donate_amount) - sum(order_amount), 0)
To answer your first question :
how to subtract (donates_amount and orders_amount)
You must use SUM() function with a minus sign:
SUM(donates.amount - orders.amount);
this will subtract the total sum of two tables
But we have some problem here: you may have null values (because you may not have some of the blood groups present in one of tables) that give the wrong result. you must change the null values to zero with COALESCE() function:
SUM(COALESCE(donates.amount,0) - COALESCE(orders.amount,0))
We must extra check if the result does not equal to null:
COALESCE(SUM(COALESCE(donates.amount,0) - COALESCE(orders.amount,0)),0)
how to make them positive (subtract which one first)
And at last if you want to avoid negative numbers you must use mysqli math functions named ABS() that give you absulute value:
ABS(COALESCE(SUM(COALESCE(donates.amount,0) - COALESCE(orders.amount,0)),0))
so your query will look like this:
$sql = "SELECT donates.blood_group as blood_group,
COALESCE(donates.amount,0) as donates_amount,
COALESCE(orders.amount,0) as orders_amount,
ABS(COALESCE(SUM(COALESCE(donates.amount,0) - COALESCE(orders.amount,0)),0)) as needed_amount
FROM `donates`
LEFT JOIN `orders`
ON donates.blood_group = orders.blood_group
GROUP BY donates.blood_group";
how to show the result even if one blood group is not presented on
the other (full join)
In order to make full join you must use union with the invers form of your query. so that you find other records in orders table and unite the results into one results:
$sql = "SELECT donates.blood_group as blood_group,
COALESCE(donates.amount,0) as donates_amount,
COALESCE(orders.amount,0) as orders_amount,
ABS(COALESCE(SUM(COALESCE(donates.amount,0) - COALESCE(orders.amount,0)),0)) as needed_amount
FROM `donates`
LEFT JOIN `orders`
ON donates.blood_group = orders.blood_group
GROUP BY donates.blood_group
UNION
SELECT orders.blood_group as blood_group,
COALESCE(donates.amount,0) as donates_amount,
COALESCE(orders.amount,0) as orders_amount,
ABS(COALESCE(SUM(COALESCE(orders.amount,0) - COALESCE(donates.amount,0)),0)) as needed_amount
FROM `orders`
LEFT JOIN `donates`
ON orders.blood_group = donates.blood_group
GROUP BY orders.blood_group";

MYSQL: closest to supplied date grouped by user_id

I need to get multiple rows with a date_added closest to but not past a user supplied date, grouped by user_id.
I've looked at a bunch of max in group type answers but I'm not quite there:
Get nearest records to specific date grouped by type
SQL Query to show nearest date?
Find closest datetime to specified datetime in mysql query
Get closest date from MySQL table
https://dev.mysql.com/doc/refman/5.0/en/example-maximum-column-group-row.html
This is close: https://stackoverflow.com/a/17038667/5319244. Finds the max date though, I need the max specified by user input, not max outright.
Here's a subset of the data with the correct organisation_id, framework_id and level_id already filtered.
+----+---------+-----------------+--------------+----------+---------------------+
| id | user_id | organisation_id | framework_id | level_id | date_added |
+----+---------+-----------------+--------------+----------+---------------------+
| 2 | 1 | 2 | 1 | 1 | 2015-07-31 14:02:49 |
| 9 | 2 | 2 | 1 | 1 | 2015-09-01 11:05:09 |
| 11 | 1 | 2 | 1 | 1 | 2015-09-07 14:13:39 |
+----+---------+-----------------+--------------+----------+---------------------+
If the supplied date is 2015-09-07. I'd expect to see id's: 9 and 11.
If the supplied date is 2015-09-01. I'd expect to see id's: 2 and 9.
If the supplied date is 2015-07-31. I'd expect to see id: 2.
This query is as close as I got:
SELECT t1.id
, t1.user_id
, t1.date_added
FROM
completed_frameworks AS t1
WHERE date_added = (
SELECT
MAX(date_added)
FROM
completed_frameworks
WHERE
user_id = t1.user_id
AND
date_added <= '2015-09-07 23:59:59'
)
AND
(
t1.organisation_id = 2
AND
t1.framework_id = 1
AND
t1.level_id = 1
)
It returns what I expect for the date: 2015-09-07
When the date is 2015-09-01 however it only returns id 9. Not also 2 as I'd expect.
When the date is 2015-07-31 it returns 0 rows..
Let me know if I there's anything else I can provide.
Cheers!
EDIT:
Thanks for the replies thus far. I need to clarify two points:
1) I don't have a limit. I'm expecting those rows due to the user id's. There could be n users returned. I just want a row for each user where the date_added is closest to the user supplied date.
2) The date supplied will not have a time value. It will be from a simple datepicker UI. In my example query I've added the time of 23:59:59 to encompass all of that day.
Try this subquery instead. The organisation_id/framework_id/level_id filter is moved into the subquery and it now returns the right values for the examples you've given.
SELECT t1.id, t1.user_id, t1.date_added
FROM
completed_frameworks AS t1
WHERE date_added = (
SELECT
MAX(date_added)
FROM
completed_frameworks
WHERE
user_id = t1.user_id
AND
date_added <= '2015-09-01 23:59:59'
AND
organisation_id = 2
AND
framework_id = 1
AND
level_id = 1
)
Here is a sample query you could use for an input date of '2015-09-07 14:13:39'. The inner query returns the input date along with the next highest date. This temporary table is then used to filter completed_frameworks to give you only records from the two dates most recent to the input date.
SELECT t1.id, t1.user_id, t1.date_added
FROM completed_frameworks t1
INNER JOIN
(
SELECT cf.date_added
FROM completed_frameworks cf
GROUP BY cf.date_added
HAVING cf.date_added <= '2015-09-07 14:13:39'
ORDER BY cf.date_added DESC
LIMIT 2
) t2
ON t1.date_added = t2.date_added
WHERE t1.organisation_id = 2 AND t1.framework_id = 1 AND t1.level_id = 1
I think this does what you want:
SELECT f.id, f.user_id f.date_added
FROM completed_frameworks f
WHERE date(f.date_added) <= '2025-09-07' -- or whatever your date is
ORDER BY f.date_added DESC
LIMIT 2;
EDIT:
If you want one date per user id that is closest to the specified date, I would suggest:
select f.*
from completed_frameworks f join
(select user_id, max(date_added) as maxda
from completed_frameworks
where date(date_added) <= '2015-09-07'
group by user_id
) u
on f.user_id = u.user_id and f.date_added = maxda

How to left join selecting value with the highest user_id

well this question is related to my previous question How to left join 2 tables with SUM() and MAX() grouped by date
what i changed is i added user_id column (auto incremented) and want to select value with highest user_id per date
i have table loadhistory ( wanted to "select only value with highest user_id per date" group by and order by date DESC.)
so in this case, i want to select 150 for 2015-02-27 since it has the highest user_id in that date and 50 for 2015-02-28
| user_id | customer_id | date | bal |
1 1 2015-02-27 100
2 1 2015-02-27 150
3 1 2015-02-28 150
4 1 2015-02-28 50
and table transactionrecord (want to sum up values per date using SUM(bal) group by and order by date DESC)
| user_id |customer_id | date | bal |
1 1 2015-02-27 50
2 1 2015-02-27 20
3 1 2015-02-28 10
And i want to join the 2 tables which would look like this:
| date | balance | amount paid |
2015-02-28 50 10
2015-02-27 150 70
this is the code so far (i used the code i got from my previous question and edited it here in my new question hoping to arrive desired result but did not)
SELECT a.customer_id, a.date, (b.bal AS bal WHERE b.user_id = MAX(b.user_id) , a.paid
FROM (
SELECT customer_id, date, SUM(bal) AS paid
FROM transactionrecord
GROUP BY customer_id, date
) AS a LEFT JOIN loadhistory AS b
ON a.customer_id = b.customer_id AND a.date = b.date
WHERE a.customer_id = 1
GROUP BY a.customer_id, a.date, a.paid
ORDER BY a.date DESC
help please. thanks in advance
MySQL use the first row by group by, so you must order it before you can use group by like this:
SELECT * FROM (SELECT * FROM `loadhistory` ORDER BY user_id DESC) history GROUP BY date
So you can use the following query as solution:
SELECT h.date,h.bal as balance, t.amount as 'amount paid' FROM
(SELECT * FROM (SELECT * FROM `loadhistory` ORDER BY user_id DESC) history GROUP BY date) as h
JOIN
(SELECT SUM(bal) as amount, customer_id, date FROM `transactionrecord` GROUP BY date) as t
ON h.date = t.date AND h.customer_id = t.customer_id
ORDER BY date DESC

select one winner of highest score of every one month

I need to select highest scorer as a monthly winner. I want to show previous winners too. My current query selects only previous one month winner, but how can I select all previous monthly winners?
my query:
function month_winner_now()
{
$query = $this->db->query("SELECT winner.id,winner.score,winner.user_id,winner.date, user.id,user.username,user.email,user_profile.user_image,user_profile.hometown,user_profile.country FROM `winner` LEFT JOIN `user` ON user.id = winner.user_id LEFT JOIN `user_profile` ON user_profile.user_id = winner.user_id WHERE MONTH(CURDATE())= MONTH(winner.date) ORDER BY winner.score DESC
LIMIT 1");
return $query->result_array();
}
My current output :
"monthly winners":[
{
"id":"5",
"score":"1256",
"user_id":"5",
"date":"2014-03-05",
"username":"",
"email":"",
"user_image":"",
"hometown":"",
"country":""
}
But I need output like
"monthly winners":[
{
"id":"4",
"score":"233",
"user_id":"4",
"date":"2014-03-02",
"username":"Mahmudul Hasan Swapon",
"email":"",
"user_image":"",
"hometown":"",
"country":""
},
{
"id":"7",
"score":"123",
"user_id":"7",
"date":"2014-03-04",
"username":"Prosanto Biswas",
"email":"",
"user_image":"",
"hometown":"",
"country":""
}
],
Monthly winners json array shows previous all month winners but every month should have one winner.
DB table look like
id | name | userid | score | date |
------------------------------------------------------------
1 | john | 1 | 44 | 2013-03-2
2 | mary | 2 | 59 | 2013-03-5
3 | john | 12 | 38 | 2013-03-8
4 | elvis | 3 | 19 | 2013-02-10
5 | john | 11 | 1002 | 2013-01-11
6 | johnah | 10 | 200 | 2013-01-11
I recreated sql query and added one more field "month_of_year", now I think it will be helpful for you according to your requirement
SELECT
winner.id,winner.score,winner.user_id,winner.date,
user.id,user.username,user.email,user_profile.user_image,
user_profile.hometown,user_profile.country,
date_format( winner.date, '%Y-%m' ) AS month_of_year
FROM
`winner`
LEFT JOIN `user`
ON user.id = winner.user_id
LEFT JOIN `user_profile`
ON user_profile.user_id = winner.user_id
GROUP BY month_of_year
ORDER BY winner.score DESC
i want to show previous all month winner
You are checking for equality of month hence other months (previous or later) are omitted. Change comparison to <= or >= as the case may be.
And if you use LIMIT 1 there is a chance that other month details are not fetched if exists a record in current month.
Try this:
SELECT
winner.id -- etc fields
FROM
`winner`
LEFT JOIN `user`
ON user.id = winner.user_id
LEFT JOIN `user_profile`
ON user_profile.user_id = winner.user_id
WHERE
date_format( winner.date, '%Y%m' ) <= date_format( CURDATE(), '%Y%m' )
ORDER BY
winner.score DESC
For the winner from previous month:
$query = $this->db->query("
SELECT winner.id,winner.score,winner.user_id,winner.date, user.id,user.username,user.email,user_profile.user_image,user_profile.hometown,user_profile.country
FROM `winner`
LEFT JOIN `user`
ON user.id = winner.user_id
LEFT JOIN `user_profile`
ON user_profile.user_id = winner.user_id
WHERE MONTH(DATE_SUB(CURDATE(), INTERVAL 1 MONTH)) = MONTH(winner.date) ORDER BY winner.score DESC LIMIT 1
");
Try something like this query:
SELECT * FROM user
INNSER JOIN
(SELECT user_id, MAX(score) AS s, MONTH(date) AS d
FROM winner
GROUP BY MONTH(date)) monthlyWinner ON (user.id = monthlyWinner.user_id)
Try this,
;with cte as
(
select Datename(mm,[date]) as m
--,max(amount_paid)
,Rank() over(PARTITION BY Datename(mm,[date]) order by [score] desc) as rr
,[score]
,id
from myTbl
--where DATEDIFF(YY,[date],'1/1/2013') = 0
)
select * from cte
left join myTbl as r on r.id=cte.id
where rr = 1

Categories