How can this be done in a single select statement? - mysql

I've got these three tables in the DB & I want to select the event_name for a specific userID from t1event given that I know the value of ID from t1user. How can I do this in a single select statement. (I am using mysql).
**t1user**
+----+
| ID |
+----+
**t2userEvent**
+---------+----------+
| userID | eventID |
+---------+----------+
**t1event**
+----------+--------------+
| eventID | event_name |
+----------+--------------+

Use join:
SELECT t1user.ID, t1event.event_name
FROM t1user
JOIN t2userEvent ON t1user.ID = t2userEvent.userID
JOIN t1event ON t1event.eventID = t2userEvent.eventID
WHERE t1user.ID = :user_id
If you want the users who doesn't have events be listed too, then use LEFT JOIN instead.

You could try this:
SELECT A.event_name FROM t1event A INNER JOIN t2userEvent B
ON A.eventID = B.eventID WHERE b.userID = ?
If I understand it correctly you have the userID as parameter?

Related

Complex MySQL select with 2 foreign keys

I am attempting to do a select between 3 tables. The only problem is that one of the tables is used to resolve foreign keys on 2 different tables. A short description of the tables can be found below. I have removed some columns so that only pertinent ones are shown.
mail_addr
+------------+
| id |
+------------+
| email |
+------------+
msg_rcpt
+------------+
|MsgID |
+------------+
|rid |
+------------+
|content |
+------------+
msgs
+------------+
|MsgID |
+------------+
|sid |
+------------+
|msgTime |
+------------+
|size |
+------------+
I have attempted...
SELECT msg_rcpt.MsgID, msg_rcpt.content, mail_addr.email as rcpt_addr, msgs.msgTime
FROM msg_rcpt
JOIN msgs ON msgs.MsgID = msg_rcpt.MsgID
JOIN mail_addr ON msg_rcpt.rid = mail_addr.id
JOIN mail_addr ON msgs.sid = mail_addr.id
When I do these joins it comes back with Not unique table/alias: 'mail_addr'.
I know this is wrong but I am unsure how or even what I should be looking for in order to resolve this query.
Any feedback would be appreciated.
Viper
You can join a table multiple times, but you HAVE to alias the 2nd and subsequent joins:
JOIN mail_addr ON msg_rcpt.rid = mail_addr.id
JOIN mail_addr AS someaslias ON msgs.sid = somealias.id
^^^^^^^^^^^^^ ^^^^^^^^^
If you wanna join two times on the same table, you'll have to use aliases (well at least one, but two aliases will make things clearer).
SELECT msg_rcpt.MsgID, msg_rcpt.content, m1.email as rcpt_addr, msgs.msgTime
FROM msg_rcpt
JOIN msgs ON msgs.MsgID = msg_rcpt.MsgID
JOIN mail_addr m1 ON msg_rcpt.rid = m1.id
JOIN mail_addr m2 ON msgs.sid = m2.id
I did not understand if this is the result you want to achieve but this should work (based on your example query)
SELECT msg_rcpt.MsgID, msg_rcpt.content, m1.email as rcpt_addr, msgs.msgTime
FROM msg_rcpt JOIN msgs ON msgs.MsgID = msg_rcpt.MsgID
JOIN mail_addr m1 ON msg_rcpt.rid = m1.id
JOIN mail_addr m2 ON msgs.sid = m2.id
If this is not what you're expecting, please edit your question adding an example of the final result!

Return min value from query with inner join

I have two table:
table POI:
NAME | VOTE
Paris | rt_1
Milan | rt_2
Rome | rt_3
... | ...
table rtgitems:
ITEM | TOTALRATE
rt_1 | 22
rt_2 | 3
rt_3 | 3
rt_4 | 5
... | ...
I want the attribute NAME from first table with minimum value in TOTALRATE from second table. Example: Milan, Rome.
I use this query:
SELECT POI.Name FROM POI INNER JOIN rtgitems ON POI.Vote=rtgitems.item WHERE POI.Vote = (SELECT MIN(rtgitems.totalrate) FROM rtgitems)
but don't work, I have empty result.
How must I do?
Thanks.
When ever you are using min or max kind or function in your sql you should use group by clause to get the real output from the table.
SELECT POI.Name FROM POI INNER JOIN rtgitems ON POI.Vote=rtgitems.item where totalrate= (select min(totalrate) from rtgitems)
GROUP BY POI.Name
hope it helps.
SELECT POI.Name, min(totalrate) FROM POI INNER JOIN rtgitems ON POI.Vote=rtgitems.item
GROUP BY POI.Name
try SELECT POI.name FROM POI join rtgitems ON POI.vote=rtgitems.item where totalrate<=(SELECT totalrate from rtgitems order by totalrate desc limit 1)

Join results from two selects - mysql

I have two select queries:
SELECT Name as CategoryName FROM providerscategories where ID in(
Select catId from provider_in_category
where providerUsername = 'a' group by providerUsername
);
and the other one is:
SELECT Username, providerName from serviceproviders where Username in(
SELECT providerUsername from usedproviders where Username='Admin')
Basically what it does is getting the providerName from the usedProviders by Username from usedproviders table, then gets the UserName and the providerName of the provider based on the results.
Then I want to check which category this provider is in and get this category name, and add it to the UserName and the providerName of the category.
Here is a sql fiddle link: SQL Fiddle
the desired result set is:
Username | ProviderName | categoryName
OK. Let's start here... what part of the problem does the following query not solve...
SELECT sp.*
, up.username
, c.name
, pc.catid
FROM usedproviders up
JOIN serviceproviders sp
ON sp.username = up.providerusername
JOIN provider_in_category pc
ON pc.providerusername = up.providerusername
JOIN providerscategories c
ON c.id = pc.catid
WHERE up.username = 'Admin';
+----------+--------------+----------+--------------+-------+
| Username | providerName | username | name | catid |
+----------+--------------+----------+--------------+-------+
| a | providerA | Admin | CategoryName | 1 |
| b | providerB | Admin | CategoryName | 1 |
+----------+--------------+----------+--------------+-------+
This is what you can do
select
sp.Username,
sp.providerName,
Name
from serviceproviders sp
left join
(
select
usedproviders.ProviderUsername ,
providerscategories.Name
from
provider_in_category pic
inner join
usedproviders on usedproviders.ProviderUsername = pic.providerUsername
inner join
providerscategories on providerscategories.ID = pic.catId
where
usedproviders.ProviderUsername = 'a'
AND usedproviders.Username = 'admin'
group by usedproviders.ProviderUsername
)t
on sp.Username = t.ProviderUsername
DEMO
I have seen you are filtering data with admin and a so if you dont want to see the 2nd record NULL then instead LEFTJOIN just make it INNER join the first part.
Also if you do not want filter then remove the where clause and will list all data.
something as DEMO

MySQL JOIN+COUNT+WHERE+AND

I have table of regions:
table region:
id | title
Region has many adverts:
table advert:
id | region_id | ...
Then advert has many uses (many-many through table adv_use):
table use:
use | slug | ...
----------------
1 | slug_1 | ...
2 | slug_2 | ...
..................
table adv_use:
adv_id | use_id
I want select all regions with count(*) of adverts, which have uses with slug_1 AND slug_2. If advert has no use with slug_1 or with slug_2 (or both), it's must not be counted.
What i have now:
SELECT COUNT(DISTINCT advert.id) as count
FROM region
JOIN advert ON region.id = advert.region_id
JOIN adv_use ON advert.id = adv_use.adv_id
JOIN use ON adv_use.use_id = use.id
WHERE use.slug IN ('slug_1', 'slug_2')
GROUP BY region.id
HAVING COUNT(DISTINCT adv_use.use_id) = 2
But it's working not as i want.
SQLFiddle: http://sqlfiddle.com/#!2/5b4d4/1
Thanks for help and sorry for bad english.
You need to use a subquery to select the adv_id that use both slugs:
SELECT COUNT(DISTINCT advert.id) as count, region.id as reg_id
FROM region
JOIN advert ON region.id = advert.region_id
JOIN (SELECT adv_use.adv_id
FROM adv_use
JOIN tbl_use ON adv_use.use_id = tbl_use.id
WHERE tbl_use.slug IN ('slug1', 'slug2')
GROUP BY adv_use.adv_id
HAVING COUNT(DISTINCT adv_use.use_id) = 2) adv_use
ON advert.id = adv_use.adv_id
GROUP BY region.id
;
SQLFIDDLE

Complex Query with related tables - Optimal Solution

The Schema:
I have 3 Tables:
User
Feature
User_has_Feature:
initially all users has no features
Example data:
User:
| id | name |
| 1 | Rex |
| 2 | Job |
Feature:
| id | name |
| 1 | Eat |
| 2 | Walk |
User_has_Feature:
| id | user_id | feature_id | have_feature |
| 1 | 1 | 1 | true |
| 2 | 1 | 1 | true |
| 3 | 2 | 2 | true |
| 4 | 2 | 2 | false |
The questions are:
¿How to get only the records that have all features? (explicitly)
Example:
| user_name | feature_name | feature_status |
| Rex | Eat | true |
| Rex | Walk | true |
How to get records that do not have all the features? (again explicitly)
Example:
| user_name | feature_name | feature_status |
| Job | Eat | true |
| Job | Walk | false |
Some conditions have to be attended
I need the Users list with all features (true or false) in both queries like examples
User have 650k records (for now)
Feature have 45 records (for now)
Is one time query.
The idea is to export the result to a CSV file
Early Solution
thanks to the answers of (#RolandoMySQLDBA, #Tom Ingram, #DRapp) I found a solution:
SELECT u.name, f.name, IF(uhf.status=1,'YES','NO') as status
FROM user u
JOIN user_has_feature uhf ON u.id = uhf.user_id
JOIN feature f ON f.id = uhf.feature_id
JOIN
(
SELECT u.id as id
FROM user u
JOIN user_has_feature uhf ON uhf.user_id = u.id
WHERE uhf.status = 1
GROUP BY u.id
HAVING count(u.id) <= (SELECT COUNT(1) FROM feature)
) as `condition` ON `condition`.id = u.id
ORDER BY u.name, f.id, uhf.status
For get records that do not have all the features and for get all record that have all features change:
WHERE uhf.status = 1 by WHERE uhf.status = 2
HAVING count(u.id) <= (SELECT COUNT(1) FROM feature) by HAVING count(u.id) = (SELECT COUNT(1) FROM feature)
but I want to know if this is an optimal solution?
SELECT
UNF.*,
IF(
(LENGTH(UNF.FeatureList) - LENGTH(REPLACE(UNF.FeatureList,',','')))
= (FC.FeatureCount - 1),'Has All Features','Does Not Have All Features'
) HasAllFeatures
FROM
(SELECT
U.name user_name
GROUP_CONCAT(F.name) Features
FROM
(SELECT user_id,feature_id FROM User_has_Feature
WHERE feature_status = true) UHF
INNER JOIN User U ON UHF.user_id = U.id
INNER JOIN Feature F ON UHF.feature_id = F.id
GROUP BY
U.name
) UNF,
(SELECT COUNT(1) FeatureCount FROM Feature) FC
;
The UNF subquery returns with all users listed in User_has_Feature and a comma-separated list of the features. The column HasAllFeatures is determined by the number of columns in UNF.FeatureList. In your case, there are two features. If the number of commas in UNF.FeatureList is FeatureCount - 1, then the user has all features. Otherwise, user does not have all features.
Here is a better version that shows all users and whether or not they have all, some or no features
SELECT
U.name user_name,
IFNULL(UsersAndFeatures.HasAllFeatures,
'Does Not Have Any Features')
WhatFeaturesDoesThisUserHave
FROM
User U LEFT JOIN
(
SELECT
UHF.user_id id,
IF(
(LENGTH(UHF.FeatureList) - LENGTH(REPLACE(UHF.FeatureList,',','')))
= (FC.FeatureCount - 1),
'Has All Features',
'Does Not Have All Features'
) HasAllFeatures
FROM
(
SELECT user_id,GROUP_CONCAT(Feature.name) FeatureList
FROM User_has_Feature INNER JOIN Feature
ON User_has_Feature.feature_id = Feature.id
GROUP BY user_id
) UHF,
(SELECT COUNT(1) FeatureCount FROM Feature) FC
) UsersAndFeatures
USING (id);
select
u.id,
u.name as User_Name,
f.name as Feature_Name,
uhf.feature_Status
from
( select uhf.user_id,
sum( if( uhf.feature_status, 1, 0 ) ) as UserFeatureCount
from user_has_feature uhf
group by uhf.user_id ) AllUsersWithCounts
join
( select count(*) as AllFeaturesCount
from Feature ) AllFeatures
on AllUsersWithCounts.UserFeatureCount = AllFeatures.AllFeaturesCount
join user u
on AllUsersWithCounts.user_id = u.ID
join user_has_feature uhf
on AllUsersWithCounts.User_id = uhf.user_id
join feature f
on uhf.feature_id = f.id
The above query should get all people that explicitly have ALL features. In order to get those that do NOT have all features, just change the one join from = to <
on AllUsersWithCounts.UserFeatureCount < AllFeatures.AllFeaturesCount
Here's my bash at it
create a view of the general information
CREATE VIEW v_users_have_features AS
SELECT usr.id, usr.name, feature.name, has_feature.status
FROM usr
JOIN has_feature ON usr.id = has_feature.user_id
JOIN feature ON has_feature.feature_id = feature.id;
use the view for other queries
SELECT v_users_have_features.id, v_users_have_features.u_name, v_users_have_features.f_name
FROM v_users_have_features
GROUP BY v_users_have_features.id
HAVING COUNT( v_users_have_features.id ) = (SELECT COUNT( feature.id )
FROM feature
WHERE feature.name = v_users_have_features.f_name )
p.s. you may need to adapt (particularly the latter) to your exact requirements you could also omit creating the view and nest it in the FROM clause like in another answer it just seemed handier to create the view
Count the number of features. Write a query over users that uses a correlated subquery to find all the features a user has and count them. Make the restriction criterion in the top query the equality of that count and the global number of features.
Can MySQL do correlated subqueries? If not, you might need to use a better database.