I have 4 tables from which I want to aggregate data using MySQL 5.7.
Projects
+------------+--------+------------------+
| project_id | org_id | name |
+------------+--------+------------------+
| 1 | 1 | Big Project |
| 2 | 1 | Internal Project |
+------------+--------+------------------+
Tasks
+-----------+--------+----------------+------------+
| task_id | org_id | name | project_id |
+-----------+--------+----------------+------------+
| 1 | 1 | Check Work | 1 |
| 2 | 1 | Fix Code | 1 |
| 3 | 1 | Rebuild Office | 2 |
+-----------+--------+----------------+------------+
Resources
+-------------+--------+-------------+-----------+
| resource_id | org_id | first_name | last_name |
+-------------+--------+-------------+-----------+
| 1 | 1 | Alice | Black |
| 2 | 1 | Bob | Smith |
| 3 | 1 | Charlie | White |
+-------------+--------+-------------+-----------+
Task_Details
+-------------+--------+---------+-------------+
| resource_id | org_id | task_id | total_hours |
+-------------+--------+---------+-------------+
| 1 | 1 | 1 | 12 |
| 2 | 1 | 1 | 4 |
| 3 | 1 | 1 | 8 |
| 2 | 1 | 2 | 4 |
| 3 | 1 | 2 | 4 |
| 1 | 1 | 3 | 16 |
+-------------+--------+---------+-------------+
I want to SUM the total_hours, GROUPing by task and project, while still showing the total_hours each employee has individually spent on a task. The output I'm looking for would be something like this
Desired Output
+------------------+----------------+------------+-----------+-------------+
| project_name | task_name | first_name | last_name | total_hours |
+------------------+----------------+------------+-----------+-------------+
| Big Project | Check Work | Alice | Green | 12 |
| Big Project | Check Work | Bob | Smith | 4 |
| Big Project | Check Work | Charlie | Brown | 8 |
| Big Project | Check Work | NULL | NULL | 24 |
| Big Project | Fix Code | Bob | Smith | 4 |
| Big Project | Fix Code | Charlie | Brown | 4 |
| Big Project | Fix Code | NULL | NULL | 8 |
| Big Project | NULL | NULL | NULL | 32 |
| Internal Project | Rebuild Office | Alice | Green | 16 |
| Internal Project | Rebuild Office | NULL | NULL | 16 |
| Internal Project | NULL | NULL | NULL | 16 |
+------------------+----------------+------------+-----------+-------------+
I've managed to create a query that JOINs the relevant tables together, and even managed to GROUP them by project_id, task_id and resource_id. However, adding a WITH ROLLUP statement to the end of my query causes it to fail even though it works without one.
This is my current query:
SELECT
t1.project_name,
t1.task_name,
t2.first_name,
t2.last_name,
SUM(t1.task_hours)
FROM (
SELECT
Projects.project_id,
Projects.name AS project_name,
Tasks.task_id,
Tasks.name AS task_name,
Resources.resource_id,
Task_Details.total_hours AS task_hours
FROM
Projects
RIGHT OUTER JOIN
Tasks
ON
Projects.org_id = Tasks.org_id AND
Projects.project_id = Tasks.project_id
LEFT OUTER JOIN
Task_Details
ON
Task_Details.org_id = Tasks.org_id AND
Task_Details.task_id = Tasks.task_id
LEFT OUTER JOIN
Resources
ON
Resources.org_id = Task_Details.org_id AND
Resources.resource_id = Task_Details.resource_id
WHERE
Projects.org_id = 1
) AS t1
JOIN (
SELECT
resource_id,
first_name,
last_name
FROM
Resources
WHERE
org_id = 1
) AS t2
ON
t2.resource_id = t1.resource_id
GROUP BY
t1.project_id,
t1.task_id,
t1.resource_id;
How can I modify my query such that WITH ROLLUP works?
My SQLFiddle is here, but notably is for MySQL 5.6 rather than 5.7
IMHO, the problem with your query is this: You select some columns which are not in the GROUP BY. That causes some non-sensical values in the columns first_name, last_name, project_name and task_name. However, the sum column is probably correct, isn't it?
This works for me:
SELECT p.name as project_name,
s1.task_name,
first_name,
last_name,
s1.total_hours
FROM (
SELECT
t.project_id,
t.name as task_name,
h.resource_id,
sum(h.total_hours) as total_hours
FROM Task_Details as h
JOIN Tasks as t ON (t.task_id=h.task_id)
GROUP BY t.project_id, t.name, h.resource_id WITH ROLLUP
) AS s1
LEFT JOIN Resources AS r ON (s1.resource_id=r.resource_id)
JOIN Projects AS p ON (p.project_id=s1.project_id);
The nested SELECT does the interesting work, it sums up the total_hours of every resource_id, every task_name and and every project_id. The nesting SELECT then collects the name of every resource and project.
OUTPUT:
+------------------+----------------+------------+-----------+-------------+
| project_name | task_name | first_name | last_name | total_hours |
+------------------+----------------+------------+-----------+-------------+
| Big Project | NULL | NULL | NULL | 32 |
| Big Project | Check Work | Alice | Green | 12 |
| Big Project | Check Work | NULL | NULL | 24 |
| Big Project | Check Work | Bob | Smith | 4 |
| Big Project | Check Work | Charlie | Brown | 8 |
| Big Project | Fix Code | Bob | Smith | 4 |
| Big Project | Fix Code | Charlie | Brown | 4 |
| Big Project | Fix Code | NULL | NULL | 8 |
| Internal Project | NULL | NULL | NULL | 16 |
| Internal Project | Rebuild Office | NULL | NULL | 16 |
| Internal Project | Rebuild Office | Alice | Green | 16 |
+------------------+----------------+------------+-----------+-------------+
Hope this helps.
Related
I am stuck in this situation where I need to use Row Number and Column Number values from table's columns to derive the output mentioned below. I have tried everything - if/else, case when/then but not helping.
Any help/suggestions are really appreciated!
Here is a mocked up sample data present in db table -
+--------+--------+--------+----------+-------------+
| Record | ColNbr | RowNbr | ColTitle | CellContent |
+--------+--------+--------+----------+-------------+
| 1 | 1 | 1 | Unit | sqf |
| 1 | 1 | 2 | Unit | cm |
| 1 | 2 | 1 | Desc | roof |
| 1 | 2 | 2 | Desc | rod |
| 1 | 3 | 1 | Material | concrete |
| 1 | 3 | 2 | Material | steel |
| 1 | 4 | 1 | Quantity | 100 |
| 1 | 4 | 2 | Quantity | 12 |
| 1 | 1 | 1 | Unit | liter |
| 1 | 1 | 2 | Unit | ml |
| 1 | 2 | 1 | Desc | bowl |
| 1 | 2 | 2 | Desc | plate |
| 1 | 3 | 1 | Material | plastic |
| 1 | 3 | 2 | Material | glass |
| 1 | 4 | 1 | Quantity | 2 |
| 1 | 4 | 2 | Quantity | 250 |
+--------+--------+--------+----------+-------------+
Expected Output -
+--------+--------+--------+----------+-------------+
| Record | Unit | Desc | Material | Quantity |
+--------+--------+--------+----------+-------------+
| 1 | sqf | roof | concrete | 100 |
| 1 | cm | rod | steel | 12 |
| 2 | liter | bowl | plastic | 2 |
| 2 | ml | plate | glass | 250 |
+--------+--------+--------+----------+-------------+
If your actual data is like that, I suggest that you consider to separate the data to; for example, 4 different tables (unit,description,material & a table to store all that ids+quantity). The former 3 tables will store the prerequisite info that get minor updates throughout time and the last table will store all the quantity records. Let's say your tables will look something like this:
CREATE TABLE `Unit` (
unit_id INT,
unit_name VARCHAR(50));
+---------+-----------+
| unit_id | unit_name |
+---------+-----------+
| 1 | sqf |
| 2 | cm |
| 3 | liter |
| 4 | ml |
+---------+-----------+
CREATE TABLE `Description` (
desc_id INT,
desc_name VARCHAR(50));
+---------+-----------+
| desc_id | desc_name |
+---------+-----------+
| 1 | roof |
| 2 | rod |
| 3 | bowl |
| 4 | plate |
+---------+-----------+
CREATE TABLE `Material` (
mat_id INT,
mat_name VARCHAR(50));
+--------+----------+
| mat_id | mat_name |
+--------+----------+
| 1 | concrete |
| 2 | steel |
| 3 | plastic |
| 4 | glass |
+--------+----------+
CREATE TABLE `Records` (
unit_id INT,
desc_id INT,
mat_id INT,
quantity DECIMAL(14,4));
+---------+---------+--------+----------+
| unit_id | desc_id | mat_id | Quantity |
+---------+---------+--------+----------+
| 1 | 1 | 1 | 100 |
| 2 | 2 | 2 | 12 |
| 3 | 3 | 3 | 2 |
| 4 | 4 | 4 | 250 |
+---------+---------+--------+----------+
Then you insert the data accordingly.
Anyhow, for your existing data example, it could be done but there are some concern over whether the unit+desc+material+quantity matching are correct. The only way I can maybe at least think that it's correctly matched is by giving all of the query a similar ORDER BY clause. Hence, the following:
SELECT A.record,A.unit,B.Desc,C.Material,D.Quantity FROM
(SELECT #rn:=#rn+1 AS record,CASE WHEN coltitle='unit' THEN cellcontent END AS Unit
FROM yourtable, (SELECT #rn :=0 ) v
HAVING unit IS NOT NULL
ORDER BY colnbr) A LEFT JOIN
(SELECT #rn1:=#rn1+1 AS record,CASE WHEN coltitle='Desc' THEN cellcontent END AS `Desc`
FROM yourtable, (SELECT #rn1 :=0 ) v
HAVING `Desc` IS NOT NULL
ORDER BY colnbr) B ON a.record=b.record LEFT JOIN
(SELECT #rn2:=#rn2+1 AS record,CASE WHEN coltitle='material' THEN cellcontent END AS Material
FROM yourtable, (SELECT #rn2:=0 ) v
HAVING Material IS NOT NULL
ORDER BY colnbr) C ON a.record=c.record LEFT JOIN
(SELECT #rn3:=#rn3+1 AS record,CASE WHEN coltitle='Quantity' THEN cellcontent END AS Quantity
FROM yourtable, (SELECT #rn3:=0 ) v
HAVING Quantity IS NOT NULL
ORDER BY colnbr) D ON a.record=d.record;
The idea here is to make a sub-query based on COLTITLE then assign a numbering/ranking (#rn,#rn1,#rn2,#rn3) variable to each of the sub-query and join them up using LEFT JOIN. Now, this experiment works to exactly return the output that you need but its not a definite answer because there are some part that is questionable especially on matching the combination correctly. Hopefully, this will give you some idea.
i am watching a tutorial. There is a code which i don't understand what is supposed to do.
$sql = 'SELECT p.*,
a.screen_name AS author_name,
c.name AS category_name
FROM
posts p
LEFT JOIN
admin_users a ON p.author_id = a.id
LEFT JOIN
categories c ON p.category_id = c.id
WHERE
p.id = ?';
I read about the left joins but i didn't understand them. Can somebody please explain me the code i shared.
Thanks in advance!
Imagine you have two tables. One that stores the information about the programmers on your website, and the other table that keeps track of their online purchases.
PROGRAMMERS Table
+--------------------------------------------+
| ID | NAME | AGE | ADDRESS | SALARY |
+----+----------+-----+-----------+----------+
| 1 | Desire | 32 | 123 fake s| 3000.00 |
| 2 | Jamin | 25 | 234 fake s| 2500.00 |
| 3 | Jon | 23 | 567 fake s| 2000.00 |
| 4 | Bob | 30 | 789 fake s| 1500.00 |
| 5 | OtherGuy | 31 | 890 fake s| 1000.00 |
| 6 | DudeMan | 32 | 901 fake s| 500.00 |
+--------------------------------------------+
PURCHASES Table
+---------------------------------------------+
| ORDER_ID | PROG_ID | DATE | PRICE |
+-------------+---------+---------------------|
| 1 | 1 | 1-1-2017 | 100 |
| 2 | 2 | 1-2-2017 | 200 |
| 3 | 6 | 1-3-2017 | 300 |
+---------------------------------------------|
You decide you need to make a new table to consolidate this information to a table that contains
certain columns you want.
For example, you figure it would be nice for shipping purposes to have a table
that has the ID, the NAME, the PRICE, and the DATE columns.
Currently, the tables we have don't display all of that in a single table.
If we were to LEFT JOIN these tables, we would end up filling the desired columns
with NULL values where there is no information to join.
SELECT ID, NAME, PRICE, DATE
FROM PROGRAMMERS
LEFT JOIN PURCHASES
ON PROGRAMMERS.ID = PURCHASES.PROG_ID;
Notice that I'm selecting the columns I want from the starting table, then joining the right table
even though there might be missing information.
RESULTING TABLE
+-------------------------------------+
| ID | NAME | PRICE | DATE |
+----+----------+-----------------+---+
| 1 | Desire | 100 | 1-1-2017 |
| 2 | Jamin | 200 | 1-2-2017 |
| 3 | Jon | NULL | NULL |
| 4 | Bob | NULL | NULL |
| 5 | OtherGuy | NULL | NULL |
| 6 | DudeMan | 300 | 1-3-2017 |
+-------------------------------------+
For a visual representation of the difference between SQL JOINs check out
https://www.codeproject.com/Articles/33052/Visual-Representation-of-SQL-Joins .
For this question I have created a simple example that illustrates what I am asking.
Say I had a table called 'books'
+----+----------------------------+-----------+
| pk | title | author_id |
+----+----------------------------+-----------+
| 1 | The Lost Symbol | 1 |
| 2 | Follow Us Home | 2 |
| 3 | The Man in the High Castle | 3 |
+----+----------------------------+-----------+
(table a)
And another table called 'shops', that had a list of shops that sold each book:
+----+---------+-------------+-------+
| pk | book_id | shop_name | price |
+----+---------+-------------+-------+
| 1 | 1 | WHSmith | 5.00 |
| 2 | 1 | Waterstones | 7.00 |
| 3 | 1 | Amazon | 2.50 |
| 4 | 2 | WHSmith | 4.00 |
| 5 | 2 | Borders | 4.50 |
+----+---------+-------------+-------+
(table b)
If I do a simple select that grabs a book and all of the places it is sold using a join such as:
SELECT
books.*,
shops.shop_name,
shops.price
FROM
books
JOIN shops ON books.pk = shops.book_id
WHERE
book.book_name = "The Lost Symbol"
I would get results such as below:
+----+-----------------+-----------+-------------+-------+
| pk | title | author_id | shop_name | price |
+----+-----------------+-----------+-------------+-------+
| 1 | The Lost Symbol | 1 | WHSmith | 5.00 |
| 1 | The Lost Symbol | 1 | Waterstones | 7.00 |
| 1 | The Lost Symbol | 1 | Amazon | 2.50 |
+----+-----------------+-----------+-------------+-------+
(table c)
However, I would LIKE to receive results like this:
+----+-----------------+-----------+-------------+-------+
| pk | title | author_id | shop_name | price |
+----+-----------------+-----------+-------------+-------+
| 1 | The Lost Symbol | 1 | NULL | NULL |
| 1 | The Lost Symbol | 1 | WHSmith | 5.00 |
| 1 | The Lost Symbol | 1 | Waterstones | 7.00 |
| 1 | The Lost Symbol | 1 | Amazon | 2.50 |
+----+-----------------+-----------+-------------+-------+
(table d)
I.e. the first row is just the result of left outer join and the rest of the results are the the inner join.
An even more desired outcome is:
+------+-----------------+-----------+-------------+-------+
| pk | title | author_id | shop_name | price |
+------+-----------------+-----------+-------------+-------+
| 1 | The Lost Symbol | 1 | NULL | NULL |
| NULL | NULL | NULL | WHSmith | 5.00 |
| NULL | NULL | NULL | Waterstones | 7.00 |
| NULL | NULL | NULL | Amazon | 2.50 |
+------+-----------------+-----------+-------------+-------+
(table e)
Having shop_name and price concatenated and grouped in a single row seems not to work as it only does the first result from shops instead of all of them, also in my real world scenario, I have punctuation in the data so have to be careful with the separator.
So how would I get the result of table e?
You can use UNION ALL to build the required result set:
SELECT pk, title, author_id, NULL AS shop_name, NULL AS price
FROM books
WHERE books.title = "The Lost Symbol"
UNION ALL
SELECT NULL AS pk, NULL AS title, NULL AS author_id, shops.shop_name, shops.price
FROM books
JOIN shops ON books.pk = shops.book_id
WHERE books.title = "The Lost Symbol"
The first part of the union operation returns the first row of the result, i.e. the book title. The second part returns the rest of the rows, i.e.the shop names.
Demo here
I am trying to get the sum of a single column from all rows that share similar data. For example, given the following data:
|ppID | cID | Count | NameSpace|
|-----|-------|-------|----------|
|586 | 18281 | 1 | LAB |
|587 | 18269 | 1 | LAB |
|588 | 18281 | 1 | LAB |
|589 | 17823 | 1 | IPB |
|590 | 18281 | 1 | LAB |
|591 | 18256 | 1 | LAB |
|592 | 18256 | 1 | LAB |
|593 | 18269 | 1 | IPB |
|-----|-------|-------|----------|
I'm hoping to get:
|ppID | cID | Count | NameSpace|
|-----|-------|-------|----------|
|586 | 18281 | 3 | LAB |
|587 | 18269 | 1 | LAB |
|589 | 17823 | 1 | IPB |
|591 | 18256 | 2 | LAB |
|593 | 18269 | 1 | IPB |
|-----|-------|-------|----------|
I've pieced together a couple of different things and come up with `
SELECT * FROM PopularPages
WHERE cID IN (SELECT cID FROM PopularPages
GROUP BY cID
HAVING COUNT(cID) > 1)
ORDER BY cID, Namespace
which will list out each of the rows but without counting up the sum of the Count column. Any help would be appreciated.
is this what you want ?
SELECT
MIN(ppID) as ppID,
cID,
SUM(`Count`) as COUNT,
NameSpace
FROM PopularPages
GROUP BY cID
HAVING Count > 1;
I have a search section for looking up products which has a navigation bar for filtering purposes that shows the total results of each product feature. For example:
TOTAL RESULTS 60
New (32)
Used (28)
Particular (10)
Company (50)
In mysql I have the following queries (one per feature)
Type
SELECT a.id_type, whois.name as whoisName, COUNT(a.id_type) as countWhois
FROM (published a
INNER JOIN types whois ON whois.id = a.id_type)
GROUP BY id_type
+---------+------------+------------+
| id_type | whoisName | countWhois |
+---------+------------+------------+
| 0 | Company | 50 |
| 1 | Particular | 10 |
+---------+------------+------------+
Condition
SELECT a.id_condition, cond.name as condName, COUNT(a.id_condition) as countCondition
FROM (published a
INNER JOIN conditions cond ON cond.id = a.id_condition)
GROUP BY id_condition
+--------------+---------------+----------------+
| id_condition | conditionName | countCondition |
+--------------+---------------+----------------+
| 0 | New | 32 |
| 1 | Used | 28 |
+--------------+---------------+----------------+
I want to summarize the two queries in a single one but canĀ“t figure out how. I was thinking something like this:
+---------+------------+------------+--------------+---------------+----------------+
| id_type | whoisName | countWhois | id_condition | conditionName | countCondition |
+---------+------------+------------+--------------+---------------+----------------+
| 0 | Company | 50 | NULL | NULL | NULL |
| 1 | Particular | 10 | NULL | NULL | NULL |
| NULL | NULL | NULL | 0 | New | 32 |
| NULL | NULL | NULL | 1 | Used | 28 |
+---------+------------+------------+--------------+---------------+----------------+
Is this possible?
Thanks and sorry if my English is bad, it's not my native language.