JOIN 4 Tables with meta_table - mysql

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'
;

Related

how to search multiple tables according to criteria

I want you to give me a hand. My idea is to be able to search according to criteria. these criteria are related tables.
where I have a projects table and this has a relationship with the populations and departments table.
enter image description here
table projects
id | name | year |
4 | proyect 1 | 2019 |
6 | proyect 2 | 2020 |
table populations_project
id | project_id | name |
1 | 4 | rural |
2 | 6 | city |
table departments_project
id | project_id | name |
1 | 4 | florida |
2 | 6 | california |
the result
p_p = populations_project
d_p = departments_project
result projects
id | name | year | p_p | p_p | d_p_id | d_name
4 | proyect 1 | 2019 | City | 1 | 1 | florida
search
find 2019 and florida and city
and my inicial sql is
SELECT * FROM `projects` WHERE year BETWEEN '2019' and '2020'
with this filter the year and the table projects
see the image please
Try this one:
SELECT p.id, p.name, p.year, pp.name, pp.id, dp.id, dp.name
FROM projects p
join departments_projects dp on p.id = dp.project_id
join population_projects pp on p.id = pp.project_id
WHERE
dp.name='florida' AND
pp.name='city' AND
p.year = 2019;
Let us know if it returns your expected result
Maybe you need join table?
select *
from projects p
join populations_project pp on p.id = pp.project_id
join departments_project dp on p.id = dp.project_id
where dp.name = 'florida'
and pp.name = 'city'
and p.year BETWEEN '2019' and '2020'
SqlFiddle
In you data for proyect 1 and populations_project.name = 'city' and departments_project.name = 'florida' doesn't exists relation

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

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`

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.

Selecting 2 columns from a MySQL db with join

I have the following tables:
Teams
+------+--------------+--------------+
| id | team_name | team_code |
+------+--------------+--------------+
| 1 | Wales | WAL |
| 2 | England | ENG |
| 3 | New Zealand | NZL |
+------+--------------+--------------+
Matches
+------+-----------+-----------+
| id | team_a | team_b |
+------+-----------+-----------+
| 1 | WAL | ENG |
| 2 | ENG | NZL |
| 3 | WAL | NZL |
+------+-----------+-----------+
I know how a join works, but I can't get my head around how I can query to the database by Matched.id to get the team name for BOTH teams from the Teams database. Let me explain better
For each match I need to determine the team names
I will query by Matches.id
So for Match: 1 I'll need to select 'Wales' and 'England'
Hope I've explained my problem clearly enough, but If I haven't please feel free to ask more questions
The database structure seems to be "off" to me.
You should have both team_a and team_b as foreign keys to the Teams table primary key.
You can then join on the IDs (twice) to get the full names.
Matches:
+------+-----------+-----------+
| id | team_a | team_b |
+------+-----------+-----------+
| 1 | 1 | 2 |
| 2 | 2 | 3 |
| 3 | 1 | 3 |
+------+-----------+-----------+
SELECT T1.team_name, T2.team_name
FROM Matches M
INNER JOIN Teams T1
ON M.team_a = T1.id
INNER JOIN Teams T2
ON M.team_b = T2.id
WHERE M.id = 1
Select
matches.*,
teamA.team_name as TeamA_Name,
teamB.team_name as TeamB_Name
from matches
inner join teams teamA on matches.team_a = TeamA.team_code
inner join teams teamB on matches.team_b = TeamB.team_code
where
matches.id = 1
select
m.id,
t1.team_name,
t2.team_name
from
Matches m
inner join Teams t1 on m.team_a = t1.team_code
inner join Teams t2 on m.team_b = t2.team_code

What query to obtain the following on MySQL?

Users
+--------+--------------+----------+--------------+
| userID | userUsername | userName | userLastName |
+--------+--------------+----------+--------------+
| 6 | richard | Ricardo | Vega |
| 10 | jason | Jason | Bourne |
+--------+--------------+----------+--------------+
Restocks
+-----------+-------------+--------+--------+-----------------+
| restockID | restockDate | itemID | userID | restockQuantity |
+-----------+-------------+--------+--------+-----------------+
| 1 | 2012-02-29 | 1 | 6 | 48 |
| 2 | 2012-02-29 | 1 | 10 | 100 |
| 3 | 2012-02-29 | 2 | 10 | 50 |
| 4 | 2012-02-29 | 2 | 6 | 100 |
| 5 | 2012-02-29 | 2 | 6 | 200 |
| 6 | 2012-02-29 | 2 | 10 | 2000 |
| 7 | 2012-02-29 | 1 | 10 | 2000 |
+-----------+-------------+--------+--------+-----------------+
Items
+--------+--------------------+
| itemID | itemName |
+--------+--------------------+
| 1 | Coca Cola (lata) |
| 2 | Cerveza Sol (lata) |
+--------+--------------------+
Ok guys, i have supplied some sample data as requested. I need to get this table:
+--------+--------------------+---------------+-------------+----------+--------------+--------------+
| itemID | itemName | itemExistence | restockDate | userName | userLastName | userUsername |
+--------+--------------------+---------------+-------------+----------+--------------+--------------+
| 2 | Cerveza Sol (lata) | 2350 | 2012-02-29 | Jason | Bourne | jason |
| 1 | Coca Cola (lata) | 2148 | 2012-02-29 | Ricardo | Vega | richard |
+--------+--------------------+---------------+-------------+----------+--------------+--------------+
But, i need restockDate to be THE LATEST ONE for each itemName. In the example, it shows the first restock and not the latest one. I just need to show what's the existence for the item and when was restocked for last time, not first time.
If my tables are not good or so, please suggest a new schema.
I know maybe this is a lot so i will tip 5 USD (Paypal) to the one how can help me with this. Promise.
As discussed in comments, many restocks can be performed on the same day so it is not possible to compare dates in this case. Two options are presented here: Use the incremental PK from restocks table or restructure the table. For the first case, this is the solution:
select i.itemID, i.itemName, i.itemExistence, r.restockDate, u.userName,
u.userLastName, u.userUsername
from items i
left join (
select r1.restockDate, r1.itemID, r1.userID from restocks r1
left join restocks r2
on r1.itemId = r2.itemId and r1.restockId < r2.restockId
where r2.restockDate is null
) as r on i.itemID = r.itemID
inner join users u on r.userID = u.userID
For the second case, the restructre would imply changing the date field to a unique datetime that would uniquely identify a record. That is the best solution, however, it does require to also update any previous data present in the table. That means, to update all the records that have the same date for a single product restock and set different date times to them.
The lazy one (like me), would go for the first option :) Let me know if you have any doubt about this.
first get the distinct from items table and then use it to join others
SELECT items.*, restocks.restockDate, users.userName, users.userLastName, users.userUsername
FROM (SELECT DISTINCT items.itemID, items.itemName, items.itemExistence FROM items) AS items
LEFT JOIN restocks on items.itemID = restocks.itemID
LEFT JOIN users on restocks.userID = users.userID
GROUP BY items.itemName
Not Tested
UPDATED
select items.itemID, items.itemName, items.itemExistence, restocks.restockDate, users.userName, users.userLastName, users.userUsername
from items
inner join restocks on items.itemID = restocks.itemID
inner join users on restocks.userID = users.userID
GROUP BY items.itemName
select
items.itemID, items.itemName, items.itemExistence,
(select A.restockDate from restocks A where A.itemId = items.itemID limit 0, 1),
(select B.userID from restocks B where B.itemId = items.itemID limit 0, 1),
users.userName, users.userLastName, users.userUsername
from items
left join users on B.userID = users.userID
Please try this.
You don't mention what itemExistence is, so I'm hoping it's a column in the Items table.
Here's an easy way to do it with a self-join:
SELECT i.itemID, i.itemName, i.itemExistence, r1.restockDate,
u.userName, u.userLastName, u.userUsername
FROM Items i
JOIN Restocks r1
ON r1.itemID = i.itemID
JOIN Users u
ON u.userID = r1.userID
LEFT JOIN Restocks r2
ON r2.itemID = i.itemID
AND r2.restockDate > r1.restockDate
WHERE r2.itemID IS NULL
The LEFT JOIN with the WHERE clause ensures that we only pull the row with the latest restockDate.
The advantage of this approach is that it avoids subqueries, which often negate the use of indexes.
You can get duplicate records for a particular item if it was restocked more than once on the same date.