Recursive query for MySql - get all subtree - mysql

Lets assume that there is a factory that produces products. the factory have catalog of a products, therefore tbl_catalog will be.
+----------------+--------------+-----------------------------------------------+
| catalog_number | catalog_name | details |
+----------------+--------------+-----------------------------------------------+
| 1 | LaptopX | Full assembled laptop - X |
| 2 | Top Half | Where the screen & webcam are |
| 3 | Bottom Half | Where mothereboard, and rest of the parts are |
| 4 | WebCam | WebCam for laptopX |
| 5 | LcdX | Lcd screen for laptopX |
| 6 | Keyboard | set of keys |
| 7 | SSD | Place to store data |
| 8 | Touchpad | If there is no mouse connected |
| 9 | DVD | to play music and watch movies |
| 10 | Lazer Beam | a must have for our DVD |
| 11 | EngineX | EngineX will spin our DVD with no problem |
+----------------+--------------+-----------------------------------------------+
Products composed of other products in the catalog - represented as tree, for our sample, tbl_catalog_tree will be:
+-----------+----------+
| parent_id | child_id |
+-----------+----------+
| 1 | 2 |
| 1 | 3 |
| 2 | 4 |
| 2 | 5 |
| 3 | 6 |
| 3 | 7 |
| 3 | 8 |
| 3 | 9 |
| 9 | 10 |
| 9 | 11 |
+-----------+----------+
That's for the catalog. now lets assemble some products. we need another table - assembly:
+------+----------------+--------------+-------+
| id | catalog_number | assembled_by | qc_by |
+------+----------------+--------------+-------+
| 100 | 11 | Joe | Dan |
| 101 | 11 | Joe | Dan |
| 102 | 11 | Joe | Dan |
| 200 | 10 | Joe | Dan |
| 201 | 10 | Joe | Dan |
| 201 | 10 | Joe | Dan |
| 300 | 9 | Mike | Dan |
| 301 | 9 | Mike | Dan |
| 302 | 9 | Mike | Dan |
+------+----------------+--------------+-------+
but we still don't know what is the connection between the DVD's sub parts so we need tbl_assembly_tree:
+-----------+----------------------+----------+
| parent_id | child_catalog_number | child_id |
+-----------+----------------------+----------+
| 302 | 11 | 100 |
| 302 | 10 | 200 |
| 301 | 11 | 101 |
| 301 | 10 | 201 |
| 300 | 11 | 102 |
| 300 | 10 | 202 |
+-----------+----------------------+----------+
since sub trees can be part of different catalogs (like this DVD that can mee part of laptopX and a future laptopY) we need to know what is the child catalog number for the specific assembly (and not count on the catalog tree structur).
My question is:
How to query so I get a product and all it's sub tree products?
If I want to query the DB about all the assmbled Items of a catalog number and for each assembly to know who build and who qc all it's sub parts recursively,in the above example, If Ill query for DVD I want the answer to be something like:
+----+---------+----------+---------+--------+------------+----------+--------+-------+
| id | cat_num | cat_name | assy_by | sub_id |sub_cat_num | sub_name | ass_by | qc_by |
+----+---------+----------+---------+--------+------------+----------+--------+-------+
| 300| 9 | DVD | Mike | 102 | 11 | EngineX | Joe | Dan |
| 300| 9 | DVD | Mike | 202 | 10 | Lazer B | Joe | Dan |
| 301| 9 | DVD | Mike | 101 | 11 | EngineX | Joe | Dan |
| 301| 9 | DVD | Mike | 201 | 10 | Lazer B | Joe | Dan |
| 302| 9 | DVD | Mike | 100 | 11 | EngineX | Joe | Dan |
| 302| 9 | DVD | Mike | 200 | 10 | Lazer B | Joe | Dan |
+----+---------+----------+---------+--------+------------+----------+--------+-------+
Of course, if all the catalog was one level only I wouldn't put this question here, but I need this to be recursive, so I can see all the subtrees of assemblies for a catalog number I select.
I've build an sqlfiddle for the above example and hope that someone can help me on this

The trick which came in mind is to join the same table several times.
This will give you the result you stated in your question:
SELECT
d.parent_id as id,
a.catalog_number,
a.catalog_name,
e.assembled_by,
d.child_id as sub_id,
b.child_id as sub_cat_num,
c.catalog_name as sub_name,
f.assembled_by as sub_assemly_by,
e.qc_by
FROM
catalog as a, catalog_tree as b,
catalog as c, assembly_tree as d,
assembly as e , assembly as f
WHERE
a.catalog_name = 'DVD'
and a.catalog_number = b.parent_id
and b.child_id = c.catalog_number
and b.child_id = d.child_catalog_number
and d.parent_id = e.id
and d.child_id = f.id
ORDER BY 1

Related

Inequality in Mysql with count()

I have the following structure :
Table Author :
idAuthor,
Name
+----------+-------+
| idAuthor | Name |
+----------+-------+
| 1 | Renee |
| 2 | John |
| 3 | Bob |
| 4 | Bryan |
+----------+-------+
Table Publication:
idPublication,
Title,
Type,
Date,
Journal,
Conference
+---------------+--------------+------+-------------+------------+-----------+
| idPublication | Title | Date | Type | Conference | Journal |
+---------------+--------------+------+-------------+------------+-----------+
| 1 | Flower thing | 2008 | book | NULL | NULL |
| 2 | Bees | 2009 | article | NULL | Le Monde |
| 3 | Wasps | 2010 | inproceding | KDD | NULL |
| 4 | Whales | 2010 | inproceding | DPC | NULL |
| 5 | Lyon | 2011 | article | NULL | Le Figaro |
| 6 | Plants | 2012 | book | NULL | NULL |
| 7 | Walls | 2009 | proceeding | KDD | NULL |
| 8 | Juices | 2010 | proceeding | KDD | NULL |
| 9 | Fruits | 2010 | proceeding | DPC | NULL |
| 10 | Computers | 2010 | inproceding | DPC | NULL |
| 11 | Phones | 2010 | inproceding | DPC | NULL |
| 12 | Creams | 2010 | proceeding | DPC | NULL |
| 13 | Love | 2010 | proceeding | DPC | NULL |
+---------------+--------------+------+-------------+------------+-----------+
Table author_has_publication :
Author_idAuthor,
Publication_idPublication
+-----------------+---------------------------+
| Author_idAuthor | Publication_idPublication |
+-----------------+---------------------------+
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
| 4 | 4 |
| 1 | 5 |
| 2 | 5 |
| 3 | 5 |
| 3 | 6 |
| 4 | 7 |
| 4 | 8 |
| 4 | 9 |
| 4 | 10 |
| 3 | 11 |
| 3 | 12 |
| 2 | 13 |
+-----------------+---------------------------+
I want to obtain the list of all authors having published at least 2 times at conference DPC in 2010.
I achieved to get the list of autors that have published something, and the number of publication for each, but I can't get my 'at least 2' factor.
My following query
SELECT author.name, COUNT(name) FROM author INNER JOIN author_has_publication ON author.idAuthor=author_has_publication.Author_idAuthor INNER JOIN publication ON author_has_publication.Publication_idPublication=publication.idPublication AND publication.date=2010 AND publication.conference='DPC'GROUP BY author.name;
returns the following result (which is good)
+-------+-------------+
| name | COUNT(name) |
+-------+-------------+
| Bob | 2 |
| Bryan | 3 |
| John | 1 |
+-------+-------------+
but when I try to select only the one with a count(name)>=2, i got an error.
I tried this query :
SELECT author.name, COUNT(name) FROM author INNER JOIN author_has_publication ON author.idAuthor=author_has_publication.Author_idAuthor INNER JOIN publication ON author_has_publication.Publication_idPublication=publication.idPublication AND publication.date=2010 AND publication.conference='DPC'GROUP BY author.name WHERE COUNT(name)>=2;
When you use aggregation funcion you can filter with a proper operator named HAVING
Having worok on the result of the query (then pn the aggrgated result like count() ) instead of where that work on the original value of the tables rows
SELECT author.name, COUNT(name)
FROM author INNER JOIN author_has_publication
ON author.idAuthor=author_has_publication.Author_idAuthor
INNER JOIN publication
ON author_has_publication.Publication_idPublication=publication.idPublication
AND publication.date=2010 AND publication.conference='DPC'
GROUP BY author.name
HAVING COUNT(name)>=2;

Mysql - pivot indeterminate number of rows to multiple columns

Given the following two tables:
+- Members -+
| ID | Name |
+----+------+
| 1 | Bob |
| 2 | Jim |
| 3 | Judy |
etc...
This table represents the members' children. Each parent may have many or no children
+- Children -------------+-----+
| ID | ParentID | Name | Age |
+----+----------+--------+-----+
| 1 | 3 | Jeff | 4 |
| 2 | 3 | Casey | 3 |
| 3 | 1 | Steven | 10 |
| 4 | 2 | Mary | 7 |
| 5 | 1 | Esther | 8 |
| 6 | 2 | Abe | 11 |
| 7 | 3 | Paul | 6 |
etc...
I need to create a table that looks like this:
+----+------+--------+------+---------+------+--------+------+
| ID | Name | Child1 | Age1 | Child2 | Age2 | Child3 | Age3 |
+----+------+--------+------+---------+------+--------+------+
| 1 | Bob | Steven | 10 | Esther | 8 | | |
| 2 | Jim | Abe | 11 | Mary | 7 | | |
| 3 | Judy | Paul | 6 | Jeff | 4 | Casey | 3 |
+----+------+--------+------+---------+------+--------+------+
I've tried various pivot table approaches, but every one that I've seen requires a known number of rows in the second table for each row in the first table. I essentially need an unknown number of columns. A group_concat isn't going to meet my requirements.
Is this possible with MySQL or do I need to do this in the backend?

Sum Team Competition Values with SQL

Overview
I have a bunch of data on a competition I'm holding and I want to present it in a better format.
There's 4 tables; the first two are self-explanatory, the points and extras table are essentially the exact same thing, they're just stored in different tables with slightly different column names.
Data
users
+----+---------------+------+
| id | name | team |
+----+---------------+------+
| 1 | John Doe | 1 |
| 2 | Jane Lane | 1 |
| 3 | Jack Black | 4 |
| 4 | Dan Bam | 3 |
| 5 | Pam Jan | 2 |
| 6 | Pop Tart | 2 |
| 7 | John Q | 1 |
| 8 | Hugo Strange | 3 |
| 9 | Jimmy Neutron | 2 |
+----+---------------+------+
teams
+----+-----------------+
| id | name |
+----+-----------------+
| 1 | Team Fun |
| 2 | The Dream Team |
| 3 | In It To Win It |
| 4 | Buddies |
+----+-----------------+
points
+---------+--------+------------+
| user_id | points | event |
+---------+--------+------------+
| 1 | 2 | Basketball |
| 2 | 4 | Basketball |
| 5 | 1 | Basketball |
| 8 | 3 | Basketball |
| 9 | 5 | Basketball |
| 2 | 8 | Volleyball |
| 5 | 5.5 | Volleyball |
| 6 | 6.5 | Volleyball |
| 7 | 2 | Volleyball |
| 8 | 4 | Volleyball |
| 9 | 9.5 | Volleyball |
| 1 | 2.5 | Dodgeball |
| 3 | 3 | Dodgeball |
| 4 | 4 | Dodgeball |
| 6 | 9 | Dodgeball |
| 7 | 2.5 | Dodgeball |
| 9 | 3 | Dodgeball |
+---------+--------+------------+
extras
+---------+--------+---------------------+
| user_id | points | description |
+---------+--------+---------------------+
| 1 | 5 | Great Sportsmanship |
| 3 | 10 | Team Player |
| 8 | 5.5 | Most Improved |
+---------+--------+---------------------+
What I'm Trying To Do
I want to write a query to return all the events (and "extras") a specific team participated in, the total points from all members of the team, and the participating members in that event.
Example below uses Team Fun (Team 1):
+---------------------+--------+--------------------+------------+
| event | points | members | members_id |
+---------------------+--------+--------------------+------------+
| Basketball | 6 | John Doe,Jane Lane | 1,2 |
| Volleyball | 10 | Jane Lane,John Q | 2,7 |
| Dodgeball | 5 | John Doe,John Q | 1,7 |
| Great Sportsmanship | 5 | John Doe | 1 |
+---------------------+--------+--------------------+------------+
If anyone could help me with figuring this out, I'd appreciate it!
SQLFiddle
This is a SQLFiddle with the data schema above - http://sqlfiddle.com/#!2/e8f97a
You can use a UNION to get the extras and points together:
SELECT user_id, points, event
FROM points
UNION ALL
SELECT user_id, points, description AS event
FROM extras
Then using this, you can compile your info with a SUM and a couple of GROUP_CONCATs:
SELECT P.event, SUM(P.points) AS points,
GROUP_CONCAT(U.name) AS members, GROUP_CONCAT(U.id) AS members_id
FROM teams T
INNER JOIN users U ON T.id = U.team
INNER JOIN
(
SELECT user_id, points, event
FROM points
UNION ALL
SELECT user_id, points, description AS event
FROM extras
) P ON U.id = P.user_id
WHERE T.id = #teamId
GROUP BY P.event
SQL Fiddle Example

Mysql query to convert table from long format to wide format

I have a table called ContactAttrbiutes which contains a list of each contacts' attributes. The kind of data stored for these contacts include: Title, Forename, Surname telephone number etc.
Current Table
+-------------+-----------+------------------------------+
| attributeId | ContactId | AttributeValue |
+-------------+-----------+------------------------------+
| 1 | 5 | Lady |
| 2 | 5 | Elizabeth |
| 3 | 5 | E |
| 4 | 5 | Anson |
| 5 | 5 | |
| 6 | 5 | |
| 7 | 5 | |
| 8 | 5 | |
| 10 | 5 | 0207 72776 |
| 11 | 5 | |
| 12 | 5 | 0207 22996 |
| 13 | 5 | 0207 72761 |
| 14 | 5 | |
| 15 | 5 | |
| 60 | 5 | Lloyds |
| 61 | 5 | |
| 1 | 10 | Mr |
| 2 | 10 | John |
| 3 | 10 | J C |
| 4 | 10 | Beveridge |
| 5 | 10 | Esq QC |
| 6 | 10 | Retired |
| 7 | 10 | |
| 8 | 10 | |
| 10 | 10 | 0207 930 |
| 11 | 10 | |
| 12 | 10 | |
| 13 | 10 | 0207 930 |
| 14 | 10 | |
| 15 | 10 | |
| 60 | 10 | |
| 61 | 10 | |
+-------------+-----------+------------------------------+
However I would like to run a query to create a table that looks like...
New Table
+-----------+----------------------+-------------------------+-----------------------+------------------------+
| ContactId | AttributeValue_Title | AttributeValue_ForeName |AttributeValue_Initial | AttributeValue_Surname |
+-----------+----------------------+-------------------------+-----------------------+------------------------+
| 5 | Lady | Elizabeth | E | Anson |
+-----------+----------------------+-------------------------+-----------------------+------------------------+
| 10 | Mr | John | J C | Beveridge |
+-----------+----------------------+-------------------------+-----------------------+------------------------+
I am sure there is a very simple answer but I have spent hours looking. Can anyone help?
The above is only a small extract of my table, I have 750,000 contacts. In addition I would like the final table to have more columns than I have described above but they will come from different Attributes with the existing table.
Thank you very much in advance.
try this
SELECT ContactId ,
max(CASE when attributeId = 1 then AttributeValue end) as AttributeValue_Title ,
max(CASE when attributeId = 2 then AttributeValue end )as AttributeValue_ForeName ,
max(CASE when attributeId = 3 then AttributeValue end )as AttributeValue_Initial ,
max(CASE when attributeId = 4 then AttributeValue end) as AttributeValue_Surname
from Table1
group by ContactId
DEMO HERE
if you want to make your result more longer for other attributeId then just add a case statment as in the code.
SELECT
t_title.AttributeValue AS title,
t_name.AttributeValue AS name,
...
FROM the_table AS t_title
JOIN the_table AS t_firstname USING(contact_id)
JOIN ...
WHERE
t_title.attributeId = 1 AND
t_firstname.attributeId = 2 AND
...
EAV "model" is an antipattern in most cases. Are you really going to have a variable number of attributes? If yes, then no-SQL solution might be more appropriate than a relational database.

mysql compare tables

I'm stuck with some tables in mysql. Don't really know how to join the info from three tables. Very thankful if anyone could help me. Thanks.
This is what I have:
Table1.Users
+----+--------+--------------+
| id | name | lastname |
+----+--------+--------------+
| 1 | Peter | Elk |
| 2 | Amy | Lee |
| 3 | James | Ride |
| 4 | Andrea | Thompson |
+----+--------+--------------+
Table2.Projects
+-----+-------------+
| id | name |
+-----+-------------+
| 13 | Lmental |
| 26 | Comunica |
| 28 | Ecobalear |
| 49 | Puigpunyent |
+-----+-------------+
Table3.Users_Projects
+----------+-------------+
| id_users | id_projects |
+----------+-------------+
| 1 | 13 |
| 1 | 28 |
| 2 | 13 |
| 2 | 28 |
| 2 | 49 |
| 3 | 28 |
| 3 | 49 |
| 4 | 49 |
+----------+-------------+
And I would like to print something like this:
+--------+--------------+----------------------------------+
| name | lastname | project |
+--------+--------------+----------------------------------+
| Peter | Elk | Lmental,Ecobalear |
| Amy | Lee | Lmental,Ecobalear, Puigpunyent |
| James | Ride | Ecobalear,Puigounyent |
| Andrea | Thompson | Puigpunyent |
+--------+--------------+----------------------------------+
Something like...
SELECT Users.name, Users.lastname, Projects.name
FROM (Users, Projects, Users_Projects)
WHERE Users_Projects.id_users=Users.id AND Users_Projects.id_projects=Projects.id
ORDER BY ...
...will output a single user/project per line, which you'll then have to manipulate in your choosen language.
Attempting to perform the concatenation, etc. in SQL is liable to lead to a pretty horrendous query.