Retrieve data from tables via JOINS - mysql

I have three tables: sessions, urls and visitors.
I need to join these three tables in such a way that I should be able to get data from each table and the maximum number of rows returned should be equal to sessions.
Following is the basic schema of my tables.
Table sessions
session_id | url_id | referrer_id | country
-------------------------------------------
1234 | a1b1 | bb11 | US
4567 | x1y1 | ll33 | IN
6789 | a1b1 | ff99 | UK
Table urls
id | url |
-----------------------------------------
a1b1 | https://url-1.com |
x1y1 | https://url-2.com |
bb11 | https://referrer-url-1.com |
ll33 | https://referrer-url-2.com |
ff99 | https://referrer-url-3.com |
Table visitors
id | session_id | visiting_time |
-----------------------------------------
1 | 1234 | 447383930 |
2 | 4567 | 547383930 |
3 | 6789 | 647383930 |
What I want as the final output should look like:
session_id | visiting_time | url | referrer_url | country
------------------------------------------------------------------------------------------
1234 | 447383930 | https://url-1.com | https://referrer-url-1.com | US |
4567 | 547383930 | https://url-2.com | https://referrer-url-2.com | IN |
6789 | 647383930 | https://url-1.com | https://referrer-url-3.com | UK |
I want to map url_id in sessions table with id in urls table and get the corresponding url from urls table and have the value in the new column named url. Similarly, map referrer_id in sessions table with id in urls table and get the corresponding url from urls table and have the value in the new column named referring_url.
As you can see: JOINS with sessions and visitors is simple and can be simply done via:
select session_id, visiting_time, country
from sessions,
visitors
where sessions.session_id = visitors.session_id;
But joining with urls table and getting the url and referring_url is somewhat tricky. I have tried LEFT JOIN and INNER JOIN but couldn't make it work.
Any help with query or references would be helpful.
Thanks!

You should avoid using comma based Implicit joins and use Explicit Join based syntax
You will need two joins with urls table; one to fetch the url and another for referrer_url.
Try the following:
SELECT s.session_id,
v.visiting_time,
u1.url,
u2.url AS referrer_url,
s.country
FROM sessions AS s
JOIN visitors AS v ON v.session_id = s.session_id
JOIN urls AS u1 ON u1.id = s.url_id
JOIN urls AS u2 ON u2.id = s.referrer_id

select sessions.session_id, visitors.visiting_time, urls.url, urlsReferrer.url referrer_url, sessions.country
from sessions
inner join visitors on sessions.session_id = visitors.session_id
inner join urls on sessions.url_id = url.id
left join urls urlsReferrer on sessions.referrer_id = urlsReferrer.id

You should use a join on urls twice one of url_id and one for referrer_id
select session_id
, visiting_time
, u1.url
, u2.url
, country
from sessions
INNER JOIN visitors ON sessions.session_id = visitors.session_id
INNER JOIN urls u1 on u1.id= sessions.url_id
INNER JOIN urls u2 on u2.id= sessions.referrer_id
In this way you can join the sessions for retrive both then values you need

Joins are defined in the from statement - please read up on https://www.w3schools.com/sql/default.asp to better get a sense of join usage.
Modify the query as needed based on which table "referrer_url" actually comes from
Warning: You must include a where statement which limits your result. I strongly suggest defining a date field and range to prevent you from initiating a long running query and affecting database performance.
see below for query
select
s.session_id,
v.visiting_time,
s.country,
u.url,
u.referrer_url
from
sessions s
join visitors v on session_id
join urls on u.id=s.url_id
;

Related

Select count with value from different tables

I want to count all entries in one table grouped by the user id.
This is the query I used which works fine.
select uuid_mapping_id, count(*) from t_message group by uuid_mapping_id;
and these are the results:
+-----------------+----------+
| uuid_mapping_id | count(*) |
+-----------------+----------+
| 1 | 65 |
| 4 | 277 |
Now I would like to display the actual user name, instead of the ID.
To achieve this I would need the help of two different tables.
The table t_uuid_mapping which has two columns:
uid_mapping_id, which equals uuid_mapping_id in the other table.
And f_uuid which is also unique but completely different.
f_uuid can also be found in another table t_abook which also contains the names in the column f_name.
The result I am looking for should be:
+-----------------+----------+
| f_name | count(*) |
+-----------------+----------+
| admin | 65 |
| user1 | 277 |
I am new to the database topic and understand that this could be achieved by using JOIN in the query, but to be honest I did not completely understand this yet.
if I understand you correctly:
SELECT tm.f_name, COUNT(*) as count
FROM t_message tm
LEFT JOIN t_abook ta ON (tm.uuid_mapping_id = ta.uid_mapping_id)
GROUP BY tm.f_name

Left Join takes very long time on 150 000 rows

I am having some difficulties to accomplish a task.
Here is some data from orders table:
+----+---------+
| id | bill_id |
+----+---------+
| 3 | 1 |
| 9 | 3 |
| 10 | 4 |
| 15 | 6 |
+----+---------+
And here is some data from a bills table:
+----+
| id |
+----+
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
| 6 |
+----+
I want to list all the bills that have no order associated with.
In order to achieve that, I thought that the use of LEFT JOIN was appropriated so I wrote this request:
SELECT * FROM bills
LEFT JOIN orders
ON bills.id = orders.bill_id
WHERE orders.bill_id IS NULL;
I thought that I would have the following result:
+----------+-----------+----------------+
| bills.id | orders.id | orders.bill_id |
+----------+-----------+----------------+
| 2 | NULL | NULL |
| 5 | NULL | NULL |
+----------+-----------+----------------+
But I can't reach the end of the request, it has run more than 5 minutes without result, I stopped the request because this can't be a production time anyway.
My real dataset has more than 150 000 orders and 100 000 bills. Is the dataset too big?
Is my request wrong somewhere?
Thank you very much for your tips!
EDIT: side note, the tables have no foreign keys defined... *flies away*
Your query is fine. I would use table aliases in writing it:
SELECT b.*
FROM bills b LEFT JOIN
orders o
ON b.id = o.bill_id
WHERE o.bill_id IS NULL;
You don't need the NULL columns from orders, probably.
You need an index on orders(bill_id):
create index idx_orders_billid on orders(bill_id);
By your where statement, I assume your looking for orders that have no bills.
If that's the case you don't need to do a join to the bills table as they would by definition not exist.
You will find
SELECT * FROM orders
WHERE orders.bill_id IS NULL;
A much better performing query.
Edit:
Sorry I missed your "I want to list all the bills that have no order associated with." when reading the question. As #gordon pointed out an index would certainly help. However if changing the scheme is feasible I would rather have a nullable bill.order_id column instead of a order.bill_id because you won't need a left join, an inner join would suffice to get order bills as it would be a quicker query for your other assumed requirements.

MySQL Query JOIN returning 1 null field

I have two simple tables:
user
+-------------------------------------------------+
| uid | firstname | lastname |
|-------------------------------------------------|
| 4000 | Zak | Me |
+-------------------------------------------------+
user_role
+----------------------------------------------+
| rid | uid | oid |
|----------------------------------------------|
| 5 | 4000 | 7000 |
+----------------------------------------------+
I am using this query
SELECT us.firstname, us.lastname, ur.oid
FROM user us
LEFT JOIN user_role ur
ON us.uid = ur.uid
WHERE us.firstname = 'Zak';
My result is
+-------------------------------------------------+
| firstname | lastname | oid |
|-------------------------------------------------|
| Zak | Me | (null) |
+-------------------------------------------------+
What am I missing? It has to be something simple!
UPDATE
Has to do something with the WHERE clause .. Because if left out, it returns all rows with oid included
Follow a process like this:
run this query
SELECT *
FROM user us
WHERE us.firstname = 'Zak';
If you get a result the Where clause is fine. So now you run:
SELECT *
FROM user us
LEFT JOIN user_role ur
ON us.uid = ur.uid
WHERE us.firstname = 'Zak';
If yuo get no records then there is something wrong with the join. Could be that they have some unprintable characters and 4000 <>4000 as a result. So let's check that.
select * FROM user us where us.uid = 4000
select * FROM user_role us where us.uid = 4000
If one of then does not return a result then there is a data problem where one of the fields contains unprintable characters.
If the select * works, then try the original query again only add a few other fields from the user_role table such as the uid. Then you can see if the join is working but the field is empty or if the join is wrong or possibly you are looking at the wrong field.
SELECT us.firstname, us.lastname, ur.oid, ur.uid
FROM user us
LEFT JOIN user_role ur
ON us.uid = ur.uid
WHERE us.firstname = 'Zak';
It is also possible the join fields are different datatypes and some type of implicit conversion is messing them up. In that case you probably want to explicitly convert or preferably design your table so that they both use the same data type as they appear to be in a PK/FK relationship.

MySQL: Multiple ids in one row from another table

This may be a bit convoluted but I'll give it a shot.
I have a table that has event data:
| id | event | from_location | to_location |
| 1 | move | 12 | 14 |
| 2 | move | 13 | 15 |
and the from location and to location are ids referenced in another able
| id | name |
| 12 | london |
| 13 | paris |
| 14 | newyork |
| 15 | tokyo |
My issue is I need to search both the first table based on the location, but using the name in the second, and I'd like to do it as simply as possible with one query.
If it was one column, I could just do a join and have the name available but since it is two, this doesn't work.
I could search for the name in the first table, then having the id, use that to search the other table - but I'd like to do it in one query.
So my question - is there a way to simply replace the ids with the corresponding name - then do the search, all in one query?
I would say one more thing - I didn't set this up. if I had, I'd have forgone the use of ids altogether and simply used the names as the keys. But now that it is how it is - lets assume I can't change this.
Thanks
If you want to check if the from or to location is a particular one:-
SELECT event_data.id, event_data.event, event_data.from_location, event_data.to_location
FROM event_data
INNER JOIN locations l1 ON event_data.from_location = l1.id
INNER JOIN locations l2 ON event_data.to_location = l2.id
WHERE l1.name = 'somewhere'
OR l2.name = 'somewhere'
If you want a particular from location and another particular to location
SELECT event_data.id, event_data.event, event_data.from_location, event_data.to_location
FROM event_data
INNER JOIN locations l1 ON event_data.from_location = l1.id
INNER JOIN locations l2 ON event_data.to_location = l2.id
WHERE l1.name = 'somewhere'
AND l2.name = 'somewhereelse'

Conditional field in mysql join query result

I have following two tables
1. publisher_site_regionwise_adratio
publisher_id | site | region | ad_ratio | product_code
=========================================================
001 | xyz.com | US | 8:2 | TB
and
2. publisher_site_regionwise_info
publisher_id | site | region | regional_keywords
=======================================================
001 | xyz.com | US | business, warehouse
Now second table has data that is not product_code wise. Regional info for sites is irrespective of product_code for a publisher with a particular site for a particular region. Now I want a query that will give me following fields
site | region | ad_ratio | has_regional_info
============================================
xyz.com | US | 8:2 | 1
has_regional_info column will have 0 or 1 depending upon whether a site in a particular region has regional_keywords mapped or not.
I can't possibly imagine how I can use JOIN and get such result. Any help will be very appreciated.
I would take a simple JOIN into your info table, then check if there is data in the regional_keywords column to determine your 1 or 0 for has_regional_info
SELECT ar.site, ar.region, ar.ad_ratio
, CASE
WHEN i.regional_keywords IS NOT NULL THEN 1
ELSE 0
END AS has_regional_info
FROM publisher_site_regionwise_adratio AS ar
LEFT JOIN publisher_site_regionwise_info AS i ON ar.publisherId = i.publisherId
If your foreign key between publisher_site_regionwise_adratio and publisher_site_regionwise_info is more than just publisherId (difficult to tell with 3 repeated columns in your example) then just add these to the ON statement of the join like: AND ar.otherColumn = i.otherColumn
SELECT ratio.site, ratio.region, ratio.ad_ration,
IF(info.has_regional_info IS NULL, true, false)
FROM publisher_site_regionwise_adratio AS ratio
LEFT JOIN publisher_site_regionwise_info AS info
ON ratio.publisher_id = info.publisher_id
select publisher_site_regionwise_adratio.site,
publisher_site_regionwise_adratio.region,
publisher_site_regionwise_adratio.ad_ratio,
LENGTH(regional_keywords)>0 as has_regional_info
from publisher_site_regionwise_adratio
join publisher_site_regionwise_info.publisher_id
on publisher_site_regionwise_adratio.publisher_id
= publisher_site_regionwise_info.publisher_id
This should do:
SELECT a.publisher_id, a.site, a.ad_ratio,
i.regional_keywords IS NOT NULL AS has_regional_keywords
FROM publisher_site_regionwise_adratio a, publisher_site_regionwise_info i
WHERE a.publisher_id = i.publisher_id;