Gynteniuxas
Gynteniuxas

Reputation: 7093

How to select column from 2nd level depth subquery?

I have found a few answers how to select a column from subquery, but it doesn't seem to be working if there is a subquery inside subquery.

My table relationships are as follow (simplified, with example):

Plans
| id | role_id
| --------------
| 4  | 2

Roles
| id | name
----------------
| 2  | Operator

Assignments
| role_name | user_id
| ---------------------
| Operator  | 12

Table Plans have foreign key role_id to table Roles column id. And Table Roles have foreign key id to table Assignments column role_name.

I'm writing a query that would select Plans but instead of role_id I would show user_id from table Assignments.

I'm pretty close with my current work but I'm stuck at final step:

SELECT assigned_user, plan.id, plan.role_id
FROM `Plans` AS plan
LEFT JOIN (
    SELECT roleTable.id, roleTable.name 
    FROM `Roles` AS roleTable
    INNER JOIN (
        SELECT base_assign.user_id AS assigned_user, base_assign.role_name 
        FROM `Assignments` AS base_assign) AS roleAssign
    ON roleTable.name = roleAssign.role_name
) AS role 
ON (plan.assignee_role = role.id)

Column assigned_user on SELECT part (line 1) is not found but I tried by adding alias inside of subquery SELECT. How I can select base_assign.user_id? Note that I (most likely) cannot modify database structure as it was given like this in the first place.

Upvotes: 0

Views: 1521

Answers (2)

Gurwinder Singh
Gurwinder Singh

Reputation: 39477

You are creating subqueries too excessively and you don't even need subqueries for this. Subqueries can be materialized by optimzer in MySQL which can cause performance issues.

See https://dev.mysql.com/doc/refman/5.6/en/subquery-optimization.html

Try this using simple left joins:

select a.user_id as assigned_user,
    p.id,
    p.role_id
from `Plans` p
left join `Roles` r on p.assignee_role = r.id
left join `Assignments` a on r.name = a.role_name;

You query wasn't working because you were not selecting the column while joining.

Your somewhat "fixed" code:

select assigned_user,
    plan.id,
    plan.role_id
from `Plans` as plan
left join (
    select roleAssign.assigned_user roleTable.id,
        roleTable.name
    from `Roles` as roleTable
    inner join (
        select base_assign.user_id as assigned_user,
            base_assign.role_name
        from `Assignments` as base_assign
        ) as roleAssign on roleTable.name = roleAssign.role_name
    ) as role on (plan.assignee_role = role.id)

Upvotes: 1

Gordon Linoff
Gordon Linoff

Reputation: 1269873

You don't need subqueries for this. It is a little confusing what you are trying to do. The following will return all plans, even those with no roles and no assignments:

SELECT b.user_id as assigned_user, p.id, p.role_id
FROM Plans p LEFT JOIN
     Roles r
     ON p.role_id = r.id LEFT JOIN
     Assignments a
     ON a.name = r.name;

Subqueries are bad for two reasons. In MySQL, the are materialized. That means extra overhead for reading and writing the tables. Plus, you lose any indexes on them.

In your case, the subqueries just add layer upon layer of naming, and the names really do not help make your query more understandable.

Upvotes: 1

Related Questions