retrieving data from three tables using MySQL - mysql

i am having three tables namely
1)cd_register
|----------------------------------------------------|
| username | name | age | sex | dob |
|----------------------------------------------------|
2)cd_social
|-------------------------------------|
| username | religion | caste |
|-------------------------------------|
and
3)cd_professional
|----------------------------------------|
| username | occupation | education |
|----------------------------------------|
now i'm using this query in mysql but its giving error
SELECT cd_register.name, cd_register.age, cd_register.dob, cd_social.religion, cd_social.caste, cd_professional.profession
FROM cd_register, cd_social,cd_professional
WHERE
cd_register.sex = 'Male',
cd_social.religion = 'Hindu',
cd_social.caste = 'Brahmin',
cd_professional.occupation = 'Doctor',
cd_register.username = cd_social.username AND
cd_register.username = cd_professional.username
now i want to mention that username is the primary key of all the tables. Also
username of cd_register is foreign key in cd_social. Also
username of cd_register is foreign key in cd_professional.

You can use join
SELECT t1.name, t1.age, t1.dob, t2.religion, t2.caste, t3.profession
FROM cd_register as t1
LEFT JOIN cd_social as t2
ON t1.username = t2.username and t2.caste = 'Brahmin' and t2.religion ='Hindu'
LEFT JOIN cd_professional as t3
ON t1.username = t3.username and t3.occupation = 'Doctor'
WHERE t1.sex = 'Male'

You have commas in your WHERE clause, which will definitely cause an error (you probably want ANDs there) Try this:
SELECT cdr.name, cdr.age, cdr.dob, cds.religion, cds.caste, cdp.profession
FROM cd_register cdr
JOIN cd_social cds
ON cdr.username = cds.username
JOIN cd_professional cdp
ON cdr.username = cdp.username
WHERE cdr.sex = 'Male'
AND cds.religion = 'Hindu'
AND cds.caste = 'Brahmin'
AND cdp.occupation = 'Doctor'

It might help to tell us what the error is.
Also, the last two lines are identical.
You probably need to join the cd_professional table, too.

Related

Join 3 table and prevent Duplicate Details

Hello I got this 3 table and I want to make a single joined table with no duplicate details.
User Table
userLRN userLevel
user_information Table
userInfoID userLRN userFirstname userLastname userMiddlename userSex userContactNumber userYearGraduated
workinfo Table
workinfoID userLRN workOccupation
Design:
User Table is to one user_information
user_information is to many
workinfo
I tried this SQL Query:
SELECT userLRN, userFirstname, userLastname, userMiddlename, userSex,
userContactNumber, userYearGraduated, workOccupation
From user_information
INNER JOIN users on user_information.userLRN = users.userLRN
INNER JOIN workinfo on workinfo.userLRN = user_information.userLRN
WHERE users.userLevel = users
and I got multiple names and id.
I want to combine the same id and name with different work.
Please Help me with this.
I want this output:
Image Output
based on your SQLFiddle and comments below, try:
SELECT user_information.userLRN, userFirstname, userLastname,
group_concat(workOccupation) as workStuff
From user_information
INNER JOIN users on user_information.userLRN = users.userLRN
INNER JOIN workinfo on workinfo.userLRN = user_information.userLRN
WHERE users.userLevel = 'user'
group by user_information.userLRN, userFirstname, userLastname;
+---------+---------------+--------------+-------------------+
| userLRN | userFirstname | userLastname | workStuff |
+---------+---------------+--------------+-------------------+
| 1 | Al | Go | Work1,Work2,Work5 |
| 2 | Megan | Fox | Work4 |
+---------+---------------+--------------+-------------------+
mysql manual page on group_concat
A Percona article entitled The power of MySQL’s GROUP_CONCAT

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

How can this be done in a single select statement?

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?

How would I execute this complex conditional multi-table MySQL join (queries provided)?

Ok I have a few tables tables. I am only showing relevant fields:
items:
----------------------------------------------------------------
name | owner_id | location_id | cab_id | description |
----------------------------------------------------------------
itm_A | 11 | 23 | 100 | Blah |
----------------------------------------------------------------
.
.
.
users:
-------------------------
id | name |
-------------------------
11 | John |
-------------------------
.
.
.
locations
-------------------------
id | name |
-------------------------
23 | Seattle |
-------------------------
.
.
.
cabs
id | location_id | name
-----------------------------------
100 | 23 | Cool |
-----------------------------------
101 | 24 | Cool |
-----------------------------------
102 | 24 |thecab |
-----------------------------------
I am trying to SELECT all items (and their owner info) that are from Seattle OR Denver, but if they are in Seattle they can only be in the cab NAMED Cool and if they are in Denver they can only be in the cab named 'thecab' (not Denver AND cool).
This query doesn't work but I hope it explains what I am trying to accomplish:
SELECT DISTINCT
`item`.`name`,
`item`.`owner_id`,
`item`.`description`,
`user`.`name`,
IF(`loc`.`name` = 'Seattle' AND `cab`.`name` = 'Cool',1,0) AS `cab_test_1`,
IF(`loc`.`name` = 'Denver' AND `cab`.`name` = 'thecab',1,0) AS `cab_test_2`,
FROM `items` AS `item`
LEFT JOIN `users` AS `user` ON `item`.`owner_id` = `user`.`id`
LEFT JOIN `locations` AS `loc` ON `item`.`location_id` = `loc`.`location_id`
LEFT JOIN `cabs` AS `cab` ON `item`.`cab_id` = `cabs`.`id`
WHERE (`loc`.`name` IN ("Seattle","Denver")) AND `cab_test_1` = 1 AND `cab_test_2` = 1
I'd rather get rid of the IFs is possible. It seems inefficent, looks clunky, and is not scalable if I have a lot of location\name pairs
Try this:
SELECT DISTINCT
item.name,
item.owner_id,
item.description,
user.name
FROM items AS item
LEFT JOIN users AS user ON item.owner_id = user.id
LEFT JOIN locations AS loc ON item.location_id = loc.id
LEFT JOIN cabs AS cab ON item.cab_id = cabs.id
WHERE ((loc.name = 'Seattle' AND cab.name = 'Cool')
OR (loc.name = 'Denver' AND cab.name = 'thecab'))
My first thought is to store the pairs of locations and cab names in a separate table. Well not quite a table, but a derived table generated by a subquery.
You still have the problem of pivoting the test results into separate columns. The code can be simplified by making use of mysql boolean expressions, which get rid of the need for a case or if.
So, the approach is to use the same joins you have (although left join is not needed because the comparison on cab.name turns them in to inner joins). Then add a table of the pairs you are looking for, along with the "test name" for the pair. The final step is an explicit group by and a check whether conditions are met for each test:
SELECT i.`name`, i.`owner_id`, i.`description`, u.`name`,
max(pairs.test_name = 'test_1') as cab_test_1,
max(pairs.test_name = 'test_2') as cab_test_2
FROM `items` i LEFT JOIN
`users` u
ON i.`owner_id` = u.`id` LEFT JOIN
`locations` l`
ON i.`location_id` = l.`location_id` left join
`cabs` c
ON i.`cab_id` = c.`id` join
(select 'test_1' as testname, 'Seattle' as loc, 'cool' as cabname union all
select 'test_2', 'Denver', 'thecab'
) pairs
on l.name = pairs.name and
l.cabname = c.name
group by i.`name`, i.`owner_id`, i.`description`, u.`name`;
To add in additional pairs, add them into the pairs table along, and add an appropriate line in the select for the test flag.

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.