MySQL JOIN 3 tables using multiple columns/keys - mysql

Complete newbie to mySQL. So any help will be appreciated.
I have 3 tables -- carts, users, actions.
carts:
+------------+-------------+-------+
| cartId | session_id | userId|
+------------+-------------+-------+
users:
+----------+-------------+
| usedId | email |
+----------+-------------+
actions:
+-------------+------------------+---- ---------+
| session_id | impressionAction | impressionId |
+-------------+------------------+-----+--------+
In carts, there is one session_id per line.
In users, there is one userId per line.
In actions, there are multiple lines per session_id counting for all the actions for that session.
I would like to JOINthe three tables getting the output to be something like
+------+-------------+--------+------------------+--------------+-------+
userId | session_id | cartId | impressionAction | impressionId | email |
+------+-------------+--------+------------------+--------------+-------+
Where there will be multiple lines per userId and session_id; essentially a flattened file. I think if we JOIN carts and users on userId resulting in say A and then JOIN A and actions' onsession_id`, we are home.
A sample expected output is:
+------------+-------------+--------+------------------+--------------+---------+
userId | session_id | cartId | impressionAction | impressionId | email |
+------------+-------------+--------+------------------+--------------+---------+
| 1234 | abc3f45 | 0001 | LOGIN | 2032 |ab#yc.com|
| 1234 | abc3f45 | 0001 | ADD | 4372 |ab#yc.com|
| 1234 | abc3f45 | 0001 | ADD | 4372 |ab#yc.com|
| 1234 | abc3f45 | 0001 | SENDMAIL | ab#yc.com |ab#yw.com|
| 4567 | def4rg4 | 0002 | LOGIN | 2032 |db#yw.com|
| 4567 | def4rg4 | 0002 | ADD | 4372 |db#yw.com|
| 4567 | def4rg4 | 0002 | REMOVE | 3210 |db#yw.com|
+------------+-------------+--------+------------------+--------------+---------+**
I don't know how to JOIN 3 tables without one common key. I don't even know what type of join it is called.
Essentially, we are trying to join 3 tables with non-overlapping keys, gathering one common key through the first JOIN and then joining the intermediate with the third one. Is this called a CROSS JOIN? If no, is there a name?

Taken from your comment above
A USER may select many products, add them to their CART; a single
USER may have multiple CARTS and at the end of the event, they can
EMAIL the cart to themselves; the ACTIONS of the user are stored in
the actions table
This is how I see the structure (having in mind your data)
+---------------------+ +---------------------+ +---------------------+
| users | | carts | | actions |
+---------------------+ +---------------------+ +---------------------+
| user_id [PK] |--| | cart_id [PK] | | impression_id [PK] |
| email | |--| user_id [FK] | | action_name |
| | | product_id [FK] | |--| session_id [FK]* |
+---------------------+ | session_id [FK]* |--| | |
| | +---------------------+
+---------------------+
As you can see above, I'm joining first with carts and them with actions because only the table carts has both, user and session data.
The [FK]* next to the session_id on carts and actions could seem as a foreign key but in this case it's not - 'cause there's no separate table for sessions where it would be placed as an PK (primary key)
You asked about join - it is the same as inner join. INNER JOIN creates a new result table by combining column values of two tables (A and B) based upon the join-predicate. The query compares each row of A with each row of B to find all pairs of rows which satisfy the join-predicate.
This is a possible content of the tables
+------------------------+
| users |
+------------------------+
| id | email |
+------+-----------------+
| 1 | first#mail.org |
| 2 | second#mail.org |
| 3 | third#mail.org |
+------+-----------------+
+------------------------------------------+
| carts |
+------------------------------------------+
| id | user_id | product_id | session_id |
+------+---------+------------+------------+
| 1 | 1 | 5 | 1aaaa |
| 2 | 2 | 5 | 2ffff |
| 3 | 3 | 8 | 3ddddd |
| 4 | 1 | 5 | 1aaaaa |
| 5 | 3 | 9 | 3bbbbb |
| 6 | 1 | 6 | 1ccccc |
+------+---------+------------+------------+
+-------------------------------+
| actions |
+-------------------------------+
| id | name | session_id |
+------+-----------+------------+
| 1 | ADD | 1aaaa |
| 2 | ADD | 2ffff |
| 3 | SENDMAIL | 3ddddd |
| 4 | ADD | 3ddddd |
| 5 | SENDMAIL | 2ffff |
| 6 | ADD | 1aaaaa |
| 7 | REMOVE | 3ddddd |
| 8 | ADD | 1ccccc |
| 9 | ADD | 3bbbbb |
| 10 | SENDMAIL | 3bbbbb |
+------+-----------+------------+
As you can see, there are six products in the table carts and exactly six add actions in the table actions. Furthermore, as you can see user with an id=1 bought three products but not at the same time, since there are two sessions; user with an id=3 as well, bought these two products in different times etc...
The SQL statement
SELECT u.user_id, c.session_id, c.cart_id, a.impression_id, a.action_name, u.email
FROM users AS u
INNER JOIN carts AS c ON c.user_id = u.user_id
INNER JOIN actions AS a ON a.session_id = c.session_id
ORDER BY u.user_id, c.session_id, c.cart_id
Results:
+---------+------------+---------+---------------+-------------+-----------------+
| user_id | session_id | cart_id | impression_id | action_name | email |
+---------+------------+---------+---------------+-------------+-----------------+
| 1 | 1aaaa | 1 | 1 | ADD | first#mail.org |
| 1 | 1aaaa | 1 | 6 | ADD | first#mail.org |
| 1 | 1aaaa | 4 | 1 | ADD | first#mail.org |
| 1 | 1aaaa | 4 | 6 | ADD | first#mail.org |
| 1 | 1cccc | 6 | 8 | ADD | first#mail.org |
| 2 | 2ffff | 2 | 5 | SENDMAIL | second#mail.org |
| 2 | 2ffff | 2 | 2 | ADD | second#mail.org |
| 3 | 3bbbb | 5 | 9 | ADD | third#mail.org |
| 3 | 3bbbb | 5 | 10 | SENDMAIL | third#mail.org |
| 3 | 3dddd | 3 | 3 | SENDMAIL | third#mail.org |
| 3 | 3dddd | 3 | 4 | ADD | third#mail.org |
| 3 | 3dddd | 3 | 7 | REMOVE | third#mail.org |
+---------+------------+---------+---------------+-------------+-----------------+
Note: There's no guarantee for session uniqueness.
(Updated) Working SQL Fiddle
UPDATE: (Finding and deleting duplicates)
I've updated the SQL Fiddle in order to simulate duplicate records (when user added the same product within the same session). With the following statement you'll be able to retrieve those duplicated rows.
SELECT c.card_id, c.user_id, c.product_id, c.session_id, a.action_name, a.impression_id
FROM cards As c
INNER JOIN actions AS a ON a.session_id = c.session_id
GROUP BY c.user_id, c.product_id, c.session_id, a.action_name
HAVING count(*) > 1
Results:
+---------+------------+------------+------------+-------------+-----------------+
| card_id | user_id | product_id | session_id | action_name | impression_id |
+---------+------------+------------+------------+-------------+-----------------+
| 1 | 1 | 5 | 1aaaa | ADD | 1 |
| 6 | 1 | 6 | 1cccc | ADD | 8 |
+---------+------------+------------+------------+-------------+-----------------+
In the SELECT part of the statement above you may omit everything except card_id and impression_id. Deleting these two duplicates in one statement is a bit tricky since you can't modify the same table selected in a sub-query within the same query. I would avoid the tricky part in this case (which involves another inner sub-query) and would delete duplicates using separate statements as following
-- delete duplicates from cards
--
DELETE FROM WHERE card_id IN (1,6)
-- delete duplicates from actions
--
DELETE FROM WHERE card_id IN (1,8)
Even better, you could check if the user already has been added a selected product and don't add it twice.

Excuse my MySql syntax, as I don't know it :-p But this is the idea
SELECT u.userId, a.session_id, c.cartId, a.impressionAction, a.impressionId, u.email
FROM Carts c
JOIN Users u on u.userId = c.UserId
JOIN Actions a on a.session_id = c.session_id
This will just merge everything together, and you'll have duplicate cart records if you have many to 1 relationships

Related

How to bring columns to a table from another table by the Id?

I have this tables People, Ticket, and Report.
+----------+-------+-----+
| idPeople | Name | Age |
+----------+-------+-----+
| 1 | Name1 | 21 |
| 2 | Name2 | 37 |
| 3 | Name3 | 28 |
+----------+-------+-----+
I would like to replace the ForeingKey idPeople with columns Name and Age from People table.
+----------+------------+------------+----------+
| idTicket | ticketCol2 | ticketCol3 | idPeople |
+----------+------------+------------+----------+
| 5 | True | 01/06/99 | 1 |
| 6 | False | 01/06/99 | 2 |
| 7 | True | 01/06/99 | 4 |
+----------+------------+------------+----------+
In the Report table replace the Foreing Key idTicket with ticketCol2, Name, Age from the previous table Ticket with replaced columns (idPeople by Name, Age).
+----------+----------+------------+------------+
| idReport | idTicket | ReportCol3 | ReportCol4 |
+----------+----------+------------+------------+
| 1 | 5 | 01/06/99 | blabla |
| 2 | 7 | 01/06/99 | asdfdd |
| 2 | 6 | 01/06/99 | fooboo |
+----------+----------+------------+------------+
And I the result should be like this table and must be done in one query.
+----------+------------+------------+------------+------------+------+-----+
| idReport | ticketCol2 | ticketCol3 | ReportCol3 | ReportCol4 | Name | Age |
+----------+------------+------------+------------+------------+------+-----+
| 1 | 01/06/99 | abcd | blabla | 123456 | Name | 20 |
| 2 | 01/06/99 | bcda | asdfdd | 321456 | Name | 23 |
| 3 | 01/06/99 | asdf | fooboo | 123456 | Name | 28 |
+----------+------------+------------+------------+------------+------+-----+
I Have tried replacing the foreingkeys with LEFT JOIN and bringing some columns Name and Age to the Ticket table but now the last part where I should replace idTicket with Columns from Ticket is not working.
I have read about the nested JOINs but I cannot understand it very well, I would really appreciate some idea of how I can do it or what should I investigate. Are nested Joins the right way?
The query that I've tried to accomplish the Table Ticket.
SELECT Ticket.ticketCol2, Ticket.ticketCol3, p.Name 'Name', p.Age 'Age'
from Ticket
left join people p on (Ticket.idPeople=p.idPeople);
Try something like this:
SELECT Report.idReport,
Ticket.ticketCol2,
Ticket.ticketCol3,
Report.ReportCol3,
Report.ReportCol4,
People.Name,
People.Age
FROM People
LEFT JOIN Ticket ON Ticket.idPeople = People.idPeople
LEFT JOIN Report ON Report.idTicket = Ticket.idTicket
Like #RiggsFolly said, the Ticket.idPeople won´t match to the People.idPeople, so this will not match any rows.

Join 3 tables up count the highest user in one table

I'm trying 3 join tables so i can report who has the highest calls for each single customer please see the tables below
+-------+--+------------+
| users | | |
+-------+--+------------+
| id | | first name |
| 1 | | Bill |
| 2 | | Ben |
| 3 | | Bob |
| 4 | | Barry |
+-------+--+------------+
the second table is a call customers table
+-----------+--+-------------------+
| customers | | |
+-----------+--+-------------------+
| id | | Company |
| 1 | | windows company |
| 2 | | glass company |
+-----------+--+-------------------+
the third table is the where to calls are record
+-------------+--+--------+--------------+
| callrecords | | | |
+-------------+--+--------+--------------+
| id | | userid | company id |
| 1 | | 1 | 1 |
| 2 | | 1 | 1 |
| 3 | | 1 | 1 |
| 4 | | 2 | 1 |
| 5 | | 2 | 2 |
| 6 | | 2 | 2 |
+-------------+--+--------+--------------+
So as you can see in the call record table company id 1 which is the windows company has had 4 calls but user 1 made the most so that company i need to display bill company id 2 which is the glass company need to display user id 2 because they made 2 calls in total and not user id 1 because they only made 1 call
so the mysql query i need to make needs to loop round so the report looks like this
windows company - most calls bill
glass company - most calls Ben
If you want a single query that will fetch records. You can check below query. This will give you highest calls for each single customer.
SELECT CR.companyid, C.company, U.first_name, COUNT(CR.userid) AS callCount
FROM callrecords AS CR
INNER JOIN customers AS C ON (C.id= CR.companyid)
INNER JOIN users AS U ON (U.id= CR.userid)
GROUP BY C.id,CR.userid HAVING callCount = (
SELECT count(callrecords.userid) as count
FROM callrecords
WHERE callrecords.companyid = CR.companyid
GROUP BY callrecords.userid
ORDER BY count DESC
LIMIT 1
);
Ignore spells of table or column name because table's or column's name in question is not well formatted.

Left joins, i need an explanation about a code

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 .

Product with same model to related products table

I'm getting crazy to create an insert to a table from a select of same model.
I think that I should create a temp table to elaborate all model, but how?
Here the table I have:
Table product
+------------+----------+
| id_product | id_model |
+------------+----------+
| 1 | D |
| 2 | D |
| 3 | A |
| 4 | C |
| 5 | D |
+------------+----------+
Here the table I would like to create, but my best problem is that the record in related should be duplicate like
id1->id2 and id2->id1
Table related
+------------+-----------------+
| id_product | related_product |
+------------+-----------------+
| 1 | 2 |
| 1 | 5 |
| 2 | 1 |
| 2 | 5 |
| 5 | 1 |
| 5 | 2 |
+------------+-----------------+
Hmmm. I think you just want a self-join
select p1.id_product, p2.id_product as related_product
from product p1 join
product p2
on p1.id_model = p2.id_model and
p1.id_product <> p2.id_product;

Join two table mysql query return all user list values?

Join two table MySQL query return all user list values.
Please correct this query or provide some query.
Table1 : users
+---------+------------+-----------+
| user_id | user_name | cource_id |
+---------+------------+-----------+
| 1 | ramalingam | 1,2,3,4 |
| 2 | yuvi | 1 |
| 3 | Saravanan | 1,2,3 |
| 4 | gandhi | 1 |
+---------+------------+-----------+
Table2 : course
+-----------+-------------+
| cource_id | cource_name |
+-----------+-------------+
| 1 | php |
| 2 | wordpress |
| 3 | seo |
| 4 | magento |
+-----------+-------------+
Output
--------------------------------------
user_id | user_name | cource_id
--------------------------------------
1 | ramalingam| php,wordpress,seo,magnto
2 | yuvi | php
3 | Saravanan | php,wordpress,seo
4 | gandhi | php
This my query
SELECT u.user_id,u.user_name, GROUP_CONCAT(c.cource_name)as course_name
FROM users as u
LEFT JOIN course as c ON c.cource_id = u.user_id
Thank you for any help I can get on this...
In general DB design is bad, don't use comma separated lists at all. Hovewer you should use FIND_IN_SET() in your JOIN clause in order to achieve this:
SELECT
u.user_id,
u.user_name,
GROUP_CONCAT(c.cource_name) AS course_name
FROM
users AS u
LEFT JOIN cource AS c ON FIND_IN_SET(c.cource_id, u.cource_id)
GROUP BY
u.user_id,
u.user_name
Output is:
+---------+------------+---------------------------+
| user_id | user_name | course_name |
+---------+------------+---------------------------+
| 1 | ramalingam | php,wordpress,seo,magento |
| 2 | yuvi | php |
| 3 | Saravanan | php,wordpress,seo |
| 4 | gandhi | php |
+---------+------------+---------------------------+
4 rows in set