Retrieve data from third table - sql

This question is irritating to ask, as i haven't got the hang of using middle tables in sql yet. But i'm in a time rush so i've chosen to ask anyway.
I wish to combine these sql codes so i can retrieve the data from "teacher" aswell as "team" and "level", in the same repeater.
I'm catching a name from the "teacher" table through the FK_teacher from the middletable "teacher_team" which is related to my team table through FK_team
Would it by any chance be possible to combine these so i could extract it all through one repeater?
// Team <-> level relation
SELECT
team.team_id as team_id,
level.level as level,
FROM team
INNER JOIN level ON level.level_id = team.team_FK_level
WHERE team.team_FK_type = #id
// Team <-> Team_Teacher <-> Teacher relation
SELECT teacher.teacher_name as name
FROM teacher WHERE teacher.teacher_id
IN (
SELECT teacher_team.FK_teacher
FROM teacher_team
INNER JOIN team ON team.team_id = teacher_team.FK_team
WHERE team.team_FK_type = #id
)
* EDIT *
Got this code to work based on the answer of Ravi Singh. I've encountered another problem.
My repeater will output the teams row twice, if there are two teachers related to it in the teacher_teams (of course). Is there any way i could merge these without making a repeater inside a repeater?
SELECT
team.team_id as team_id,
level.level as level,
teacher_team.FK_teacher,
teacher.teacher_name as teacher
FROM team
INNER JOIN level ON level.level_id = team.team_FK_level
LEFT JOIN teacher_team on teacher_team.FK_hold = team.team_id
LEFT JOIN teacheron teacher.teacher_id = teacher_team.FK_teacher
WHERE team.team_FK_type = #id

Try this :
SELECT
teacher.teacher_name as name
,team.team_id as team_id
,level.level as level
FROM teacher
inner join teacher_team on teacher.teacher_id =teacher_team.FK_teacher
INNER JOIN team ON team.team_id = teacher_team.FK_team
INNER JOIN level ON level.level_id = team.team_FK_level
WHERE team.team_FK_type = #id
Update :
This should help with your updated question :
with demo_cte as(
SELECT
teacher.teacher_name as name
,team.team_id as team_id
,level.level as level
FROM teacher
inner join teacher_team on teacher.teacher_id =teacher_team.FK_teacher
INNER JOIN team ON team.team_id = teacher_team.FK_team
INNER JOIN level ON level.level_id = team.team_FK_level
WHERE team.team_FK_type = #id
)
select distinct t1.team_id,
t1.level,
STUFF(
(SELECT ', ' + t2.name
FROM demo_cte t2
where t1.team_id = t2.team_id
and t1.level = t2.level
FOR XML PATH (''))
, 1, 1, '') AS name
from demo_cte t1;

Related

PostgreSQL query optimize

Here is my query
SELECT
DISTINCT(org.id),
org.name,
org.partner_id,
pos.partner_id,
pos.id,
org.partner_offer_section_id,
pos.title,
pos.offer_value,
pos.offer_currency,
(SELECT user_info.email FROM user_info WHERE user_info.org_id=org.id ORDER BY created ASC LIMIT 1) as user_email,
(SELECT CONCAT(user_info.first_name,' ',user_info.last_name) FROM user_info WHERE user_info.org_id=org.id ORDER BY created ASC LIMIT 1) as name
FROM org
INNER JOIN partner_offer_section pos ON org.partner_offer_section_id = pos.id
WHERE org.partner_offer_section_id != 0 AND org.partner_id != 0
Here is the same subquery that is executing the twice the same query. I was trying to left join this query but the problem is when I left join I got a null value. I have to get one user name or user email insted of multiple users aginst org.
SELECT org.name,
org.partner_id,
org.partner_offer_section_id,
org.offer_applied_date,
partner_offer_section.title,
partner_offer_section.offer_value,
partner_offer_section.offer_currency,
user_info.email
FROM org
left join (SELECT user_info.id, user_info.email,user_info.created, user_info.org_id FROM user_info WHERE role='Org Admin' LIMIT 1) user_info on org.id = user_info.org_id
left join partner_offer_section on org.partner_offer_section_id = partner_offer_section.id
where org.partner_id = 1
Now I wanna optime this query instead of multiple same subqueries.
You should join the table directly instead of doing a subquery. Bellow is the example, making a JOIN with the first table and the LEFT only with the last one. Also, DISTINCT applies to all columns, it's not a function, as user #a_horse_with_no_name pointed out
SELECT DISTINCT
org.name,
org.partner_id,
org.partner_offer_section_id,
org.offer_applied_date,
partner_offer_section.title,
partner_offer_section.offer_value,
partner_offer_section.offer_currency,
user_info.email
FROM org
join partner_offer_section on org.partner_offer_section_id = partner_offer_section.id
left join user_info on org.id = user_info.org_id
and user_info.role='Org Admin'
where org.partner_id = 1

Postgres: Optimize Query

I have a situation where I need to show a list of subjects to which a learning material or a test has been published to.
The query below works but it takes about ~8s. Is there a better way I can optimize this query?
Thank you.
SELECT sj.*, sj.id AS id FROM subjects sj
WHERE (
(SELECT COUNT(lmc.id) FROM learning_materials_codes lmc
INNER JOIN students s ON lmc.student_id = s.id
INNER JOIN learning_materials lm ON lmc.learning_material_id = lm.id
WHERE lmc.student_id = 1 AND sj.id = ANY(lm.subject_ids)) > 0
OR
(SELECT COUNT(tc.id) FROM test_codes tc
INNER JOIN students s ON tc.student_id = s.id
INNER JOIN tests t ON tc.test_id = t.id
WHERE tc.student_id = 1 AND t.subject_id = sj.id) > 0
)
AND sj.school_id = 1
Table structure below
subjects
~~~~~~~~~~~
id
name
school_id
...
students
~~~~~~~~~~
id
f_name
l_name
school_id
...
tests
~~~~~~~~~
id
title
subject_id
...
test_codes
~~~~~~~~~~
test_id
student_id
code
...
learning_materials
~~~~~~~~~~~~~~~~~
id
title
subject_ids []
...
learning_material_codes
~~~~~~~~~~~~~~~~~~~~~~~~
learning_material_id
student_id
code
...
Note:
Each time a learning material or a test is published an access code is generated for students and that data is kept in learning_material_codes or test_codes table
I would rewrite the query to
SELECT sj.*
FROM subjects sj
WHERE EXISTS (SELECT 1
FROM learning_materials_codes lmc
INNER JOIN students s ON lmc.student_id = s.id
INNER JOIN learning_materials lm ON lmc.learning_material_id = lm.id
WHERE lmc.student_id = 1
AND sj.id = ANY(lm.subject_ids)
AND lmc.id IS NOT NULL)
AND sj.school_id = 1
UNION
SELECT sj.*
FROM subjects sj
WHERE EXISTS (SELECT 1
FROM test_codes tc
INNER JOIN students s ON tc.student_id = s.id
INNER JOIN tests t ON tc.test_id = t.id
WHERE tc.student_id = 1
AND t.subject_id = sj.id
AND tc.id IS NOT NULL)
AND sj.school_id = 1;
That way, PostgreSQL can use a semi-join and may be faster. If you don't mind duplicate result rows, use UNION ALL instead of UNION for better performance.
More performance may be gained with appropriate indexes, but it requires EXPLAIN (ANALYZE, BUFFERS, VERBOSE, SETTINGS) output to assess that.

Why can't I connect OUTER JOIN tables?

I have a table of teachers and each teacher can also be a principal.
I have to introduce a certain teacher and all his principals.
He does it well for me in this code:
WITH teacherTablecte AS
(SELECT teacher.IdTeacher, teacher.FirstName, teacher.LastName,
teacher.PhoneNumber,teacher.ManagerId, 1 as 'EmpLevel', boss.[FirstName]+'
'+boss.LastName as bossName
FROM [dbo].[T_Teachers] teacher JOIN [dbo].[T_Teachers] boss
ON teacher.ManagerId = boss.IdTeacher
WHERE teacher.IdTeacher =2011
UNION ALL
SELECT teacher.IdTeacher, teacher.FirstName, teacher.LastName,
teacher.PhoneNumber,teacher.ManagerId, teacherTablecte.EmpLevel + 1, boss.
[FirstName]+' '+boss.LastName
FROM [dbo].[T_Teachers] teacher
JOIN teacherTablecte
ON teacher.IdTeacher = teacherTablecte.ManagerId JOIN [dbo].[T_Teachers]
boss on
teacher.MANAGERID= boss.IdTeacher
)
SELECT *
FROM teacherTablecte
But he does not introduce me to the Chief Executive
I tried to convert
WITH teacherTablecte AS
(SELECT teacher.IdTeacher, teacher.FirstName, teacher.LastName,
teacher.PhoneNumber,teacher.ManagerId, 1 as 'EmpLevel', boss.[FirstName]+'
'+boss.LastName as
bossName
FROM [dbo].[T_Teachers] teacher JOIN [dbo].[T_Teachers] boss
ON teacher.ManagerId = boss.IdTeacher
WHERE teacher.IdTeacher =2011
UNION ALL
SELECT teacher.IdTeacher, teacher.FirstName, teacher.LastName,
teacher.PhoneNumber,teacher.ManagerId, teacherTablecte.EmpLevel + 1, boss.
[FirstName]+'
'+boss.LastName
FROM [dbo].[T_Teachers] teacher
JOIN teacherTablecte
ON teacher.IdTeacher = teacherTablecte.ManagerId OUTER JOIN [dbo].
[T_Teachers] boss on
teacher.MANAGERID= boss.IdTeacher
)
SELECT *
FROM teacherTablecte
but the JOIN to OUTER JOIN but I get this error: Msg 462, Level 16, State 1, Line 2
Outer join is not allowed in the recursive part of a recursive common table expression 'teacherTablecte'.

Use name of controllers to find table name in Select

I want to extract a ID , User_ID value from one of the Companies and Contract tables, depending on the ContorollerName value.
select P.TitleProject, P.StartDateProject, P.EndDateProject,P.ControllerID,P.RecordID,P.IsAllocated,P.ProjectStatus_ID,
CN.ControllerName,CN.PersianName,
PU.ProjectID,PU.UserID,PU.RoleID,
CASE
WHEN CN.ControllerName = 'Company' THEN
Companies.Id,Companies.[User_Id]
WHEN CN.ControllerName = 'Contract' THEN
Contracts.Id,Contracts.[User_Id]
END
from Projects P
left outer join Controllers CN ON P.ControllerID = CN.Id
left outer join ProjectUsers PU ON P.Id = PU.ProjectID
where P.IsAllocated = 1
For example, if ContorollerName is 'Company' , the select command is as follows :
select P.TitleProject, P.StartDateProject, P.EndDateProject,P.ControllerID,P.RecordID,P.IsAllocated,P.ProjectStatus_ID,
CN.ControllerName,CN.PersianName,
PU.ProjectID,PU.UserID,PU.RoleID,
Companies.Id,Companies.[User_Id]
You are on the right track -- using left join. But you need to add the tables to the from clause with the appropriate logic.
The logic for the join is quite unclear. The query looks something like this:
select . . .,
coalesce(c.id, co.id) as id,
coalesce(c.user_id, co.user_id) as user_id
from Projects P left join
Controllers CN
on P.ControllerID = CN.Id left join
ProjectUsers PU
on P.Id = PU.ProjectID left join
companies c
on c.? = ? and -- no idea what the right join conditions are
c.ControllerName = 'Company' left join
contracts co
on co.? = ? and -- no idea what the right join conditions are
co.ControllerName = 'Contract'
where P.IsAllocated = 1

SQL - Select resources accessible by an user directly or via his group

I'm trying to list all the resources a user has access to. He can have a direct access (user permission) or being in a group which has access to the resource (group permission).
My tables would be:
group: id, name
user: id, name
resource: id, name
rel_resource_user: resource, user
rel_group_resource: group, resource
rel_group_user: group, user
I tried the following query:
SELECT DISTINCT resource.id
FROM resource
LEFT OUTER JOIN rel_group_resource ON resource.id = rel_group_resource.resource
INNER JOIN `group` ON rel_group_resource.`group` = `group`.id
INNER JOIN rel_group_user ON rel_group_user.`group` = `group`.id
LEFT OUTER JOIN rel_resource_user ON rel_resource_user.resource = resource.id
WHERE rel_resource_user.user = 1 OR rel_group_user.user = 1
But I receive only the resources access via the group and not by the user directly. If I cut the query in two query, one for the resources accessed by groups and one by users directly, it works. But I'm not able to get all the resources in one query.
Thank you for you help !!!
You could try to use two where resource.id in like this:
select resource.id
from resource
where resource.id in (select rel_resource_user.resource
from rel_resource_user
where rel_resource_user.user = 1) or
resource.id in (select rel_group_resource.resource
from rel_group_resource
inner join rel_group_user
on rel_group_resource.group = rel_group_user.group
where rel_group_user.user = 1)
If they have the same number of output fields and the same type you can use UNION or UNION ALL, the only difference is UNION gets rid of duplicates.
SELECT
ResourceID
FROM
FIRSTTABLE
UNION ALL
SELECT ResourcedID
FROM
SecondTable
SELECT r.id
FROM resource AS r
WHERE EXISTS
( SELECT *
FROM rel_resource_user AS ru
WHERE ru.resource = r.id
AND ru.user = 1
)
OR EXISTS
( SELECT *
FROM rel_group_user AS gu
JOIN `group` AS g
ON g.id = gu.`group`
JOIN rel_group_resource AS gr
ON gr.`group` = g.id
WHERE gr.resource = r.id
AND gu.user = 1
)
try this query:
select Resource_id from
(
select rel_resource_user.resource as Resource_id,user.id as user_id FROM user inner join rel_resource_user on rel_resource_user.user=user.id
UNION
SELECT rel_group_resource..resource as Resource_id,rel_group_user.user as user_id FROM group inner join rel_group_resource on group.id= rel_group_resource.group INNER JOIN rel_group_user.group= group.id
)