SQL Query count rows where multiple column-values - php

So this is a how my MySQL table looks like (I have many thousands of rows):
| ID | date | Color | IUSERQ |
| 1 | 2020-09-25 18:55:54 | RED | GGW3 |
| 2 | 2020-09-25 18:24:12 | RED | FFQ3 |
| 3 | 2020-09-24 17:32:52 | RED | GWW3 |
| 4 | 2020-09-23 17:42:37 | BLUE | JJN6 |
| 5 | 2020-09-23 17:33:55 | BLUE | VVV5 |
| 6 | 2020-09-23 18:53:57 | RED | FFQ3 |
| 7 | 2020-09-22 18:15:11 | BLUE | FFQ3 |
Now to count all of the rows and group them in weeks, I do this:
if($stmt = $link->query("SELECT WEEK(date),COUNT(*) FROM sales WHERE color='RED' AND YEAR(date) = YEAR(NOW()) GROUP BY WEEK(date) order by MONTH(date) ASC")){
$php_data_array = Array(); // create PHP array
while ($row = $stmt->fetch_row()) {
$php_data_array[] = $row; // Adding to array
}
}else{
echo $link->error;
}
echo json_encode($php_data_array);
On the echo json_encode($php_data_array);, it gives the this current output: [["36","154"],["37","247"],["38","275"]]. So the first string in the array (36, 37, 38) is the week number, and the second one the number of rows where color is RED. Now, I also want to add where color is BLUE in the same array, so the expected value should be something like: [["36","154","166"],["37","247","265"],["38","275","298"]].
What approach should I use to do this?

Just add another column:
SELECT WEEK(date) as week, SUM(color = 'RED') as red, SUM(color = 'BLUE') as blue
FROM sales
WHERE color IN ('RED', 'BLUE') AND YEAR(date) = YEAR(NOW())
GROUP BY WEEK(date)
ORDER BY WEEK(date) ASC;
Note the changes to the query:
The SUM() does conditional aggregation for each color.
The columns are given names, which can be used to reference them in the application.
The ORDER BY is by the column being aggregated.

Use conditional aggregation:
select week(date) week_date, sum(color = 'RED') cnt_red, sum(color = 'BLUE') cnt_blue
from sales
where color in ('RED', 'BLUE') and date >= date_format(current_date, '%Y-%m-01')
group by week(date)
order by week_date
Note that this uses direct filtering on the date column, without applying functions on the filtered column. This is more efficient that year(date) = year(now()), especially if you have an index on that column.
I also changed the order by clause: obviously, you want to order by week rather than month (in most databases, that's a compilation error, because the month does not belong to the group by clause).

You can use dynamic query as another option :
SET #sql = NULL;
SELECT GROUP_CONCAT(
DISTINCT
CONCAT(
'SUM( color =''', color, ''' ) AS ',color
)
)
INTO #sql
FROM sales s;
SET #sql = CONCAT('SELECT WEEK(date) AS WEEK,',#sql,
' FROM sales
WHERE YEAR(date) = YEAR(NOW())
GROUP BY WEEK
ORDER BY WEEK');
and then call from php code block such as :
if($stmt = $link->query( #sql )){ ....
$php_data_array = Array(); // create PHP array
....
This way, no need to add each color name to be grouped individually, the query pivots all color column values whichever exist within the table. New colors might be added or existing ones might be deleted without constructing new querie.
Demo (in order to show result of the query)

Related

Create line chart using value from SQL database

So I've been following this tutorial: https://www.plus2net.com/php_tutorial/chart-line-database.php
I am trying to add a line chart to my website to display number of sales for each month.
This is an example on how my SQL table looks like:
| ID | user | sale_id | date |
| 1 | RVN4372 | 1341234 | 2020-09-22 17:31:32 |
| 2 | OVI6517 | 5452351 | 2020-09-22 15:14:43 |
| 3 | RVN4372 | 8452176 | 2020-09-17 16:23:54 |
| 4 | FOK8905 | 7421312 | 2020-09-17 11:23:11 |
| 5 | DIF9127 | 4236123 | 2020-09-15 15:32:26 |
This is how my current query looks like:
<?php
if($stmt = $link->query("SELECT user,COUNT(*) FROM sales WHERE yearweek(DATE(date), 1) = yearweek(curdate(), 1) GROUP BY user order by COUNT(*) DESC")){
$php_data_array = Array(); // create PHP array
while ($row = $stmt->fetch_row()) {
$php_data_array[] = $row; // Adding to array
}
}else{
echo $link->error;
}
//print_r( $php_data_array);
// You can display the json_encode output here.
echo json_encode($php_data_array);
// Transfor PHP array to JavaScript two dimensional array
echo "<script>
var my_2d = ".json_encode($php_data_array)."
</script>";
?>
<div id="curve_chart"></div>
This is how it looks like on my website:
So this basically groups the users, and count how many sales each user has. On the X axis is display the user's name, and Y axis total number of sales.
I want to change this, so in the X asix is display the month, and Y asis total number of sales. How can I accomplish this?
EDIT: Hava been trying out some, but can't make it work. This is what I've got so far:
if($stmt = $link->query("
SELECT YEAR(date)
as SalesYear,
MONTH(date) as SalesMonth,
COUNT(*) AS TotalSales
FROM sales
GROUP BY YEAR(date), MONTH(date)
ORDER BY YEAR(date), MONTH(date)
AND COUNT(*) DESC
")){
If you have more than one year then you need to group in Year-month combination. Then change this query to this.
SELECT CONCAT(YEAR(date),'-' MONTHNAME(date)) as ym, COUNT(*) FROM sales GROUP BY ym ORDER BY count(*) DESC

Select the rows with lowest value in specific column from those who match the other critarias

I have created an SQL table to save events data into it.
Each event can have multiple occurrences and when I filter them on site - I want to have the first matching occurrence of each event. each occurrence is saved in a different row, which contains a column for the general event_id and specific occ_id to each occurrence.
I need to get from the matching rows - only one row from each event_id, and it needs to be the one with the lowest occ_id value.
i.e.
gen_id | event_id | occ_id | month
------------------------------------
1 | 190 | 1 | 4
2 | 190 | 2 | 4
3 | 190 | 3 | 4
4 | 192 | 1 | 4
5 | 192 | 2 | 4
6 | 192 | 3 | 4
7 | 193 | 1 | 5
8 | 193 | 2 | 5
If I'm looking for events from month = 4, I need to get the events (gen_id): 1,4
and if I'm looking for month = 5 I need to get only event (gen_id): 7
My SQL query right now gets the matching events but with no occ_id filteration:
(it looks something like this right now)
SELECT
event_id,
event_title,
occ_id
FROM
table_name
WHERE month = 4
GROUP BY event_id
ORDER BY
event_id
DESC
I have tried to also use MIN / MAX but I guess it either not the right handler for this case or I'm using it wrong...
You want to filter. One method uses a correlated subquery in the WHERE clause:
select t.*
from table_name t
where t.occ_id = (select min(t2.occ_id)
from table_name t2
where t2.event_id = t.event_id
);
However, the lowest value always seems to be "1", so this might work as well:
select t.*
from table_name t
where t.month = 4 and
t.occ_id = 1;
To add month, you can add it to the outer query:
select t.*
from table_name t
where t.month = 4 and
t.occ_id = (select min(t2.occ_id)
from table_name t2
where t2.event_id = t.event_id and
t2.month = t.month
);
umm,
select t.event_id, t.occ_id, t.month, min(t.gen_id) from (
select event_id,month,min(occ_id) as min_occ_id from t where month=5 group by event_id, month
) t1 join t on t1.min_occ_id = t.occ_id and t1.event_id = t.event_id and t1.month = t.month
group by t.event_id, t.occ_id, t.month;
if columns event_id,occ_id,month make up an UNIQUE KEY, the SQL can be simplified.
select t.event_id, t.occ_id, t.month, t.gen_id from (
select event_id,month,min(occ_id) as min_occ_id from t where month=5 group by event_id, month
) t1 join t on t1.min_occ_id = t.occ_id and t1.event_id = t.event_id and t1.month = t.month

How to do a result and row that will find the sql sum of two columns?

Table 1 Table 2
_________________ ____________________
| ID| Name |Age | | ID| Cost | Date |
|---|-------|----| |---|-------|-------|
| 1 | Kirk | 33 | | 1 | 10 | 9/10 |
| 2 | Lonzo | 55 | | 1 | 20 | 7/8 |
| 3 | Dave | 44 | | 2 | 12 | 25/7 |
| 3 | 5 | 30/4 |
| 3 | 5 | 4/10 |
I want the result to be Kirk, who is 33, has a total cost of 30 and total dates to 2
etc.. (btw they enter their names and all this comes up)
The sql statement would be like this?
$sql= "SELECT Table1.Name, Table1.ID, Table2.ID, Table1.Age, SUM(Table2.Cost), SUM(Table2.Date)
AS count from Table1, Table2
WHERE Table1.ID = Table2.ID and Table1.Name = (my example here)"
$result = $conn->query($sql);
$Cost = 0;
$Date= 0;
$rec = $result->fetch_assoc();
$Date= $rec{'count'};
$Cost= $rec{'count'};
$Age = $rec{'Age'}
echo "$Name, who is $Age years old a total cost of $Cost and $Date total dates";
Im getting errors, saying my variables are undifined
so how would I put them into variables?
EDIT fixed
instead of using the sql to find it, i used result from a while loop that kept adding one to another variable (to count the date). The cost is able to do in both sql(sum) and result.
instead of using the sql to find it, i used result from a while loop that kept adding one to another variable (to count the date). The cost is able to do in both sql(sum) and result.
You were missing a group clause. I also put that in a subquery and used an INNER JOIN
SELECT Id, Name, Age, Cost, Dates
FROM Table1
INNER JOIN (
SELECT Id i, SUM(Cost) Cost,
COUNT(Date) Dates
from Table2
GROUP BY Id) t
ON i=Id
WHERE Name = 'Kirk'
You should also change one of the php statements to
$Cost= $rec{'Cost'};
Just use join..
SELECT table2.date ,SUM(cost) AS tatol FROM table1
JOIN table2 ON table2.id = table1.id
WHERE table1.id = 1

How to count the number of grouped rows in mysql when I already count the total rows

I have the below table and I want to do the following:
Count the number of times each item appears in the table
Count the DISTINCT number of items
Group the items by name
+-------+---------+
| id | names |
+-------+---------+
| 1 | Apple |
| 2 | Orange |
| 3 | Grape |
| 4 | Apple |
| 5 | Apple |
| 6 | Orange |
| 7 | Apple |
| 8 | Grape |
+-------+---------+
For the 1. and 3. points I have the following query which works quite well:
SELECT * ,
COUNT(names) as count_name,
FROM tbl_products WHERE type = '1'
GROUP BY names
So I get:
Apple (4)
Orange (2)
Grape (2)
Now I want to also count the number of grouped by rows and added a line to count the distinct elements, however there is some problem, since MySQL accepts the query but cannot output a result:
SELECT * ,
COUNT(names) as count_name,
COUNT(DISTINCT names) as count_total
FROM tbl_products WHERE type = '1'
GROUP BY names
Can anyone advice what might be the problem?
EDIT: For more clearance I want to get a table like this:
+-------+---------+------------+-------------+
| id | names | count_ctg | count_total |
+-------+---------+------------+-------------+
| 1 | Apple | 4 | 3 |
| 2 | Orange | 2 | 3 |
| 3 | Grape | 2 | 3 |
+-------+---------+------------+-------------+
Why not just use the query you are using:
SELECT * ,
COUNT(names) as count_name,
FROM tbl_products WHERE type = '1'
GROUP BY names
This query achieves all three objectives.
1) You get a count of the number of each name value in count_name.
2) The number of distinct names values will be equal to the number of rows in the result set , since you are grouping by names. Pretty much any client-side MySQL DB connection library will enable you to retrieve this value.
3) You meet your third criteria of grouping by name by explictly using GROUP BY names
Of course the value for id in the result set is meaningless, you may want to only select names and count_names.
1-.Count the number of times each item appears in the table:
SELECT names, count(names) FROM tbl_products WHERE type = '1' group by names
2-. How many distinct items exist in the table:
SELECT DISTINCT names FROM tbl_products WHERE type = '1'
3-. Group the items by name:
SELECT count(DISTINCT names) as Total FROM tbl_products WHERE type = '1'
As your last EDIT (ALL IN ONE):
SELECT id, names, count(names), total FROM tbl_products, (select count(distinct names) as total from tbl_products) as total WHERE type = '1' group by names
You can get the count of distinct names in a subquery, then OUTER JOIN that thing back into your main query where you already solved for 1 and 3:
SELECT names ,
COUNT(names) as count_name,
Total
FROM tbl_products
OUTER JOIN (SELECT count(DISTINCT names) as Total FROM tbl_products) t2
WHERE type = '1'
GROUP BY names
You can use the SQL Windowing OVER()
This query returns the row_number() function as the id column in the results, and the over(...) for row_number requires an order by clause. You could order by whatever you want, but it most be ordered by something.
;WITH vwGroups (name, Quantity) AS
(
SELECT name
, COUNT(*)
FROM tbl_products
GROUP BY name
)
SELECT ROW_NUMBER() OVER(ORDER BY Quantity DESC, name) AS id
, name
, Quantity AS count_name
, COUNT(*) OVER () AS count_total
FROM vwGroups

Group by and display latest entry date for that group

I have a groupby and sum table that I use to display current stock (it groups and sums all stock entries). I want to now display the latest entry date for each groupby group.
Here is what I'm working with:
-------------------------------------------------------------------
ProductID | Color | Timestamp | Quantity | RowID |
-------------------------------------------------------------------
25 | Red | 10/10/10 06:22:15 | 250 | 1 |
-------------------------------------------------------------------
32 | Green | 10/10/10 06:23:45 | 100 | 2 |
-------------------------------------------------------------------
$query = "SELECT productid, color, SUM(Quantity) AS TotalQuantity FROM inventory GROUP BY productid, color";
$result = mysql_query($query) or die(mysql_error());
// Table markup here
echo '<tr><td>'. $row['productid'] . '</td><td>' . $row['color']. '</td><td>'. $row['TotalQuantity'];
// Table markup here
I was trying to use the max value function to get the max rowid for each groupby but it was getting so confusing!
Well, if the latest entry date for each group is also by construction the most recent one, you can simply use:
SELECT
productid,
color,
SUM(Quantity) AS TotalQuantity,
MAX(Timestamp) AS LatestDate
FROM inventory
GROUP BY productid, color;

Categories