Mysql query to group by field and print grouped fields - php

I need help to make the best query posible here. I have the following Database:
+----+--------------+-----------------+------------------------------+
| id | reference_id | reference_field | value |
+----+--------------+-----------------+------------------------------+
| 1 | 6215 | title | Best recipe |
| 2 | 6215 | introText | Intro for best recipe |
| 3 | 6215 | fullText | Full text for best recipe |
| 4 | 6216 | title | Play Football |
| 5 | 6216 | introText | Intro for play football |
| 6 | 6216 | fullText | Full text for play football |
+----+--------------+-----------------+------------------------------+
I need to make a query where I group by reference_id and I should print the value by the reference_field, example of the output info:
Best recipe
Intro for best recipe
Full text for best recipe
Play Football
Intro for play football
Full text for play football
UPDATE
To accomplish this, I will print the query on the following way with PHP:
$result = $config->mysqli->query("SELECT * FROM table_name ORBER BY reference_id");
while($row = $result->fetch_array()) {
echo ('<h1>'.$row["title"].'</h1>');
echo ('<h1>'.$row["introText"].'</h1>');
echo ('<h1>'.$row["fullText"].'</h1>');
}
With the query above, I get all the records one by one (not grouped by the reference_id), in other hand if I do the query
SELECT * FROM table_name GROUP BY reference_id
How do I get the 3 values (title, introText, fullText) to print on the loop interaction in PHP?
As you can see with a normal "order by" or "group by" does not produce the proper result to print the values on the loop. What I see here is that on the result I should print the values of 3 records by each loop interaction in PHP instead print 3 fields of each records, does it make sense?

I think this is what you need:
SELECT value from TABLE_NAME
ORDER BY reference_id

You may want this:
select *
from your_table
order by reference_id, reference_field desc;

Create connection, do query and show the results:
<?php
$dbo = //Create db connection
$statement = $dbo->prepare("SELECT * FROM table_name ORBER BY reference_id");
$statement->execute();
$result = $statement->fetchAll(PDO::FETCH_ASSOC);
echo "<pre>";
print_r ($result);
echo "</pre>";
?>

Related

SQL need to improve run speed

I am trying to select data from mysql by a date field in the database. (Users can enter start date and end date)
For each selected row between user selected dates, I need to select from the same table to produce a result.
Example:
$query = "SELECT * FROM table WHERE date BETWEEN $begindate AND $enddate"; //Select by date
$result = mysqli_query($dbc,$query);
while($row = mysqli_fetch_array($result)){
vardump($row); //user needs to see all data between date selection
$query = "SELECT * FROM table WHERE field = $row['field']";
// and then do calculations with the data
}
This runs very slowly and I can see why. How can I improve the run speed?
Edit:
The original purpose was to generate a sales report between dates. Now the user wants the report to produce another result. This result could only be produced by searching against the same table, and the rows that I need is not within the date selection.
Edit 2:
I do need to output the entire table between date selection. Each row will need to find ALL other rows where field = field, within or out side of the date selection.
Edit 3: Solved the problem. All the answers are helpful, though I think the chosen answer was most related to my question. However, I believe using join when working with two tables is the right way to go. For my problem, I actually just solved it by duplicating the table and run my search against the duplicated table. The chosen answer did not work for me because the second query selection is not a part of the first query selection. Hope this would help anyone looking at this post. Again, thanks for all the help!
Well, so if you are really looking for such a conditions in same table, I suggest you should use IN selector like following:
$query = "SELECT * FROM table
WHERE field IN
(SELECT DISTINCT field FROM table
WHERE
date BETWEEN $begindate AND $enddate)";
So final code will look some like following:
$query = "SELECT * FROM table
WHERE field IN
(SELECT DISTINCT field FROM table
WHERE
date BETWEEN $begindate AND $enddate)";
$result = mysqli_query($dbc,$query);
while($row = mysqli_fetch_array($result)){
// do calculations with the $row
}
I guess your table names arent TABLE:
just user inner join
$query = "SELECT *
FROM table1
JOIN table2
ON table1.field = table2.field
WHERE date BETWEEN $begindate AND $enddate
ORDER BY table1.field;"
Stop writing pseudo-SQL
SELECT * FROM is technically pseudo-SQL (a sql command which the interpreter has to modify before the command can be executed. It is best to get in a habit of specifying columns in the SELECT statement.
Use SQL joins
Joins are what makes relational databases so useful, and powerful. Learn them. Love them.
Your set of SQL queries, combined into a single query:
SELECT
table1.id as Aid, table1.name as Aname, table1.field as Afield,
table2.id as Bid, table2.name as Bname, table2.field
FROM table table1
LEFT JOIN table table2
ON table1.field = table2.field
WHERE table1.date BETWEEN $begindate AND $enddate
ORDER BY table1.id, table2.id
Your resulting print of the data should result in something which access each set of data akin to:
$previous_table1_id = 0;
while($row = mysqli_fetch_array($result)){
if ($row['Aid'] != $previous_table1_id) {
echo 'Table1: ' . $row['Aid'] . ' - ' . $row['Aname'] . ' - '. $row['Afield'] . "\n";
$previous_table1_id = $row['Aid'];
}
echo 'Table2: ' . $row['Bid'] . ' - ' . $row['Bname'];
}
Dealing with aggregated data
Data-aggregation (multiple matches for table1/table2 on field), is a complex subject, but important to get to know. For now, I'll leave you with this:
What follows is a simplified example of one of what aggregated data is, and one of the myriad approaches to working with it.
Contents of Table
id | name | field
--------------------
1 | foos | whoag
2 | doh | whoag
3 | rah | whoag
4 | fun | wat
5 | ish | wat
Result of query I gave you
Aid | Aname | Afield | Bid | Bname
----------------------------------
1 | foos | whoag | 1 | foos
1 | foos | whoag | 2 | doh
1 | foos | whoag | 3 | rah
2 | doh | whoag | 1 | foos
2 | doh | whoag | 2 | doh
2 | doh | whoag | 3 | rah
3 | rah | whoag | 1 | foos
3 | rah | whoag | 2 | doh
3 | rah | whoag | 3 | rah
4 | fun | wat | 4 | fun
4 | fun | wat | 5 | ish
5 | ish | wat | 4 | fun
5 | ish | wat | 5 | ish
GROUP BY example of shrinking result set
SELECT table1.id as Aid, table1.name as Aname
group_concat(table2.name) as field
FROM table table1
LEFT JOIN table table2
ON table1.field = table2.field
WHERE table1.date BETWEEN $begindate AND $enddate
ORDER BY table1.id, table2.id
GROUP BY Aid
Aid | Aname | field
----------------------------------
1 | foos | foos,doh,rah
2 | doh | foos,doh,rah
3 | rah | foos,doh,rah
4 | fun | fun, ish
5 | ish | fun, ish

Php/Mysql count dancers from each moment added issue

I have a dance contest site and each user can login and add dance moments,
in my html table with all moments from all users i have all the data but i want in a html column to add "number of dancers for each moment added by the logged user id".
I have this:
$c = mysql_query("SELECT * FROM moments");
$dancers = 0;
while($rows = mysql_fetch_array($c)){
for($i = 1; $i <= 24; $i++){
$dan_id = 'dancer'.$i;
if($rows[$dan_id] != "" || $rows[$dan_id] != null )
$dancers++;
}
}
echo "<th class="tg-amwm">NR of dancers</th>";
echo "<td class='tg-yw4l'>$dancers</td>";
phpMyAdmin moments table: has id, clubname, category, discipline, section, and this:
But this process is count all the dancers names from all users moments.
Example for this process: You have a total of 200 dancers !
I want the process to count for me all dancers names for each moment added in the form not a total of all entire users moments, something like this: if user john has two moments added: Moment 1: 5 dancers - moment 2: 10 dancers, and so on for each user.
Let me try to put you in the right way (it seems a long post but I think it's worth the beginners to read it!).
You have been told in the comments to normalize your database, and if I were you and if you want your project to work well for a long time... I'd do it.
There are many MySQL normalization tutorials, and you can google it your self if you are interested... I'm just going to help you with your particular example and I'm sure you will understand it.
Basically, you have to create different tables to store "different concepts", and then join it when you query the database.
In this case, I would create these tables:
categories, dance_clubs, users and dancers store "basic" data.
moments and moment_dancers store foreign keys to create relations between the data.
Let's see the content to understand it better.
mysql> select * from categories;
+----+---------------+
| id | name |
+----+---------------+
| 1 | Hip-hop/dance |
+----+---------------+
mysql> select * from dance_clubs;
+----+---------------+
| id | name |
+----+---------------+
| 1 | dance academy |
+----+---------------+
mysql> select * from users;
+----+-------+
| id | name |
+----+-------+
| 1 | alex |
+----+-------+
mysql> select * from dancers;
+----+-------+
| id | name |
+----+-------+
| 1 | alex |
| 2 | dan |
| 3 | mihai |
+----+-------+
mysql> select * from moments;
+----+--------------+---------------+-------------------+
| id | main_user_id | dance_club_id | dance_category_id |
+----+--------------+---------------+-------------------+
| 1 | 1 | 1 | 1 |
+----+--------------+---------------+-------------------+
(user alex) (dance acad..) (Hip-hop/dance)
mysql> select * from moment_dancers;
+----+-----------+-----------+
| id | moment_id | dancer_id |
+----+-----------+-----------+
| 1 | 1 | 1 | (moment 1, dancer alex)
| 2 | 1 | 2 | (moment 1, dancer dan)
| 3 | 1 | 3 | (moment 1, dancer mihai)
+----+-----------+-----------+
Ok! Now we want to make some queries from PHP.
We will use prepared statements instead of mysql_* queries as they said in the comments aswell.
The concept of prepared statement can be a bit hard to understand at first. Just read closely the code and look for some tutorials again ;)
Easy example to list the dancers (just to understand it):
// Your connection settings
$connData = ["localhost", "user", "pass", "dancers"];
$conn = new mysqli($connData[0], $connData[1], $connData[2], $connData[3]);
$conn->set_charset("utf8");
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
// Here we explain MySQL which will be the query
$stmt = $conn->prepare("select * from dancers");
// Here we explain PHP which variables will store the values of the two columns (row by row)
$stmt->bind_result($dancerId, $dancerName);
// Here we execute the query and store the result
$stmt->execute();
$stmt->store_result();
// Here we store the results of each row in our two PHP variables
while($stmt->fetch()){
// Now we can do whatever we want (store in array, echo, etc)
echo "<p>$dancerId - $dancerName</p>";
}
$stmt->close();
$conn->close();
Result in the browser:
Good! Now something a bit harder! List the moments:
// Your connection settings
$connData = ["localhost", "user", "pass", "dancers"];
$conn = new mysqli($connData[0], $connData[1], $connData[2], $connData[3]);
$conn->set_charset("utf8");
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
// Query to read the "moments", but we have their main user and dancers in other tables
$stmtMoments = $conn->prepare("
select
moments.id,
(select name from users where users.id = moments.main_user_id) as main_user,
(select name from dance_clubs where dance_clubs.id = moments.dance_club_id) as dance_club,
(select name from categories where categories.id = moments.dance_category_id) as dance_category,
(select count(*) from moment_dancers where moment_dancers.moment_id = moments.id) as number_of_dancers
from moments
");
// Five columns, five variables... you know ;)
$stmtMoments->bind_result($momentId, $momentMainUser, $momentDanceClub, $momentDanceCategory, $momentNumberOfDancers);
// Query to read the dancers of the "moment" with id $momentId
$stmtDancers = $conn->prepare("
select
dancers.name as dancer_name
from
dancers join moment_dancers on dancers.id = moment_dancers.dancer_id
where
moment_dancers.moment_id = ?
");
$stmtDancers->bind_param("i", $momentId);
$stmtDancers->bind_result($momentDancerName);
// Executing the "moments" query
$stmtMoments->execute();
$stmtMoments->store_result();
// We will enter once to the while because we have only one "moment" right now
while($stmtMoments->fetch()){
// Do whatever you want with $momentId, $momentMainUser, $momentDanceClub, $momentDanceCategory, $momentNumberOfDancers
// For example:
echo "<h3>Moment $momentId</h3>";
echo "<p>Main user: $momentMainUser</p>";
echo "<p>Dance club: $momentDanceClub</p>";
echo "<p>Category: $momentDanceCategory</p>";
echo "<p>Number of dancers: $momentNumberOfDancers</p>";
echo "<p><strong>Dancers</strong>: ";
// Now, for this moment, we look for its dancers
$stmtDancers->execute();
$stmtDancers->store_result();
while($stmtDancers->fetch()){
// Do whatever you want with each $momentDancerName
// For example, echo it:
echo $momentDancerName . " ";
}
echo "</p>";
echo "<hr>";
}
$stmtUsers->close();
$stmtMoments->close();
$conn->close();
Result in browser:
And that's all! Please ask me if you have any question!
(I could post the DDL code to create the database of the example with the content data if you want)
Edited: added dancers table. Renamed moment_users to moment_dancers. Changed functionality to adapt the script to new tables and names.

Categorize/Group data from table using SQL

I have stored the physical locations of specific files in my database with download counters to provide downloads via shorter urls like /Download/a4s. Each file has a categoryId assigned via foreign keys which just describes to which course/lecture it belongs for an easier overview. The table fileCategories basically looks like this
categoryId | categoryName
---------------------------
1 | Lecture 1
2 | Lecture 2
3 | Personal Stuff
Assume that I have a files table which looks like this with some other columns I did omit
fileId | categoryId | filePath | ...
----------------------------------------
1 | 1 | /Foo/Bar.pdf | ...
2 | 1 | /Foo/Baz.pdf | ...
3 | 2 | /Bar/Foo.pdf | ...
4 | 2 | /Baz/Foo.pdf | ...
5 | 3 | /Bar/Baz.pdf | ...
I have created a page which should display some data about those files and group them by their categories which produces a very simple html table which looks like this:
Id | Path | ...
-----------------------
Lecture 1
-----------------------
1 | /Foo/Bar.pdf | ...
2 | /Foo/Baz.pdf | ...
-----------------------
Lecture 2
-----------------------
3 | /Bar/Foo.pdf | ...
4 | /Baz/Foo.pdf | ...
-----------------------
Personal Stuff
-----------------------
5 | /Bar/Baz.pdf | ...
So far I am using multiple SQL queries to fetch and store all categories in PHP arrays and append file entries to those arrays when iterating over the files table. It is highly unlikely this is the best method even though the number of files is pretty small. I was wondering whether there is a query which will automatically sort those entries into temporary tables (just a spontaneous guess to use those) which I can output to drastically improve my current way to obtain the data.
You can not do this with just mysql but a combination of JOIN and some PHP.
SELECT * FROM files f LEFT JOIN fileCategories c USING (categoryId) ORDER BY c.categoryName ASC
Be sure to order by the category first (name or ID) and optionally order by other params after that to allow the following code example to work as expected.
in PHP then iterate over the result, remember the category id from each row and if it changes you can output the category delimiter. assumung the query result is stored in $dbRes
Example Code:
$lastCategoryId = null;
while ($row = $dbRes->fetchRow()) {
if ($lastCategoryId !== $row['categoryId']) {
echo "--------------------" . PHP_EOL;
echo $row['categoryName'] . PHP_EOL
echo "--------------------" . PHP_EOL;
}
echo $row['filePath'] . PHP_EOL;
$lastCategoryId = $row['categoryId'];
}

Show database entries separated by comma

I have one field in the backend, where I input IDs separated by comma - ID1, ID2, ID3....These are videos in fact. All ids are stored in the field product_videos in the database (as they are typed).
How can I echo these id's on the frontend so they all show for the product?
Storing comma separated data in one data field is a bad idea. It is a real pain to manipulate, so you should really consider revising your db structure.
You haven't shown your data structure, so I'll give a basic example and then explain how it can be improved. My example assumes product_videos is linked to particular users:
table: `users`
| user_id | user_name | product_videos |
|---------|-----------|----------------|
| 1 | User1 | 1,2,3,4,6,7 |
| 2 | User2 | 5 |
You would maybe run a query
SELECT `product_videos` FROM `users` WHERE `user_name` = 'User1'
This would give you one row, with a comma separate value - you would then need to use something like PHP's explode() to convert it into an array and then loop through that array. That is a very bad method (and it will only become harder as you try to do more advanced things).
Instead, it would be easier to use a link table. Imagine:
table: `users`
| user_id | user_name |
|---------|-----------|
| 1 | User1 |
| 2 | User2 |
table: `videos`
| video_id | user_id |
|-----------|---------|
| 1 | 1 |
| 2 | 1 |
| 3 | 1 |
| 4 | 1 |
| 5 | 2 |
| 6 | 1 |
| 7 | 1 |
In this example, each video is a separate row in a db table (and each video is linked to an existing user). Each row is readily able to be handled independently. This makes it really easy to handle extra data for each video, such as a title, runtime length, date of uploading, etc.
You would then need to run a JOIN query. e.g.
SELECT `videos`.`video_id` FROM `videos`
INNER JOIN `users` ON `users`.`user_id` = `videos`.`user_id`
WHERE `users`.`user_name` = 'User1'
In PHP, you would do something like:
$q = mysql_query("SELECT `videos`.`video_id` FROM `videos` INNER JOIN `users` ON `users`.`user_id` = `videos`.`user_id` WHERE `users`.`user_name` = 'User1'");
while ($row = mysql_fetch_assoc($q)) {
echo "VIDEO ID = " . $row["video_id"] . "<br/>";
}

Group results in subgroups

I have been trying to get the subgroup total of the data below using the method in the accepted answer here: how to group result in subgroups in php, but to no avail. Somebody please help.
I want this:
+---------------+-----------------+
| Type | Price |
+---------------+-----------------+
| Music | 19.99 |
| Music | 3.99 |
| Music | 21.55 |
| Toy | 89.95 |
| Toy | 3.99 |
+---------------+-----------------+
displayed as this:
Music | 45.53
Toy | 93.94
in group and subtotal.
You should do it directly in MySQL when you perform the query (if possible):
SELECT DISTINCT Type, SUM(Price) as Total FROM table_name GROUP BY Type
You could select the sum of the prices, then group the subtotals by type. I used this SQL statement in a PHP script;
SELECT SUM(Price) AS Total, Type FROM Table GROUP BY Type
I then executed the SQL statement and echoed the results in a simple foreach loop. It appears to work fine with me.
Try this...
// Get the sum
$group = array();
foreach($rows as $r){
$group[$r->type] = $group[$r->Type] + $r->Price;
}
// Display
foreach($group as $type=>$price){
echo "Type:".$type." Price: ".$price;
}

Categories