Select all if no subset is present, otherwise select subset - mysql

Ok here's my problem. Assume a customer has access to a number of regions defined in a CustomerRegions table:
CustomerRegionID | CustomerID | RegionID
----------------------------------------
1 | 1 | 1
2 | 1 | 2
Assume that customer 1 has three users 1, 2, and 3. For each user we can specify to which of the CustomerRegions they have access via a table UserRegions:
UserRegionID | UserID | CustomerRegionID
----------------------------------------
1 | 1 | 1
2 | 1 | 2
3 | 2 | 2
So user 1 will have access to both Customerregions and user 2 will only have access to CustomerRegion 2.
If there are UserRegions specified for a given user then only those CustomerRegions are present in the result set, but if no UserRegions are specified for a given user then all CustomerRegions are present in the result. I want to get all accessible regions per user of a given customer. The result I am looking for is something like this:
CustomerID | UserID | RegionID
------------------------------
1 | 1 | 1
1 | 1 | 2
1 | 2 | 2
1 | 3 | 1
1 | 3 | 2
My question is can this be done in a single query and how?
Edit:
I seem to have it working now:
SELECT CustomerID,
UserID,
RegionID
FROM users
LEFT JOIN customerregions ON customerregions.CustomerID = users.CustomerID
LEFT JOIN userregions ON userregions.UserID = users.UserID AND userregions.CustomerRegionID = customerregions.CustomerRegionID
LEFT JOIN regions ON regions.RegionID = customerregions.RegionID
WHERE (userregions.UserID IS NOT NULL
OR (SELECT COUNT(1) FROM userregions WHERE userregions.UserID = users.UserID) = 0)
AND CustomerID = 1
The extra count query in the where seems to do the trick. Thanks #Pablo Martinez for your help. However if someone knows of a better way to do this please let me know.

I'm aggre with #diEcho, the table structure is very confusing
have you try to do a join?
Select CustomerID, UserID, RegionID
from UserRegions join CustomerRegion
on CustomerRegion.CustomerRegionID=UserRegions.CustomerRegionID
where customerID=1

Related

MySQL add avg of count by id to existing select with id

Im not even sure what the title of this question should be but lets start out with my data.
I have a table of users who have taken a few lessons while belonging to a particular training center.
lesson table
id | lesson_id | user_id | has_completed
----------------------------------------
1 | asdf3314 | 2 | 1
2 | d13saf12 | 2 | 1
3 | a33adff5 | 2 | 0
4 | a33adff5 | 1 | 1
5 | d13saf12 | 1 | 0
user table
id | center_id | ...
----------------------------------------
1 | 20 | ...
2 | 30 | ...
training center table
id | center_name | ...
----------------------------------------
20 | learn.co | ...
30 | teach.co | ...
I've written a small chunk but am now stuck as I don't know how to proceed. This statement gets the counted total of completed lessons per user. it then figures the average completed value from a center id. if two users belong to a center and have completed 3 lessons and 2 lessons it finds the average of 3 and 2 then returns that.
SELECT
FLOOR(AVG(a.total)) AS avg_completion,
FROM
(SELECT
user_id,
user.center_id,
count(user_id) AS total
FROM lesson
LEFT JOIN user ON user.id = user_id
WHERE is_completed = 1 AND center_id = 2
GROUP BY user_id) AS a;
The question I have is how do I loop through the training centers table and also append average data from similar select statement as above to each center that is queried. I cant seem to pass the center id down to the subquery so there must be a fundamentally different way to achieve the same query but also loop through training centers.
An example of desired result:
center.id | avg_completion | ...training center table
-----------------------------------------------------
20 | 2 | ...
Your main query needs to select a.center_id and then use GROUP BY center_id. You can then join it with the training_center table.
SELECT c.*, x.avg_completion
FROM training_center AS c
JOIN (
SELECT
a.center_id,
FLOOR(AVG(a.total)) AS avg_completion
FROM (
SELECT
user_id
user.center_id,
count(*) AS total
FROM lesson
JOIN user ON user.id = user_id
WHERE is_completed = 1 AND center_id = 2
GROUP BY user_id) AS a
GROUP BY a.center_id) AS x
ON x.center_id = c.id
If I understand correctly:
select u.center_id, count(*) as num_users,
sum(l.has_completed) as num_completed,
avg(l.has_completed) as completed_ratio
from lesson l join
user u
on l.user_id = u.id
group by u.center_id

Make an alias column with custom string in MySQL

I have a MySQL tables like this:
##customer##
+-----------+----+---------+
|customer_id|name|telephone|
+-----------+----+---------+
| 1 |Andi|+62932011|
| 2 |Boby|+62928291|
| 3 |Jane|+62932212|
| 4 |John|+62999021|
| 5 |Beth|+62999021|
| 6 |Noel|+62999021|
+-----------+----+---------+
##plus_membership##
+-----------------+-----------+-------+------------+
|plus_membership_id|customer_id|status |requested_at|
+------------------+-----------+-------+------------+
| 1 | 1 | 1 | 2018-11-01 |
| 2 | 2 | 0 | 2018-11-03 |
| 3 | 4 | 2 | 2018-11-04 |
| 4 | 6 | 1 | 2018-11-05 |
+------------------+-----------+-------+------------+
there are two tables in above structure, the first is the customer with customer_id as the primary key and the second one is the plus_membership which has foreign key customer_id, the plus_membership table is a table to show a request if customer request to become a plus member, status 1 means the customer is apporved to be plus member. I need to select the customer table and add alias column lets say the alias column name is membership , that shows only regular or plus , plus means the customer in plus_membership status is 1, and regular if customer doesnt exist in plus_membership table or status is not 1 in membership table. for example:
SELECT *, .... AS membership FROM customer;
+-----------+----+---------+----------+
|customer_id|name|telephone|membership|
+-----------+----+---------+----------+
| 1 |Andi|+62932011| Plus |
| 2 |Boby|+62928291| Regular |
| 3 |Jane|+62932212| Regular |
| 4 |John|+62999021| Regular |
| 5 |Beth|+62999021| Regular |
| 6 |Noel|+62999021| Plus |
+-----------+----+---------+----------+
You can use Left Join between the two tables, and use Case .. When conditional expressions to evaluate membership accordingly.
Left Join would ensure that all the customer(s) from the customer table are considered, whether they have a corresponding matching row in the plus_membership table or not.
SELECT
c.customer_id,
c.name,
c.telephone,
(CASE WHEN pm.status = 1 THEN 'Plus' ELSE 'Regular' END) AS membership
FROM customer AS c
LEFT JOIN plus_membership AS pm
ON pm.customer_id = c.customer_id
Another approach can be using Correlated Subquery and Exists(). Generally, this would be less efficient than Left Join approach.
SELECT
c.customer_id,
c.name,
c.telephone,
CASE WHEN EXISTS (SELECT 1
FROM plus_membership AS pm
WHERE pm.customer_id = c.customer_id AND
pm.status = 1
)
THEN 'Plus'
ELSE 'Regular'
END AS membership
FROM customer AS c
We use EXISTS or IN to look up data in another table.
select customer_id, name, telephone,
case when customer_id in (select customer_id from plus_membership where status = 1)
then 'Plus' else 'Regular' end as membership
from customer
order by customer_id;

Mysql: Show all users that a user is following and add dynamic column if i also follow that user

I have a website with a twitter-like follower system.
I have a mySQL-table "followers", like this:
'followers' TABLE STRUCTURE:
| user_id | follow_id |
--------------------------
| 1 | 2 |
| 1 | 3 |
| 2 | 3 |
| 2 | 4 |
Now I want to get all the users listed that f.e. user 2 is following (so 3 and 4) but I also want to show if the current user logged in (let's assume user 1) is also following a user. This information should be added dynamically to a new column "ifollow", like this:
Expected result when checking which users "user 2" is following:
| user_id | follow_id | ifollow |
---------------------------------------
| 2 | 3 | true |
| 2 | 4 | false |
Right now I just get all the users a user is following and then check in the view with a function like "doIFollow($id)" to display the right follow/unfollow-buttons but I think it would be way better to just get all the needed information with the query. Does anyone know how this ist possible?
You can use a correlated subquery for this:
SELECT user_id, follow_id,
COALESCE((SELECT true
FROM followers AS f2
WHERE f2.follow_id = f1.follow_id AND
f2.user_id = 1), false)
FROM followers AS f1
WHERE user_id = 2
Edit: (credit goes to #Mjh)
The same thing can also be accomplished with a simple LEFT JOIN operation:
SELECT f1.user_id, f1.follow_id,
IF(f2.user_id IS NULL, false, true) AS ifollow
FROM followers AS f1
LEFT JOIN followers AS f2
ON f1.follow_id = f2.follow_id AND f2.user_id = 1
WHERE f1.user_id = 2

MySQL Join two tables with condition

Based on these two tables:
products
| ID | Active | Name | No
--------------------------------------------------
| 1 | 1 | Shirt | 100
| 2 | 0 | Pullover | 200
variants
| MasterID | Active | Name | No
--------------------------------------------------
| 1 | 1 | Red | 101
| 1 | 0 | Yellow | 102
I want to get every product which is active and also their active variants in one sql.
Relation between those tables MasterID -> ID
Needed result:
ID (master) | Name | No
--------------------------------------------------
1 | Shirt | 100
1 | Red | 101
I tried it with using union, but then I am not able to get the belonging MasterIDs.
It looks like you just need a simple join:
select *
from products
left join variants
on products.ID = variants.MasterID
where products.Active = 1
and variants.Active = 1
Update after requirements were made clearer:
select ID, Name, No, 'products' as RowType
from products
where Active = 1
union
select variants.MasterID as ID, variants.Name, variants.No, 'variants' as RowType
from products
join variants
on products.ID = variants.MasterID
where products.Active = 1
and variants.Active = 1
order by ID, RowType, No
I've assumed you want the results ordered by ID, with products followed by variants. The No column may order it this way implicitly (it's impossible to know without real data), in which case the RowType column can be removed. The order by clause might need to be altered to match your specific RDBMS.
This should gives you the expected result:
select * from products left join variants on products.id = variants.masterId
where products.active=1 and variants.active=1
If not please add the expected result to your question.

How to select/match from MySQL table?

I have the following table:
People
---------
ID | Name
---------
1 | John
2 | Sam
And I have another table:
Permissions
-----------
ID | Perm
-----------
1 | View
2 | Edit
3 | Delete
These two tables are linked in a third table:
UserPermissions
----------------------
ID | User | Permission
----------------------
1 | 1 | 1 (View)
2 | 1 | 3 (Delete)
3 | 2 | 1 (View)
I am trying to select a "total" permissions type table, where, if I wanted to get the permission for a user (Lets say user 2 (Sam)), I would get the following table:
UserPermissions
------------------
Permission | User
------------------
1 (View) | 2
2 (Edit) | NULL (Or some other nullish value)
3 (Delete)| NULL
I have only recently started MySQL and I have no idea of what search terms I should be trying to get examples of similar queries. Does anyone know what type of queries I should be searching for / a way to implement this?
If you want to do this for one user, you want a left outer join:
select p.*, up.user;
from Permissions p left outer join
UserPermissions up
on p.Permission = up.Permission and
up.User = 2;
This works for one user.
Sounds like you're just looking for an OUTER JOIN:
SELECT P.Id, P.Perm, UP.Id User
FROM Permissions P
LEFT JOIN UserPermissions UP ON P.Id = UP.Permission