Select column if not NULL then select other - mysql

I'm trying to figure how to return a row with the associated value when using the JOIN syntax, firstly it should check the links table to see if the row is NULL, if it's not NULL use that for the JOIN e.g. ON links.role_id = roles.role_id if the row is NULl. If it's NULL then use the people table.
My syntax:
SELECT roles.role_name
FROM
(
people_links links
LEFT OUTER JOIN people people ON links.person_id = people.person_id
LEFT OUTER JOIN roles roles ON links.role_id = roles.role_id OR links.role_id = people.role_id
)
Basically when joining and using the ON syntax with the role_id, it will return the associated value from the roles table that is used from the role_id so it should rely on the links table first, check if links.role_id is NULL then return people.role_id else links.role_id for the JOIN to get the role_name.
When I run this query, it uses the role_id from the people table first, when it should look in the links table first.
I tried using COALESCE but that will only return the role_id when it should return role_name, so for example (pseudo code):
SELECT IF(links.role_id IS NOT NULL) links.role_id ELSE people.role_id ENDIF as join_value
FROM
(
people_links links
LEFT OUTER JOIN people people ON links.person_id = people.person_id
LEFT OUTER JOIN roles roles ON roles.role_id = join_value
)

You could write IF(links.role_id IS NOT NULL, links.role_id, people.role_id) — that's the closest thing to your pseudocode — but it's more idiomatic to write COALESCE(links.role_id, people.role_id).
For information on IF, see §11.4 "Control Flow Functions" in the MySQL Reference Manual; for information on COALESCE, see §11.3.2 "Comparison Functions and Operators".

I think for code clarity that I would have two joins onto role. One from the link to role and one from the people to role. I.e.
SELECT
COALESCE(links_roles.role_name, people_roles.role_name)
FROM
(
people_links links
LEFT OUTER JOIN people people ON links.person_id = people.person_id
LEFT OUTER JOIN roles link_roles ON links.role_id = link_roles.role_id
LEFT OUTER JOIN roles people_roles ON people.role_id = people_roles.role_id
)

Related

The multi-part identifier could not be bound in a multiple condition where statement

I'm trying to add an additional WHERE condition with AND to my mySQL query but keep getting the error The multi-part identifier userR.externalSource could not be bound
I have the alias setup in my left join from my other table but still I get this error. Could someone explain me why this is happening. I found article but this does not seem to be the case. I think it's rather a formatting issue of my query.
This query just counts 2 different groups of users but they can't have an empty (NULL) field for the externalSource field from the dbo.AAA_users table.
SELECT SUM(A.ROLECOUNT) AS 'Advanced Users' ,
(SELECT SUM(A.ROLECOUNT)
FROM (
SELECT role.logicalName AS Role_name ,
COUNT(DISTINCT users.userId) AS RoleCount
FROM dbo.AAA_UserRoleResource AS users
LEFT JOIN dbo.AAA_Role AS role ON role.roleId = users.roleId
LEFT JOIN dbo.AAA_User AS userR ON userR.userId = users.userId
GROUP BY role.logicalName
) A
WHERE A.Role_name IN ('ROLE_VIEWER', 'ROLE_USER')
AND userR.externalSource is not NULL <-- issue here
) AS 'Basic user'
FROM(
SELECT role.logicalName AS Role_name ,
COUNT(DISTINCT users.userId) AS RoleCount
FROM dbo.AAA_UserRoleResource AS users
LEFT JOIN dbo.AAA_Role AS role ON role.roleId = users.roleId
LEFT JOIN dbo.AAA_User AS userR ON userR.userId = users.userId
GROUP BY role.logicalName
) A
WHERE A.Role_name IN ('ROLE_ADMIN');
Any help appreciated.
So i figured out why it was not working.
I was adding the additional WHERE condition outside of the parenthesis and because of this it did not find the identifier which is within the parenthesis.

SQL query NOT EXISTS intesad of NOT IN

I'm having issues finding an appropriate NOT EXISTS-Query to replace my NOT IN one. I have to replace it because of the nullable-columns problem. However I'm rather new to SQL, so I can't get the right relations.
What I'm trying to do is to exclude all users that are already assigned to a practice, which do not have the "informal" flag set (in other words: Unassigned users with the informal flag). My current query is
SELECT USERS.UserID, USERS.Name
FROM USERS
WHERE USERS.Informal = true AND USERS.UserID NOT IN (
SELECT USERS.UserID
FROM PRACTICES
LEFT JOIN ATTENDS ON PRACTICES.PracticeID = ATTENDS.PracticeID
LEFT JOIN USERS ON ATTENDS.UserID = USERS.UserID
WHERE PRACTICES.PracticeID = 7
)
this is the relevant table structure for the query
I'd be very grateful if someoune could hint me in the right direction.
There is no ned to repeat the USERS table in the subquery -- for either NOT IN or NOT EXISTS. I strongly recommend NOT EXISTS (because it handles NULL values more intuitively):
SELECT u.UserID, u.Name
FROM USERS u
WHERE u.Informal = true AND
NOT EXISTS (SELECT USERS.UserID
FROM PRACTICES p
ATTENDS a
ON p.PracticeID = a.PracticeID
WHERE a.UserID = u.UserID AND p.PracticeID = 7
)

SQL AND without requirement

I have a table displaying on my website with a list of projects. The SQL statement below pulls in each project and converts the ###_id columns to the ###_name in another table. So far so good.
The problem I have is that this is requiring all fields in a row in the projects table to be filled out. If, for example, the project row has no value for 'proj_industry_id' then the project won't display here at all.
I've tried removing the 'AND' for each match-up in the WHERE statement and separating them with commas, but it errors out.
I've also checked SQL docs and can't seem to find my way to an answer over there.
Any ideas on how I can get my statement to still match up the id with the name when I have one, but still show the record when I don't?
Thanks!
$sql = "SELECT
projects.*,
engagement_types.eng_type_name AS eng_type,
users.user_full_name AS username,
industries.industry_name AS industry_name,
categories.category_name AS category_name,
geographies.geo_name AS geo_name,
status.status_name AS status_name
FROM
projects,
engagement_types,
users,
industries,
categories,
geographies,
status
WHERE
projects.proj_eng_type_id = engagement_types.id
AND projects.proj_lead_id = users.id
AND projects.proj_industry_id = industries.id
AND projects.proj_category_id = categories.id
AND projects.proj_geo_id = geographies.id
AND projects.proj_status_id = status.id
AND projects.proj_geo_id = '$selected_geo_id'";
*****EDIT******
Here is the final correct code from the solution below using multiple left joins!
SELECT
projects.*,
engagement_types.eng_type_name AS eng_type,
users.user_full_name AS username,
industries.industry_name AS industry_name,
categories.category_name AS category_name,
geographies.geo_name AS geo_name,
status.status_name AS status_name
FROM
projects
LEFT JOIN engagement_types ON projects.proj_eng_type_id = engagement_types.id
LEFT JOIN users ON projects.proj_lead_id = users.id
LEFT JOIN industries ON projects.proj_industry_id = industries.id
LEFT JOIN categories ON projects.proj_category_id = categories.id
LEFT JOIN geographies ON projects.proj_geo_id = geographies.id
LEFT JOIN status ON projects.proj_status_id = status.id
GROUP BY
proj_start_date
sounds like your have to look at "LEFT JOIN" https://www.w3schools.com/sql/sql_join_left.asp
Otherwise you miss the left part of the green circle.

IF/CASE in an outer join

We have two tables that I need to join on where a column (which, at this point, I can't change how/what values are used in that column) aren't using the same value. So depending on the value of the column in the first table, I need to join/select a particular value in the second table. Here is an example (which obviously doesn't work) of what I am trying to do:
SELECT Alert.*, Comments.Comment FROM
Alert
LEFT OUTER JOIN Comments ON Comments.ObjectId = Alert.ObjectId AND
CASE
WHEN Alert.ObjectType = 'U' THEN Comments.ObjectType = 'USER'
WHEN Alert.ObjectType = 'E' THEN Comments.ObjectType = 'EVENT'
END CASE
So I want everything from the Alert table and, if there are corresponding records in the Comments table, I want those, too. But only for the appropriate/matching ObjectType.
I've tried this using both CASE and IF but I can't seem to get it to work. Is something like this possible?
The CASE statement is made to return a value, not to perform an operation.
Also change the last END CASE to just END.
Use that returned value to compare with in the join condition.
Try:
SELECT Alert.*, Comments.Comment FROM
Alert
LEFT OUTER JOIN Comments ON Comments.ObjectId = Alert.ObjectId AND
Comments.ObjectType =
CASE Alert.ObjectType
WHEN 'U' THEN 'USER'
WHEN 'E' THEN 'EVENT'
END
I recommend you handle this with a UNION. In one union join the user comments in another the event comments:
SELECT Alert.*, userComments.Comment
FROM alert
LEFT OUTER JOIN comments usercomments ON userComments.ObjectId = Alert.ObjectId AND usercomments.objecttype='USER'
WHERE alert.objecttype = 'U'
UNION
SELECT Alert.*, eventComments.Comment
FROM alert
LEFT OUTER JOIN comments eventcomments ON eventComments.ObjectId = Alert.ObjectId AND eventcomments.objecttype='EVENT'
WHERE alert.objecttype = 'E'
You don't have to alias them they way I did - it just helps readability.
Why not just:
SELECT Alert.*, Comments.Comment FROM
Alert
LEFT OUTER JOIN Comments ON Comments.ObjectId = Alert.ObjectId AND
Alert.ObjectType = LEFT(Comments.ObjectType, 1);
It seems a lot simpler...
EDIT
Based on your comment, it seems that not all 'matching' values start with the same letter. In this case, I would recommend designing an intermediate table with columns AlertType char(1) and CommentType varchar(50). Insert each combination of TypeId, like U, User; E, Event; etc. The you can modify your SQL to read
SELECT Alert.*, Comments.Comment FROM
Alert
LEFT OUTER JOIN Intermediate i on Alert.ObjectType = i.AlertType
LEFT OUTER JOIN Comments ON Comments.ObjectId = Alert.ObjectId AND
Comments.ObjectType = i.CommentType;

Mysql Query Left Join Condition Problem

I have a litte problem with a mysql query.
I use 5 tables:
user_has_data (uid, dataid); users (uid, uname); user_in_group(uid, groupid, data); groups(groupid, data, packageid); packages(packageid, name)
all ids are PK. I want to build a sql query that finds a user, which belongs to a specified dataid, by its uname and checks if the user is in a group (relation in table user_in_group) belonging to a specified package (a group is assigned to one package). if so data from users, package and group should be fetched, otherwise only the user data should be fetched. Therefore I use left joins, so I can also get the users with no group:
SELECT `uac`.`uid`, `u`.`uid`, `uig`.`groupid`, `ag`.`packageid`
FROM `user_has_data` AS `uac`
INNER JOIN `users` AS `u` ON u.uid = uac.uid
LEFT JOIN `user_in_group` AS `uig` ON uig.uid = uac.uid
LEFT JOIN `groups` AS `ag` ON (ag.groupid = uig.groupid) AND (ag.packageid = 2)
WHERE (uac.dataid = '3') AND (u.uname LIKE 'test%')
GROUP BY `u`.`uid`
Unfortunately I get wrong results: I get groups that have a different packageid than stated in the join, if the user has another group assigned to him with a different packageid.
probably this is because the first left join has no restrictions to packageid and the second is a left join and so it has no restrictions on the result (packageid is NULL for all results, but should have values). If I change the second left join to a ordinary join, the group problem would be fixed but the query cant find users without group any more.
Any ideas how to fix this or even possible?
thanks in advance!
Are you saying that you are actually seeing the value ag.packageid = 2 in your query results?
If not, I think you might try something like:
SELECT `uac`.`uid`, `u`.`uid`, `g`.`groupid`, `g`.`packageid`
FROM `user_has_data` AS `uac`
INNER JOIN `users` AS `u` ON u.uid = uac.uid
LEFT JOIN (`user_in_group` AS `uig`
INNER JOIN `groups` AS `ag` ON (ag.groupid = uig.groupid) AND (ag.packageid = 2) )
AS `g` ON uac.uid = g.uid
WHERE (uac.dataid = '3') AND (u.uname LIKE 'test%')
GROUP BY `u`.`uid`
Because you are limiting your search to a specific group packageid of '2', why not just make both of your LEFT JOIN INNER JOINS and then throw in ag.packageid = 2 in your WHERE clause?
SELECT `uac`.`uid`, `u`.`uid`, `uig`.`groupid`, `ag`.`packageid`
FROM `user_has_data` AS `uac`
INNER JOIN `users` AS `u` ON u.uid = uac.uid
LEFT OUTER JOIN `user_in_group` AS `uig` ON uig.uid = uac.uid
LEFT OUTER JOIN `groups` AS `ag` ON ag.groupid = uig.groupid
WHERE (uac.dataid = '3') AND (u.uname LIKE 'test%')
AND (ag.packageid = 2 OR uig.uid IS NULL)
GROUP BY `u`.`uid`
I know LEFT JOIN and LEFT OUTER JOIN mean the same thing, but I like to be explicit. With the condition in your join, I bet you were getting groups with different packages, but weren't getting the packages?