MYSQL Left Join on multiple table but select only most recent rows - mysql

I have three tables (form_completions, customer_sessions and conversion_sessions) which are design like the below, these are heavily striped down for the purpose of this example:
Form_completions:
id | name | email
------------------------
101 | Tom | tom#website.com
102 | Ben | ben#website.com
Customer_sessions:
id | customer_id | session_id | session_source
---------------------------------
1 | 900 | 9kc73bsf | twitter
2 | 901 | 15jvuw83 | google
3 | 901 | 45h73bgf | twitter
Conversion_sessions:
id | customer_id | session_id | form_completion_id
------------------------------------
1 | 900 | 9kc73bsf | 101
2 | 901 | 45h73bgf | 102
The query that i currently have is:
SELECT custsess.session_source,
c.id as form_conversion_id,
c.name,
FROM conversion_sessions convsess
LEFT JOIN form_completions fc
ON convsess.`form_completion_id` = fc.`id`
LEFT JOIN customer_sessions custsess
ON custsess.`customer_id` = convsess.`customer_id`
This will give me the following:
session_source | form_conversion_id | name
------------------------------------------
twitter | 101 | Tom
google | 102 | Ben
twitter | 102 | Ben
But what i need is to avoid the duplication of form_conversion_id and only include the most recent so it would be...
session_source | form_conversion_id | name
------------------------------------------
twitter | 101 | Tom
twitter | 102 | Ben
Hopefully this makes sense.

Solution to your problem:
SELECT custsess.session_source,
fc.id as form_conversion_id,
fc.name
FROM conversion_sessions AS convsess
INNER JOIN form_completions AS fc
ON convsess.form_completion_id = fc.id
INNER JOIN customer_sessions AS custsess
ON custsess.customer_id = convsess.customer_id
AND custsess.session_id = convsess.session_id
While joining you forgot to join customer_sessions and coversion_sessions ON session_id.
OUTPUT:
session_source form_conversion_id name
twitter 01 Tom
twitter 102 Ben
For demo follow the below link:
https://www.db-fiddle.com/f/pQG4VCWAnoGtRJa6MkvST6/0

You need a Subquery to get the most recent, change your JOIN to something like this
LEFT JOIN customer_sessions custsess
ON custsess.id =
(
SELECT MAX(id)
FROM Customer_sessions
WHERE c.id = custsess.customer_id
)

Looking to your data You should just use inner join
You should use inner join on a subquery for max session_id
SELECT custsess.session_source,
c.id as form_conversion_id,
c.name,
FROM conversion_sessions convsess
customer_sessions custsess ON custsess.`customer_id` = convsess.`customer_id`
INNER JOIN (
select customer_id, max(session_id) as max_sess
from Customer_sessions
group by customer_id
) t on t.customer_id = custsess.customer_id and t.max_sess = custsess.session_id
INNER JOIN form_completions fc ON convsess.`form_completion_id` = fc.`id`

Related

i want to show an one particular blog have how much likes and how much comment using sql query

Blog table:
| bid | btitle |
| 29 | ...... |
| 38 | ...... |
likes table:
| lid | bid |
| 1 | 29 |
| 2 | 29 |
| 3 | 29 |
| 4 | 38 |
| 5 | 38 |
comment table
| commid | bid |
| 1 | 29 |
| 2 | 29 |
| 3 | 38 |
I had tried the following query but that will not work for me:
SELECT blog.bid,blog.btitle,COUNT(likes.lid) AS likecnt,COUNT(comment.comid) AS commentcnt FROM blog,likes,comment WHERE blog.bid=likes.bid AND blog.bid=comment.bid GROUP BY blog.bid
i want output like:
| bid | btitle | likecnt | commentcnt |
| 29 | ...... | 3 | 2 |
| 38 | ...... | 2 | 1 |
You can do left join with separate aggregation :
select b.bid, b.btitle,
coalesce(l.likecnt, 0) as likecnt,
coalesce(c.commentcnt, 0) as commentcnt
from blog b left join
(select l.bid, count(*) as likecnt
from likes l
group by l.bid
) l
on l.bid = b.bid left join
(select c.bid, count(*) as commentcnt
from comment c
group by c.bid
) c
on c.bid = l.bid;
If you want only matching bids the use INNER JOIN instead of LEFT JOIN & remove COALESCE().
Under many circumstances, correlated subqueries may be the fastest solution:
select b.bid, b.btitle,
(select count(*) from likes l where l.bid = b.bid) as num_likes,
(select count(*) from comment c where c.bid = b.bid) as num_comments
from blog b;
When is this a win performance wise. First, you want indexes on likes(bid) and comments(bid). With those indexes, it might be the fastest approach for your query.
It is particularly better if you have a where clause filtering the blogs in the outer query. It only has to do the counts for the blogs in the result set.
Use proper joins and count DISTINCT values because multiple joins increase the number of returned rows:
SELECT b.bid, b.btitle,
COUNT(DISTINCT l.lid) AS likecnt,
COUNT(DISTINCT c.comid) AS commentcnt
FROM blog b
LEFT JOIN likes l ON b.bid = l.bid
LEFT JOIN comment c ON b.bid = c.bid
GROUP BY b.bid, b.btitle
See the demo.
I use LEFT joins just in case there are no comments or likes for a post.
Results:
| bid | btitle | likecnt | commentcnt |
| --- | ------ | ------- | ---------- |
| 29 | ...... | 3 | 2 |
| 38 | ...... | 2 | 1 |

Join Table B to Table A only if entry in Table B equals entry in Table C

I have 3 tables. clients, sales and potential_sales.
The basic structure is as follows:
Clients Table:
+-----------+-------+----------------+
| client_id | name | address |
+-----------+-------+----------------+
| 1 | john | 12 blue ave |
| 2 | paul | 34 green lane |
| 3 | peter | 69 yellow road |
+-----------+-------+----------------+
Potential Sales Table:
+----------+------------+---------------------+
|product_id | client_id | received_free_promo |
+-----------+------------+---------------------+
| 3 | 1 | 1 |
| 4 | 2 | 0 |
| 5 | 2 | 1 |
+-----------+------------+---------------------+
Sales:
+----------+-----------+-----------+
| sales_id | client_id | product_id |
+----------+-----------+------------+
| 1 | 2 | 4 |
| 2 | 43 | 4 |
| 3 | 2 | 5 |
| 4 | 18 | 93 |
+----------+-----------+------------+
I want to join clients and potential_sales tables ONLY IF
1) received_promo equals 1 AND
2) they actually bought the promo package (i.e. the product_id for the potential sale has an entry into the sales table ). If they didn't eventually buy the free_promo product then I do not want to join the clients and potential_sales table at all. This is important - I can't simply JOIN to figure it out because this is only a small part of a bigger query and I can't afford to JOIN for no reason.
(Here is how I would like it to work. It's mainly pseudo-code to describe what I want to happen)
SELECT
c.*
FROM
clients c
LEFT JOIN potential_sales ps ON ps.client_id=c.id
LEFT JOIN sales ps ON s.product_id=ps.product_id
IF(s.sales_id) JOIN potential_sales ps ON ps.client_id=c.id
How do I do this in MySQL? I haven't come close to a solution. Please help!
Try this:
SELECT A.*, B.product_id, B.received_free_promo
FROM Clients A JOIN
(SELECT * FROM PotentialSales
WHERE received_free_promo=1) B
ON A.client_id=B.client_id
WHERE EXISTS (SELECT 1 FROM Sales C
WHERE A.client_id=C.client_id
AND B.product_id=C.product_id);
See Demo on SQL Fiddle.
What you are missing is the EXISTS clause:
SELECT
C.*,
P.*
FROM
Clients AS C
INNER JOIN PotentialSales AS P ON C.client_id = P.client_id
WHERE
P.received_free_promo = 1 AND
EXISTS (
SELECT
'the client already sold that product'
FROM
Sales AS S
WHERE
S.client_id = C.client_id AND
S.product_id = P.product_id)
Try this..." select * from client as c natural join potential as p join sales as s on p.product_id = s.product_id where received_promo = 1". select * will mention everything from all the 3 tables. You can choose what you want as the result.

Group 3 tables by max date

A customer can make many inquiries, and an inquiry can have many updates. I'm trying to view each inquiry and the latest update. I'm able to pull the latest date but not the update that's relevant to it. I've seen answers similar to mine but they only seem to deal with one join and I don't understand how I can use those to find a solution.
Here's my sql
select c.name, i.inquirycontent, u.updatecontent, max(u.date) from inquiries i
inner join customers c on c.customerid = i.customerid
left join updates u on u.inquiryid = i.inquiryid
group by i.inquiryid
Even if I omit the customer table, I am still unable to match the latest update to its content. How can I do this?
edit - as requested, here is some sample data
+------------+------+
| customerid | name |
+------------+------+
| 1 | jeff |
+------------+------+
| 2 | anne |
+------------+------+
+-----------+-----------------+------------+
| inquiryid | inquirycontent | customerid |
+-----------+-----------------+------------+
| 1 | inquiry1content | 1 |
+-----------+-----------------+------------+
| 2 | inquiry2content | 1 |
+-----------+-----------------+------------+
| 3 | inquiry3content | 2 |
+-----------+-----------------+------------+
+----------+-----------------+-----------+----------+
| updateid | updatecontent | inquiryid | date |
+----------+-----------------+-----------+----------+
| 1 | update1content | 1 | 01-01-17 |
+----------+-----------------+-----------+----------+
| 2 | update2content | 1 | 03-01-17 |
+----------+-----------------+-----------+----------+
| 3 | update3content | 3 | 04-01-17 |
+----------+-----------------+-----------+----------+
And here's what I want the query to output -
+------+-----------------+----------------+----------+
| name | inquirycontent | latestupdate | date |
+------+-----------------+----------------+----------+
| jeff | inquiry1content | update2content | 03-01-17 |
+------+-----------------+----------------+----------+
| jeff | inquiry2content | NULL | NULL |
+------+-----------------+----------------+----------+
| anne | inquiry3content | update3content | 04-01-17 |
+------+-----------------+----------------+----------+
This is a crude solution using UNION, but it works for what I need. The first select finds all unique inquiries by their latest update, and the second select finds all rows that haven't yet been updated.
(select c.name, i.inquirycontent, u.updatecontent, t2.mxdate
from updates u
inner join inquiries i on i.inquiryid = u.inquiryid
inner join customers c on c.customerid = i.customerid
inner join
(
select max(u.date) mxdate, i.inquiryid
from updates u
left join inquiries i on i.inquiryid = u.inquiryid
group by i.inquiryid
) t2
on u.inquiryid = t2.inquiryid
and u.date = t2.mxdate)
UNION
(select c.name, i.inquirycontent, u.updatecontent, u.date
from inquiries i
left join updates u on u.inquiryid = i.inquiryid
inner join customers c on c.customerid = i.customerid
where u.updatecontent is NULL)

MYSQL JOIN two tables limit results from second table by date

I am trying to retrieve date from two tables using a MYSQL query. I want to join them together were categories.cat_id=topics.topic_cat. Multiple entries may have the same topic_cat, so I only want to SELECT the most recent, which is equal to MAX(topic_date).
The following query shows the correct information from topics, with only one result per topic_cat and that result having the most recent date.
SELECT topic_subject, topic_cat, topic_date
FROM topics
GROUP BY topic_cat DESC
Multiple rows may have the same value for topic_cat, but I only want to retrieve and join only the most recent, MAX(topic_date) and then join to a query which shows the following information from the categories table.
SELECT categories.cat_id, categories.cat_name, categories.cat_description, topics.topic_subject, topics.topic_cat, topics.topic_date, topics.topic_by
FROM categories
LEFT JOIN topics
ON categories.cat_id=topics.topic_cat
GROUP BY cat_id;
This query displays the correct information, except one thing. It shows the topic_cat with the oldest entry, or MIN(topic_date). I have tried the following to get the topic_cat by newest entry or MAX(topic_date), but without success.
SELECT categories.cat_id, categories.cat_name, categories.cat_description
FROM categories
LEFT JOIN (SELECT topic_subject, topic_cat, topic_date, topic_by
FROM topics
GROUP BY topic_cat DESC) AS topics
ON categories.cat_id=topics.topic_cat
Any help or suggestions would be greatly appreciated.
Ok, so here is the sample data and associated desired result.
Table 1 = categories
_______________________________________________________
| cat_id | cat_name | cat_description |
-------------------------------------------------------
| 1 | james | Some information about james|
-------------------------------------------------------
| 2 | myo | Some information about myo |
-------------------------------------------------------
| 3 | brandon | Some information about brandon |
-------------------------------------------------------
Table 2 = topics
__________________________________________________
| topic_subject | topic_cat | topic_date | topic_by |
----------------------------------------------------------
| marcos | 2 | 2013-9-28 | User 1 |
---------------------------------------------------------
| ferdinand | 2 | 2013-9-29 | User 2 |
---------------------------------------------------------
| maria luisa | 2 | 2013-9-30 | User 1 |
---------------------------------------------------------
| Isabella | 1 | 2013-8-24 | User 3 |
--------------------------------------------------------
| Carlos | 3 | 2012-6-21 | User 2 |
--------------------------------------------------------
| Enrique | 3 | 2011-4-2 | User 3 |
---------------------------------------------------------
I would like the query to return the following data based on the above tables:
_________________________________________________________________________________________________
| cat_id | cat_name | cat_description | topic_subject | topic_cat | topic_date | topic_by |
----------------------------------------------------------------------------------------------------------------
| 1 | james | Some information about james | Isabella | 1 | 2013-8-24 | User 3 |
----------------------------------------------------------------------------------------------------------------
| 2 | myo | Some information about myo | maria luisa | 2 | 2013-9-30 | User 1 |
----------------------------------------------------------------------------------------------------------------
| 3 | brandon | Some information about brandon | Carlos | 3 | 2012-6-21 | User 2 |
----------------------------------------------------------------------------------------------------------------
I hope that clarifies things.
Try This:
###
SELECT * FROM categories c
LEFT JOIN topics t ON c.cat_id = t.topic_cat
WHERE c.cat_id IN (SELECT t1.cat_id FROM (
SELECT c.cat_id, c.cat_name, MAX(t.topic_date) AS maxdate FROM categories c
LEFT JOIN topics t ON c.cat_id = t.topic_cat
GROUP BY c.cat_name
) as t1 WHERE t1.maxdate = t.topic_date OR t.topic_date IS NULL );
### without nulls
SELECT * FROM categories c
LEFT JOIN topics t ON c.cat_id = t.topic_cat
WHERE c.cat_id IN (SELECT t1.cat_id FROM (
SELECT c.cat_id, c.cat_name, MAX(t.topic_date) AS maxdate FROM categories c
LEFT JOIN topics t ON c.cat_id = t.topic_cat
GROUP BY c.cat_name
) as t1 WHERE t1.maxdate = t.topic_date);
Try changing
LEFT JOIN (SELECT topic_subject, topic_cat, topic_date, topic_by
FROM topics
GROUP BY topic_cat DESC) AS topics
to:
LEFT JOIN (SELECT topic_subject, topic_cat, topic_date, topic_by
FROM topics
GROUP BY topic_cat
ORDER BY topic_date DESC
LIMIT 0,1) AS topics

JOIN 4 Tables with meta_table

I have this database structure:
sites
id | name
1 | Site 1
2 | Site 2
locations
id | city
23 | Baltimore
24 | Annapolis
people
id | name
45 | John
46 | Sue
sites_meta
id | site_id | meta_name | meta_value
1 | 1 | local | 23
2 | 1 | person | 45
3 | 2 | local | 24
4 | 2 | person | 46
So, as you can see, Site 1 (id 1) is in Baltimore and is associated with John, Site 2 (id 2) is in Annapolis and associated with Sue.
I need to figure out a clever sql statement that can return
id | name | id | city | id | name
1 | Site 1 | 23 | Baltimore | 45 | John
2 | Site 2 | 24 | Annapolis | 46 | Sue
I would be super appreciative if anyone can help me out. I've tried a few combinations of a select statement, but I keep getting stuck with using two values from the sites_meta table.
select
s.id as siteId,
s.name as siteName,
max(l.id) as locationId,
max(l.city) as city,
max(p.id) as personId,
max(p.name) as personName
from
sites_meta sm
join sites s on s.id = sm.site_id
left join locations l on l.id = sm.meta_value and sm.meta_name = 'local'
left join people p on p.id = sm.meta_value and sm.meta_name = 'person'
group by
s.id,
s.name
You can probably imagine how this kind of "meta" table might become a pain... especially as more items are added to it.
Instead, you might consider replacing it with two new tables, sites_locations and sites_people.
SELECT
s.id,s.name,l.id,l.city,p.id,p.name
FROM
sites s
INNER JOIN sites_meta sm1 ON s.id = sm1.site_id
INNER JOIN sites_meta sm2 ON s.id = sm2.site_id
INNER JOIN locations l ON sm1.meta_value = l.id AND sm1.meta_name = 'local'
INNER JOIN people p ON sm2.meta_value = p.id AND sm2.meta_name = 'person'
;