MySQL Query Two Tables and Max Timestamp - mysql

I have two tables that look something like this:
TABLE_conversations:
+-----------------+----------+----------------+------------+---------------------+--------+
| CONVERSATION_ID | QUEUE_ID | CONTACT_NUMBER | CONTACT_ID | DATE_CREATED | STATUS |
+-----------------+----------+----------------+------------+---------------------+--------+
| 1 | 1 | 15551112222 | 9000001 | 2014-09-12 00:28:24 | ACTIVE |
| 2 | 1 | 15553334444 | 9000002 | 2014-09-12 00:32:08 | ACTIVE |
+-----------------+----------+----------------+------------+---------------------+--------+
TABLE_messages:
+------------+-----------------+-------------+-------------+-----------+---------+---------------------+--------+-----------------------------------------------------------------------------------------------------------------+--------+
| MESSAGE_ID | CONVERSATION_ID | FROM_NUMBER | TO_NUMBER | DIRECTION | SENDER | TIMESTAMP | VIEWED | MESSAGE | STATUS |
+------------+-----------------+-------------+-------------+-----------+---------+---------------------+--------+-----------------------------------------------------------------------------------------------------------------+--------+
| 1 | 1 | 15551112222 | 17021112222 | IN | 9000001 | 2014-09-12 00:30:11 | 1 | Hello! Is this working? | ACTIVE |
| 2 | 1 | 17021112222 | 15551112222 | OUT | 8000001 | 2014-09-12 00:31:05 | 1 | Good evening! Of course! | ACTIVE |
| 3 | 1 | 15551112222 | 17021112222 | IN | 9000001 | 2014-09-12 00:31:27 | 1 | Perfect. Thank you! | ACTIVE |
| 4 | 1 | 17021112222 | 15553334444 | OUT | 8000002 | 2014-09-12 00:32:52 | 1 | Ticket 11251 is ready for pickup. | ACTIVE |
+------------+-----------------+-------------+-------------+-----------+---------+---------------------+--------+-----------------------------------------------------------------------------------------------------------------+--------+
I'm trying to run a query to select the CONVERSATION_ID, CONTACT_NUMBER, CONTACT_ID and most recent TIMESTAMP and grouping by phone number:
SELECT TABLE_conversations.CONVERSATION_ID, TABLE_conversations.CONTACT_NUMBER,
TABLE_conversations.CONTACT_ID, MAX(TABLE_messages.TIMESTAMP)
FROM TABLE_conversations, TABLE_messages
WHERE TABLE_conversations.STATUS='ACTIVE'
AND TABLE_messages.STATUS='ACTIVE'
GROUP BY CONTACT_NUMBER
ORDER BY TABLE_messages.TIMESTAMP;
The output I'm getting is below:
+-----------------+----------------+------------+-------------------------------+
| CONVERSATION_ID | CONTACT_NUMBER | CONTACT_ID | MAX(TABLE_messages.TIMESTAMP) |
+-----------------+----------------+------------+-------------------------------+
| 1 | 15551112222 | 9000001 | 2014-09-12 00:32:52 |
| 2 | 15553334444 | 9000002 | 2014-09-12 00:32:52 |
+-----------------+----------------+------------+-------------------------------+
I'm getting the same TIMESTAMP for both. The result I want is 2014-09-12 00:31:27 for 15551112222 and 2014-09-12 00:32:52 for 15553334444.
Really appreciate any help!

You're missing join conditions between the tables, so you're getting a full cross-product. So every conversation is being joined with every message, not just the messages from that conversation.
SELECT TABLE_conversations.CONVERSATION_ID, TABLE_conversations.CONTACT_NUMBER,
TABLE_conversations.CONTACT_ID, MAX(TABLE_messages.TIMESTAMP)
FROM TABLE_conversations
JOIN TABLE_messages ON TABLE_conversations.conversation_id = TABLE_messages.conversation_id
WHERE TABLE_conversations.STATUS='ACTIVE'
AND TABLE_messages.STATUS='ACTIVE'
GROUP BY CONTACT_NUMBER
ORDER BY TABLE_messages.TIMESTAMP;

Your sql cross join all rows from two tables, so the max timestamp for any group is the same.
SELECT TABLE_conversations.CONVERSATION_ID,
TABLE_conversations.CONTACT_NUMBER,
TABLE_conversations.CONTACT_ID,
MAX(TABLE_messages.TIMESTAMP)
FROM TABLE_conversations
JOIN TABLE_messages
ON TABLE_conversations.CONVERSATION_ID = TABLE_messages.CONVERSATION_ID
WHERE TABLE_conversations.STATUS='ACTIVE'
AND TABLE_messages.STATUS='ACTIVE'
GROUP BY CONTACT_NUMBER
Suggest you to get rid of the group by and aggregation function to see what's different between full cross join and inner join. Such as:
SELECT TABLE_conversations.CONVERSATION_ID,
TABLE_conversations.CONTACT_NUMBER,
TABLE_conversations.CONTACT_ID,
TABLE_messages.TIMESTAMP
FROM TABLE_conversations
JOIN TABLE_messages
ON TABLE_conversations.CONVERSATION_ID = TABLE_messages.CONVERSATION_ID
--without on clause above, comes to the full cross join
WHERE TABLE_conversations.STATUS='ACTIVE'
AND TABLE_messages.STATUS='ACTIVE'
ORDER BY TABLE_messages.TIMESTAMP;

Related

COUNT rows in 3 tables including zero values with MySQL

I have a MySQL 5.6 database with 3 tables:
job_offer
+----+----------+-------------+-----------+
| id | name | position_id | status_id |
+----+----------+-------------+-----------+
| 1 | John | 1 | 4 |
| 2 | Smith | 1 | 4 |
| 3 | Williams | 2 | 2 |
+----+----------+-------------+-----------+
position
+----+----------+
| id | name |
+----+----------+
| 1 | frontend |
| 2 | backend |
+----+----------+
status
+----+-----------+
| id | name |
+----+-----------+
| 1 | contacted |
| 2 | declined |
| 3 | rejected |
| 4 | interview |
+----+-----------+
I would like to build a query that can count all job offers by their position and statuses.
I have this query that performs almost the way I want it:
SELECT
position.name AS position_name,
status.name AS status_name
COUNT(job_offer.id) AS offers
FROM
job_offer
LEFT OUTER JOIN
position
ON job_offer.position_id = position.id
LEFT OUTER JOIN
status
ON job_offer.status_id = status.id
GROUP BY
position_name, status_name
Which gives me this result:
+---------------+-------------+--------+
| position_name | status_name | offers |
+---------------+-------------+--------+
| frontend | interview | 2 |
| backend | declined | 1 |
+---------------+-------------+--------+
The only problem is that I also need to display all existing statuses related to positions regardless of being NULL. So ideally it should look like this:
+---------------+-------------+--------+
| position_name | status_name | offers |
+---------------+-------------+--------+
| frontend | contacted | 0 |
| frontend | declined | 0 |
| frontend | rejected | 0 |
| frontend | interview | 2 |
| backend | contacted | 0 |
| backend | declined | 1 |
| backend | rejected | 0 |
| backend | interview | 0 |
+---------------+-------------+--------+
Is it possible to achieve this with one query? Thanks in advance for any help.
We can use a cross join approach between the position and status table to generate all possible combinations. Then, left join to job_offer and aggregate by position and status to find the counts:
SELECT
p.name AS position_name,
s.name AS status_name,
COUNT(jo.id) AS offers
FROM position p
CROSS JOIN status s
LEFT JOIN job_offer jo
ON jo.position_id = p.id AND
jo.status_id = s.id
GROUP BY
p.name,
s.name
ORDER BY
p.name,
s.name;
Demo

mysql table ordering incorrect with group by and order by

table 1: forum_threads
+-----+------+-------+
| id | title| status|
+-----+------+-------+
| 1 | a | 1 |
| 2 | b | 1 |
| 3 | c | 1 |
| 4 | d | 1 |
| 5 | e | 1 |
| 6 | f | 1 |
+-----+------+-------+
table 2: forum_comments
+-----+----------+--------------------+
| id | thread_id| comment |
+-----+----------+--------------------+
| 1 | 4 | hai |
| 2 | 4 | hello |
| 3 | 2 | welcome |
| 4 | 2 | whats your name |
| 5 | 6 | how are you |
| 6 | 5 | how old are you |
| 7 | 5 | good |
+-----+----------+--------------------+
wanted output
+-----------+----------+-----------------+
| thread_id | title | comment_count |
+-----------+----------+-----------------+
| 5 | e | 2 |
| 6 | f | 1 |
| 2 | b | 2 |
| 4 | d | 2 |
+-----------+----------+-----------------+
my Query
SELECT forum_threads.*,forum_comments.*,count(forum_comments.id) as comment_count
FROM forum_comments
LEFT JOIN forum_threads ON forum_comments.thread_id = forum_threads.id
GROUP BY forum_threads.id
ORDER BY forum_comments.id desc
Here I am trying to get the titles by the latest comment.
when I give ORDER BY forum_comments.id this returns the wrong order.
I need to order by the latest comments in the forum_comments table.
this query returns the wrong order please help me to find out the correct order.
how could I solve this easily?
This query should give you the expected result:
select t2.thread_id, t1.title, t2.comment_count from forum_threads as t1,
(SELECT id, thread_id, count(comment) as comment_count from forum_comments group by thread_id) as t2
where t1.id = t2.thread_id order by t2.id desc;
Instead of using forum_threads.* and forum_comments.* can you give specific column names and try.
If that doesn't work you should try explicitly assigning primary and foreign keys.

How to not select row of duplicate column A when column B and C is unique

Not sure how to explain this properly in the title, i might as well show the data.
I have a table (SQLFIDDLE):
+----+----------+------------+
| id | session | updated |
+----+----------+------------+
| a | c9c9c9c9 | 2018-22-03 |
| a | c9c9c9c9 | 2018-21-03 |
| a | s9s9s9s9 | 2018-20-03 |
| a | s9s9s9s9 | 2018-19-03 |
| b | s8s8s8s8 | 2018-05-02 |
| b | s8s8s8s8 | 2018-04-02 |
| c | s7s7s7s7 | 2018-03-01 |
| c | s7s7s7s7 | 2018-02-01 |
| d | s2s2s2s2 | 2018-01-01 |
+----+----------+------------+
Currently i'm using this query
SELECT id,
session,
Max(updated) AS updated_at
FROM reports
GROUP BY id,
session
ORDER BY updated_at DESC
LIMIT 4
This is the SQLFIDDLE: http://sqlfiddle.com/#!9/3c5272/1
Which is giving me this but i don't want row 2 because it's a duplicate ID
+----+----------+------------+
| id | session | updated |
+----+----------+------------+
| a | c9c9c9c9 | 2018-22-03 |
| a | s9s9s9s9 | 2018-20-03 |
| b | s8s8s8s8 | 2018-05-02 |
| c | s7s7s7s7 | 2018-02-01 |
+----+----------+------------+
I want a results like this
+----+----------+------------+
| id | session | updated |
+----+----------+------------+
| a | c9c9c9c9 | 2018-22-03 |
| b | s8s8s8s8 | 2018-05-02 |
| c | s7s7s7s7 | 2018-02-01 |
| d | s2s2s2s2 | 2018-01-01 |
+----+----------+------------+
I tried
SELECT a.id,
(SELECT session
FROM reports AS b
WHERE b.id = a.id
LIMIT 1),
Max(a.updated) AS updated_at
FROM reports AS a
GROUP BY a.id
ORDER BY updated_at DESC
LIMIT 4
But my MYSQL just keeps getting timeout with 100% CPU load, I have about 328745+ rows in that table currently.
Use "DISTINCT" when using SELECT to filter duplicate values. Visit this link to know more about DISTINCT.
You need to identify on how the data from session column will be selected, because you have id and session in the GROUP BY clause, and there are multiple sessions per id.
If this is not a concern, just remove session in the GROUP BY
SELECT
id,
session,
MAX(updated) AS updated_at
FROM reports
GROUP BY id
ORDER BY updated_at
DESC LIMIT 4
SQL FIDDLE

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

MySQL JOIN 3 tables using multiple columns/keys

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