Say I have a database that has people, grocery stores, and items you can buy in the store, like so:
Stores People Foods
----------------- ------------------ ------------------
| id | name | | id | name | | id | name |
----------------- ------------------ ------------------
| 1 | Giant | | 1 | Jon Skeet | | 1 | Tomatoes |
| 2 | Vons | | 2 | KLee1 | | 2 | Apples |
| 3 | Safeway | ------------------ | 3 | Potatoes |
----------------- ------------------
I have an additional table which keep track of which stores sell what:
Inventory
--------------------
| store_id| food_id|
--------------------
| 1 | 1 |
| 1 | 2 |
| 2 | 1 |
| 3 | 1 |
| 3 | 2 |
| 3 | 3 |
--------------------
And I have another table that has shopping lists on it
Lists
---------------------
| person_id| food_id|
---------------------
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
| 2 | 1 |
| 2 | 3 |
---------------------
My question is, given a person, or their id, what is the best way to figure out what stores they can go to so they will get everything on their list. Is there a pattern for these types of computations in MySQL?
My attempt (very ugly and messy) is something like:
-- Given that _pid is the person_id we want to get the list of stores for.
SELECT stores.name, store_id, num, COUNT(*) AS counter
FROM lists
INNER JOIN inventory
ON (lists.food_id=inventory.food_id)
INNER JOIN (SELECT COUNT(*) AS num
FROM lists WHERE person_id=_pid
GROUP BY person_id) AS T
INNER JOIN stores ON (stores.id=store_id)
WHERE person_id=_pid
GROUP BY store_id
HAVING counter >= num;
Thanks for your time!
Edit SQL Fiddle with Data
If I were to solved the problem, I'll join the four tables with their linking column (specifically the foreign keys) then a subquery on the HAVING clause to count the number of items on the list for each person. Give this a try,
SET #personID := 1;
SELECT c.name
FROM Inventory a
INNER JOIN Foods b
ON a.food_id = b.id
INNER JOIN Stores c
ON a.store_id = c.id
INNER JOIN Lists d
ON d.food_id = b.id
WHERE d.person_id = #personID
GROUP BY c.name
HAVING COUNT(DISTINCT d.food_id) =
(
SELECT COUNT(*)
FROM Lists
WHERE person_ID = #personID
)
SQLFiddle Demo
#JohnWoo: why DISTINCT?
Another one...
SET #pid=2;
SELECT store_id, name
FROM inventory
JOIN lists ON inventory.food_id=lists.food_id
JOIN stores ON store_id=stores.id
WHERE person_id=#pid
GROUP BY store_id
HAVING COUNT(*)=(
SELECT COUNT(*)
FROM lists
WHERE person_id=#pid
);
Related
We have 3 tables :
donations
purposes
expenses
Donations :
+--------+------+
| do_id | name |
+--------+------+
| 1 | A |
| 2 | B |
| 3 | A |
| 4 | D |
| 5 | B |
| 6 | B |
| 7 | A |
| 8 | B |
+--------+----- +
purposes:
+-------+-------+--------+
| pu_id | do_id | purpose|
+-------+-------+--------+
| 1 | 2 | abc |
| 2 | 2 | def |
| 3 | 2 | gih |
| 4 | 3 | jkl |
+-------+-------+--------+
expense :
+-------+-------+---------+
| ex_id | do_id | expense |
+-------+-------+---------+
| 1 | 2 | abc |
| 2 | 2 | def |
| 3 | 2 | gih |
| 4 | 3 | jkl |
+-------+-------+---------+
Now i want to make query to get all donations for donor B and join purposes table to get all purposes related to every donation_id then join expenses table to get all expenses related to donation_id and put all of that in every loop independently something like that
Row number 0
donation_id = 1
array(purposes)
array(expenses)
Row number 1
donation_id = 2
array(purposes)
array(expenses)
Row number 2
donation_id = 3
array(purposes)
array(expenses)
Row number 3
donation_id = 4
array(purposes)
array(expenses)
This is my try :
SELECT *, (
SELECT *
FROM `donation_purposes`
WHERE `donation_purposes`.`dopu_donation_id` = 4
) AS `purposes`
FROM `donations`
WHERE `donation_id` = '4'
thanks in advance
You should be able to solive this with an aggregate query using MySQL aggregate function JSON_ARRAYAGG(), like :
SELECT
d.do_id,
JSON_ARRAYAGG(p.purpose) purposes,
JSON_ARRAYAGG(e.expense) expenses
FROM donations d
INNER JOIN purposes p ON p.do_id = d.do_id
INNER JOIN expense e ON e.do_id = d.do_id
GROUP BY d.do_id
I you want to avoid duplicate values in the array, and as JSON_ARRAYAGG() (sadly) does not support the DISTINCT option, you can move aggregation to subqueries, like :
SELECT
d.do_id,
p.agg purpose,
e.agg expenses
FROM donations d
INNER JOIN (
SELECT do_id, JSON_ARRAYAGG(purpose) agg FROM purposes GROUP BY do_id
) p ON p.do_id = d.do_id
INNER JOIN (
SELECT do_id, JSON_ARRAYAGG(expense) agg FROM expense GROUP BY do_id
) e ON e.do_id = d.do_id
This demo on DB Fiddle returns :
| do_id | purpose | expenses |
| ----- | --------------------- | --------------------- |
| 2 | ["abc", "def", "gih"] | ["abc", "def", "gih"] |
| 3 | ["jkl"] | ["jkl"] |
1st Select Query Purposes
SELECT purposes.* FROM purposes
LEFT JOIN donations
ON purposes.do_id = donations.do_id
WHERE donations.do_id = '2' //This depends on the id of the donation
ORDER BY purposes.do_id ASC
2nd Select Query Expenses
SELECT expense.* FROM expense
LEFT JOIN donations
ON expense.do_id = donations.do_id
WHERE donations.do_id = '2' //This depends on the id of the donation
ORDER BY expense.ex_id ASC
All queries generated are from the table structure you've provided, but your question is quite vague!!
I am trying to get values matching the value from the second column. For example, I want to know who is the sender for Bill Gates by only using IDs.
I have two tables,
*users* table
| user_ID | Full_name |
| -------- | -------------- |
| 1 | Steve Jobs |
| 2 | Bill Gates |
| 3 | Elon Musk |
*relationships* table (with both column foreign keys)
| user_sender | user_receiver |
| ------------ | -------------- |
| 1 | 2 |
| 3 | 1 |
| 3 | 2 |
I want to select based on "user_receiver" column the matching values in the column "user_sender"
For example, I want to know who is user_sender for 2
OUTPUT:
| | |
| ------------ | -------------- |
| 1 | 2 |
| 3 | 2 |
You need to join the tables and select the rows you want
you have access to all columns of both tables by addressing them with their alias
SELECT u.user_ID , u.Full_name,r.user_receiver
FROM users u JOIN
relationships r ON u.user_ID = r.user_sender
WHERE r.user_receiver = 2
If you want to look based on the name, then join the relationships to users.
SELECT
rel.user_sender
, rel.user_receiver
-- , sender.Full_name AS sender_name
-- , receiver.Full_name AS receiver_name
FROM relationships AS rel
JOIN users AS sender ON sender.user_ID = rel.user_sender
JOIN users As receiver ON receiver.user_ID = rel.user_receiver
WHERE receiver.Full_name = 'Bill Gates'
If you already know the user_receiver number, and you only want the ID's
SELECT *
FROM relationships
WHERE user_receiver = 2
I have a table A that contains tree columns, id, users ids and vehicle id. And a table B that contains vehicleid, and vehicle name.
Table A
---------------------------
| Id | User_id |Vehicle_id|
---------------------------
| 1 | 1 | 2 |
| 2 | 1 | 3 |
| 3 | 1 | 4 |
| 4 | 2 | 2 |
| 5 | 2 | 3 |
| 6 | 4 | 5 |
---------------------------
Table B
-------------------
| Id |Vehicle_name|
-------------------
| 1 | Car |
| 2 | Bike |
| 3 | Plane |
| 4 | Boat |
| 5 | Rocket |
-------------------
Given a user id, I need to get all vehicle names, that doesn't match with table A. I've tried Outer joins, but I can't manage to do get the info that i need.
For example: Given user id 1, the query should return Car and Rocket.
thanks in advance
This is simple enough using not in or not exists:
select b.*
from b
where not exists (select 1
from a
where a.vehicle_id = b.id and a.user_id = #a_user_id
);
I also thought of using a cross join and was able to get the output in case you are more comfortable with join logic.
SELECT CJOIN.USER_ID, CJOIN.VEHICLE_ID, CJOIN.VEHICLE_NAME
FROM
(SELECT DISTINCT A.USER_ID, B.ID AS VEHICLE_ID, B.VEHICLE_NAME FROM TABLE_A A CROSS JOIN TABLE_B B) CJOIN
LEFT JOIN
TABLE_A D
ON CJOIN.USER_ID = D.USER_ID AND CJOIN.VEHICLE_ID = D.VEHICLE_ID
WHERE D.USER_ID IS NULL AND D.VEHICLE_ID IS NULL;
First, I got all possible combinations of USER_ID x VEHICLE_ID by a cross join and used this table in a left join to pull records for which there is no match.
It's the 3rd day I'm trying to write a MySQL query. Did lots of search, but it still doesn't work as expected. I'll try to simplify tables as much as possible
System has tkr_restaurants table:
restaurant_id | restaurant_name
1 | AA
2 | BB
3 | CC
Each restaurant has a division assigned (tkr_divisions table):
division_id | restaurant_id | division_name
1 | 1 | AA-1
2 | 1 | AA-2
3 | 2 | BB-1
Then there are meals in tkr_meals_to_restaurants_divisions table, where each meal can be assigned (mapped) to whole restaurant(s) and/or specific division(s). If meal is mapped to restaurant, all restaurant's divisions should see it. If meal is mapped to division(s), only specific division(s) should see it.
meal_id | mapped_restaurant_id | mapped_division_id
1 | 1 | NULL
2 | NULL | 1
3 | NULL | 2
I need to display a list of restaurants and number of meals mapped to it depending on user permissions.
Example 1: if user has permissions to access whole restaurant_id 1 and restaurant_3 (and no specific divisions), then list should be:
AA | 3
CC | 0
(because user can access meals mapped to restaurant 1 + all its division, and restaurant 3 + all its divisions (even if restaurant 3 has no divisions/meals mapped))
Example 2: if user has permissions to access only division_id 1, then list should be:
AA | 1
(because user can only access meals mapped to division 1).
The closest query I could get is:
Example 1:
SELECT *,
(SELECT COUNT(DISTINCT meal_id)
FROM
tkr_meals_to_restaurants_divisions
WHERE
tkr_meals_to_restaurants_divisions.mapped_restaurant_id=tkr_restaurants.restaurant_id
OR tkr_meals_to_restaurants_divisions.mapped_division_id=tkr_divisions.division_id)AS total_meals
FROM
tkr_restaurants
LEFT JOIN
tkr_divisions
ON tkr_restaurants.restaurant_id=tkr_divisions.restaurant_id
WHERE
tkr_restaurants.restaurant_id IN (1, 3)
OR tkr_restaurants.restaurant_id IN (
SELECT restaurant_id
FROM tkr_divisions
WHERE division_id IN (NULL)
)
GROUP BY
tkr_restaurants.restaurant_id
ORDER BY
tkr_restaurants.restaurant_name
However, result was:
AA | 2
CC | 0
I believe I'm greatly over-complicating this query, but all the simpler queries I wrote produced even more inaccurate results.
What about this query:
SELECT
FROM tkr_restaurants AS a
JOIN tkr_divisions AS b
ON a.restaurant_id = b.restaurant_id
LEFT OUTER JOIN tkr_meals_to_restaurants_divisions AS c
ON (c.mapped_restaurant_id = a.restaurant_id OR c.mapped_division_id = b.division_id)
As a Base four your further work. It combine all information into one table. If you add e.g. this:
WHERE a.restaurant_id IN (1, 3)
the result will be
| restaurant_id | restaurant_name | division_id | restaurant_id | division_name | meal_id | mapped_restaurant_id | mapped_division_id |
|---------------|-----------------|-------------|---------------|---------------|---------|----------------------|--------------------|
| 1 | AA | 1 | 1 | AA-1 | 1 | 1 | (null) |
| 1 | AA | 2 | 1 | AA-2 | 1 | 1 | (null) |
| 1 | AA | 1 | 1 | AA-1 | 2 | (null) | 1 |
| 1 | AA | 2 | 1 | AA-2 | 3 | (null) | 2 |
just count the distinct meal ids with COUNT(DISTINCT c.meal_id) and take the restaurant name to get AA: 3 for your example 2
I used a sqlfiddle: http://sqlfiddle.com/#!9/fa2b78/18/0
[EDIT]
Change JOIN tkr_divisions AS b to LEFT OUTER JOIN tkr_divisions AS b
Change SELECT * to SELECT a.restaurant_name, COUNT(DISTINCT c.meal_id)
Add a GROUP BY a.restaurant_name at the end.
Update the SQL Fiddle (new link)
Hello there I want to get data from two tables that share same column name. My table structure are
Table patients
---------------------------------------
| id | affiliate_id | somecolumn |
---------------------------------------
| 1 | 8 | abc |
---------------------------------------
| 2 | 8 | abc |
---------------------------------------
| 3 | 9 | abc |
---------------------------------------
Table Leads
---------------------------------------
| id | affiliate_id | someothern |
---------------------------------------
| 1 | 8 | xyz |
---------------------------------------
| 2 | 8 | xyz |
---------------------------------------
| 3 | 3 | xyz |
---------------------------------------
Now my requirement was to get COUNT(ID) from both tables in a single query. I want result like
----------------------------------------------------
| affiliate_id | total_patients | total_leads |
----------------------------------------------------
| 8 | 2 | 2 |
----------------------------------------------------
| 9 | 1 | 0 |
----------------------------------------------------
| 3 | 0 | 1 |
----------------------------------------------------
I wrote following query
SELECT `p`.`affiliate_id`, COUNT(p.id) AS `total_patients`,
COUNT(cpl.id) AS `total_leads`
FROM `patients` AS `p`
INNER JOIN `leads` AS `cpl` ON p.affiliate_id =cpl.affiliate_id
GROUP BY `p`.`affiliate_id`
But I am not getting result . This query results giving only one affiliate with same number of total_patients and total_leads
The problem is that you need to get a list of the distinct affiliate_id first and then join to your other tables to get the result:
select a.affiliate_id,
count(distinct p.id) total_patients,
count(distinct l.id) total_leads
from
(
select affiliate_id
from patients
union
select affiliate_id
from leads
) a
left join patients p
on a.affiliate_id = p.affiliate_id
left join leads l
on a.affiliate_id = l.affiliate_id
group by a.affiliate_id;
See SQL Fiddle with Demo
Two ways:
Select l.affiliate_id ,
count(distinct p.id) patientCount,
count(distinct l.id) LeadCOunt
From patients p Join leads l
On l.affiliate_id = p.Affiliate_id
Group By l.affiliate_id
or, (assuming affiliates are in their own table somewhere)
Select Affiliate_id,
(Select Count(*) From Patients
Where Affiliate_id = a.Affiliate_id) patientCount,
(Select Count(*) From Leads
Where Affiliate_id = a.Affiliate_id) LeadCount
From affiliates a