Why does adding this field make my query run slow? - sql

The query below runs in less than a second if I take off one field from the select list -- the last one, att. But having it there causes the query to take over a minute. Any thoughts? Note that the subquery inside the left join runs fast on its own.
select p.person_id, cfm.family_id, p.nick_name, p.last_name, s.lookup_value as group_role, gm.active, g.group_id, g.group_name,
g.active as group_active, tg.category, gc.cluster_name, coalesce(a.attendance,0) as att --this field is the culprit
from smgp_group g
join smgp_group_cluster gc on g.group_cluster_id = gc.group_cluster_id
join smgp_member gm on g.group_id = gm.group_id
join core_person p on gm.person_id = p.person_id
join core_family_member cfm on p.person_id = cfm.person_id
join core_lookup s on gm.role_luid = s.lookup_id
join #target_groups tg on g.group_id = tg.group_id
left join (
select at.person_id, goc.group_id, count(at.attended) as attendance from core_occurrence_attendance at
join core_occurrence o on at.occurrence_id = o.occurrence_id
join smgp_group_occurrence goc on o.occurrence_id = goc.occurrence_id
where goc.group_id in (select group_id from #target_groups)
and o.occurrence_start_time between #start_date and #end_date
group by at.person_id, goc.group_id
) a on p.person_id = a.person_id and g.group_id = a.group_id
where tg.category = 'adults' or (tg.category = 'kids' and s.lookup_value in ('Leader-Teacher','Assistant Leader'))

Try running the following -
SELECT p.persON_id, cfm.family_id, p.nick_name,
p.last_name, s.lookup_value as group_role,
gm.active, g.group_id, g.group_name,
g.active as group_active, tg.cATegory,
gc.cluster_name, a.ATtendance
INTO #tempDataset
FROM smgp_group g
JOIN smgp_group_cluster gc
ON g.group_cluster_id = gc.group_cluster_id
JOIN smgp_member gm
ON g.group_id = gm.group_id
JOIN core_persON p
ON gm.persON_id = p.persON_id
JOIN core_family_member cfm
ON p.persON_id = cfm.persON_id
JOIN core_lookup s
ON gm.role_luid = s.lookup_id
JOIN #target_groups tg
ON g.group_id = tg.group_id
LEFT JOIN (SELECT AT.persON_id, goc.group_id, count(AT.ATtended) as ATtendance FROM core_occurrence_ATtendance AT
JOIN core_occurrence o
ON AT.occurrence_id = o.occurrence_id
JOIN smgp_group_occurrence goc
ON o.occurrence_id = goc.occurrence_id
WHERE goc.group_id IN (SELECT group_id
FROM #target_groups)
AND o.occurrence_start_time BETWEEN #start_dATe AND #end_dATe
GROUP BY AT.persON_id, goc.group_id) a
ON p.persON_id = a.persON_id
AND g.group_id = a.group_id
WHERE tg.cATegory = 'adults' or (tg.cATegory = 'kids' AND s.lookup_value IN ('Leader-Teacher','Assistant Leader'))
SELECT persON_id,
family_id,
nick_name,
last_name,
group_role,
active,
group_id,
group_name,
group_active,
cATegory,
cluster_name,
coalesce(ATtendance,0) AS ATT
FROM #tempDataset
If that runs slowly, you can possibly add an index on the temp table, but I would try doing the above as a first step.

The query below runs in 3 seconds and resolved the problem. Thanks.
select at.person_id, goc.group_id, count(at.attended) as attendance
into #a
from core_occurrence_attendance at
join core_occurrence o on at.occurrence_id = o.occurrence_id
join smgp_group_occurrence goc on o.occurrence_id = goc.occurrence_id
where goc.group_id in (select group_id from #target_groups)
and o.occurrence_start_time between #start_date and #end_date
group by at.person_id, goc.group_id
select p.person_id, cfm.family_id, p.nick_name, p.last_name, s.lookup_value as group_role, gm.active, g.group_id, g.group_name,
g.active as group_active, tg.category, gc.cluster_name, coalesce(#a.attendance,0) as att--, --this field is the culprit
from smgp_group g
join smgp_group_cluster gc on g.group_cluster_id = gc.group_cluster_id
join smgp_member gm on g.group_id = gm.group_id
join core_person p on gm.person_id = p.person_id
join core_family_member cfm on p.person_id = cfm.person_id
join core_lookup s on gm.role_luid = s.lookup_id
join #target_groups tg on g.group_id = tg.group_id
left join #a on p.person_id = #a.person_id and g.group_id = #a.group_id
where tg.category = 'adults' or (tg.category = 'kids' and s.lookup_value in ('Leader-Teacher','Assistant Leader'))

Related

JOIN two SELECT without UNION

It needs to download the ID, name and surname of those who have registered by the deadline and have an identity card (with the relevant conditions) or passport (with the relevant conditions). ID card and passport are two separate tables.
I have made the SQL queries in UNION format and it works:
select distinct p.id, p.name, p.surname from persons.person p
join persons.documents d on d.person_id = p.id
join persons.id_card idd on d.id_card_id = idd.id
join persons.id_card_to_registration ir on idd.id = ir.id_card
join registrations.registration r on ir.registration_id = r.id
where p.created_at >= '2022-01-01'
and p.created_at <= '2022-03-30'
and p.registration_id = r.id
and ir.status in (0,5)
UNION
select distinct p.id, p.name, p.surname from persons.person p
join persons.documents d on d.person_id = p.id
join persons.passport pass on d.passport_id = pass.id
join persons.passport_country pc on pc.id = pass_country_id
join persons.passport_to_registration pr on pass.id = pr.passport_id
join registrations.registration r on pr.registration_id = r.id
where p.created_at >= '2022-01-01'
and p.created_at <= '2022-03-30'
and p.registration_id = r.id
and pc.zone in (0,1) or (pc.zone is null and pass.safe = true);
I would now like to do this SQL in one query without union and unfortunately it doesn't work for me - I tried to do it like this:
select distinct p.id, p.name, p.surname from persons.person p
join persons.documents d on d.person_id = p.id
left join persons.id_card idd on d.id_card_id = idd.id
left join persons.id_card_to_registration ir on idd.id = ir.id_card
left join persons.passport pass on d.passport_id = pass.id
left join persons.passport_country pc on pc.id = pass_country_id
left join persons.passport_to_registration pr on pass.id = pr.passport_id
join registrations.registration r on ir.registration_id = r.id
where p.created_at >= '2022-01-01'
and p.created_at <= '2022-03-30'
and p.registration_id = r.id
and (ir.status in (0,5) or ir.status is null)
and pc.zone in (0,1) or (pc.zone is null and pass.safe = true)
And it doesn't return any records to me. I would like some advice on what error I have made. And is it possible to create such a query without union?
The SQL in clause may help to filter by each condition. This may be the way to avoid the union clause, please let me know if this works for you:
select distinct p.id, p.name, p.surname from persons.person p
where p.created_at >= '2022-01-01'
and p.created_at <= '2022-03-30'
and ((p.id in
(select distinct p2.id from persons.person p2
d.person_id from persons.documents d on d.person_id = p2.id
join persons.id_card idd on d.id_card_id = idd.id
join persons.id_card_to_registration ir on idd.id = ir.id_card
join registrations.registration r on ir.registration_id = r.id
where p2.registration_id = r.id
and ir.status in (0,5)
)
) or (
(p.id in
(select distinct p3.id from persons.person p3
d.person_id from persons.documents d on d.person_id = p3.id
join persons.passport pass on d.passport_id = pass.id
join persons.passport_country pc on pc.id = pass_country_id
join persons.passport_to_registration pr on pass.id = pr.passport_id
join registrations.registration r on pr.registration_id = r.id
where p3.registration_id = r.id
and pc.zone in (0,1) or (pc.zone is null and pass.safe = true);
)
)
))
I think you'll have better performance if you can convert the query to use EXISTS.
SELECT DISTINCT p.id, p.name, p.surname
FROM persons.person p
WHERE p.created_at >= '2022-01-01'
AND p.created_at <= '2022-03-30'
AND(EXISTS (SELECT *
FROM persons.documents d
JOIN persons.id_card idd
ON d.id_card_id = idd.id
JOIN persons.id_card_to_registration ir
ON idd.id = ir.id_card
JOIN registrations.registration r
ON ir.registration_id = r.id
WHERE p.id = d.person_id
AND p.registration_id = r.id
AND ir.status IN (0,5))
OR EXISTS (SELECT *
FROM persons.documents d
JOIN persons.passport pass
ON d.passport_id = pass.id
JOIN persons.passport_country pc
ON pc.id = pass_country_id
JOIN persons.passport_to_registration pr
ON pass.id = pr.passport_id
JOIN registrations.registration r
ON pr.registration_id = r.id
WHERE p.id = d.person_id
AND p.registration_id = r.id
AND pc.zone IN (0,1)
OR (pc.zone IS NULL
AND pass.safe = TRUE)))

SQL join with union

Cant understand what is the problem the union query works just fine if run it without join but i need to use it inside left join. And when I try it shows me the error that says that column DisplayName used more than once. What im doing wrong?
LEFT JOIN (
SELECT
p.Id,
zvl.DisplayName,
dlz.DisplayName
FROM
cm.Projects p
LEFT JOIN cm.ProjectVisualBlocks pvb ON pvb.ProjectId= p.Id
JOIN DBL.[Bl_2b987e3e-4939-e711-80c8-000d3af7ae73] b ON pvb.Id = b.ProjectVisualBlockId
LEFT JOIN cm.Participants [User] ON [User].Id= b.[4a789414-4a39-e711-80c8-000d3af7ae73]
LEFT JOIN cm.Participants [User_A] ON [User_A].Id= b.[4c789414-4a39-e711-80c8-000d3af7ae73]
WHERE
p.ProjectTypeId= '526ba5f2-4c39-e711-80c8-000d3af7ae73'
UNION
SELECT
p.Id,
zvl.DisplayName,
dlz.DisplayName
FROM
cm.Projects p
LEFT JOIN (
SELECT
pp.Id,
d.DisplayName
FROM
cm.Projects pp
LEFT JOIN cm.ProjectVisualBlocks pvb ON pvb.ProjectId= pp.Id
JOIN cm.ProjectLines pl ON pvb.Id = pl.ProjectVisualBlockId
JOIN DBL.[Ln_F1CC2727-45C5-E711-80C2-0CC47A966E25] ln ON ln.ProjectLineId = pl.Id
AND ln.ProjectVisualBlockId = pvb.Id
JOIN cm.Participants d ON d.Id= ln.[F2CC2727-45C5-E711-80C2-0CC47A966E25]
) dlz ON dlz.Id= p.Id
LEFT JOIN (
SELECT
p.Id,
[User].DisplayName
FROM
cm.Projects p
LEFT JOIN cm.ProjectVisualBlocks pvb ON pvb.ProjectId= p.Id
JOIN cm.ProjectLines pl ON pvb.Id = pl.ProjectVisualBlockId
JOIN DBL.[Ln_A25439AA-B51B-E811-80C2-0CC47A966E25] l ON l.ProjectLineId = pl.Id
AND l.ProjectVisualBlockId = pvb.Id
JOIN cm.Participants [User] ON [User].Id= l.[F0CC2727-45C5-E711-80C2-0CC47A966E25]
) zvl ON zvl.Id= p.Id
WHERE
p.ProjectTypeId= 'b498aad9-3ec5-e711-80c2-0cc47a966e25'
) xavdol ON xavdol.Id= p.Id
you have used column DisplayName twice, change to this:
LEFT JOIN (
SELECT
p.Id,
zvl.DisplayName,
dlz.DisplayName as DisplayName2
FROM
cm.Projects p
LEFT JOIN cm.ProjectVisualBlocks pvb ON pvb.ProjectId= p.Id
JOIN DBL.[Bl_2b987e3e-4939-e711-80c8-000d3af7ae73] b ON pvb.Id = b.ProjectVisualBlockId
LEFT JOIN cm.Participants [User] ON [User].Id= b.[4a789414-4a39-e711-80c8-000d3af7ae73]
LEFT JOIN cm.Participants [User_A] ON [User_A].Id= b.[4c789414-4a39-e711-80c8-000d3af7ae73]
WHERE
p.ProjectTypeId= '526ba5f2-4c39-e711-80c8-000d3af7ae73'
UNION
SELECT
p.Id,
zvl.DisplayName,
dlz.DisplayName as DisplayName2
FROM
cm.Projects p
LEFT JOIN (
SELECT
pp.Id,
d.DisplayName
FROM
cm.Projects pp
LEFT JOIN cm.ProjectVisualBlocks pvb ON pvb.ProjectId= pp.Id
JOIN cm.ProjectLines pl ON pvb.Id = pl.ProjectVisualBlockId
JOIN DBL.[Ln_F1CC2727-45C5-E711-80C2-0CC47A966E25] ln ON ln.ProjectLineId = pl.Id
AND ln.ProjectVisualBlockId = pvb.Id
JOIN cm.Participants d ON d.Id= ln.[F2CC2727-45C5-E711-80C2-0CC47A966E25]
) dlz ON dlz.Id= p.Id
LEFT JOIN (
SELECT
p.Id,
[User].DisplayName
FROM
cm.Projects p
LEFT JOIN cm.ProjectVisualBlocks pvb ON pvb.ProjectId= p.Id
JOIN cm.ProjectLines pl ON pvb.Id = pl.ProjectVisualBlockId
JOIN DBL.[Ln_A25439AA-B51B-E811-80C2-0CC47A966E25] l ON l.ProjectLineId = pl.Id
AND l.ProjectVisualBlockId = pvb.Id
JOIN cm.Participants [User] ON [User].Id= l.[F0CC2727-45C5-E711-80C2-0CC47A966E25]
) zvl ON zvl.Id= p.Id
WHERE
p.ProjectTypeId= 'b498aad9-3ec5-e711-80c2-0cc47a966e25'
) xavdol ON xavdol.Id= p.Id
sql
sql-server

Nested query WHERE procedure code IN and AND

Would like to take the following Query and alter it so that it brings back ONLY records where each patient (based on MRN) has BOTH ProcedureCodeList IN ('115-1','117-1','311-1') AND ProcedureCodeList = '119-103'
SELECT P.SiteID, O.ProcedureCodeList, P.MRN, PINFO.LastName, PINFO.FirstName, PINFO.[State] AS Species, PINFO.City AS Breed, O.ProcedureDescList, RF.FieldName, RF.FieldValue, R.ContentText
, R.LastSignDate
FROM ReportFinding RF
INNER JOIN Report R
ON RF.ReportID = R.ReportID
INNER JOIN [Order] O
ON R.ReportID = O.ReportID
INNER JOIN Visit V
ON O.VisitID = V.VisitID
INNER JOIN Patient P
ON P.PatientID = V.PatientID
INNER JOIN PersonalInfo PINFO
ON P.PersonalInfoID = PINFO.PersonalInfoID
WHERE
O.ProcedureCodeList IN ('115-1','117-1','119-103')
ORDER BY R.LastSignDate DESC
There are a couple of ways to solve this. One way is to create two subqueries and join them on the MRN.
SELECT a.SiteID, a.ProcedureCodeList, a.MRN, a.LastName, a.FirstName, a.Species, a.Breed, a.ProcedureDescList, a.FieldName, a.FieldValue, a.ContentText, a.LastSignDate
FROM
(SELECT P.SiteID, O.ProcedureCodeList, P.MRN, PINFO.LastName, PINFO.FirstName, PINFO.[State] AS Species, PINFO.City AS Breed, O.ProcedureDescList, RF.FieldName, RF.FieldValue, R.ContentText, R.LastSignDate
FROM ReportFinding RF
INNER JOIN Report R ON RF.ReportID = R.ReportID
INNER JOIN [Order] O ON R.ReportID = O.ReportID
INNER JOIN Visit V ON O.VisitID = V.VisitID
INNER JOIN Patient P ON P.PatientID = V.PatientID
INNER JOIN PersonalInfo PINFO ON P.PersonalInfoID = PINFO.PersonalInfoID
WHERE O.ProcedureCodeList IN ('115-1','117-1','119-103')) as a
JOIN
(SELECT P.MRN
FROM [Order]
INNER JOIN Visit V ON O.VisitID = V.VisitID
INNER JOIN Patient P ON P.PatientID = V.PatientID
WHERE O.ProcedureCodeList = '119-103') as b ON a.MRN = b.MRN
ORDER BY a.LastSignDate
The PersonalInfo table is not needed in the second query. I don't think ReportFinding and Report are either, based on your JOINs. It depends on what these tables are actually doing.
Another way starts with the original query and adds the following to the WHERE clause (before the ORDER BY):
AND P.MRN IN
(SELECT P.MRN
FROM [Order]
INNER JOIN Visit V ON O.VisitID = V.VisitID
INNER JOIN Patient P ON P.PatientID = V.PatientID
WHERE O.ProcedureCodeList = '119-103')
I would look at the execution plans of both solutions to know which is the better one in this case.

Sql join 1 instance

I require some help with my very shaky sql skills.
Say I have the following select statement:
SELECT DISTINCT
p.ProjectId,
p.Title,
i.Name,
p.StartDate,
p.EndDate,
ped.ProjectEthicsDocumentId,
st.Description AS StatusText
FROM
dbo.Project p
inner join dbo.WorkflowHistory w ON p.ProjectId = w.ProjectId
left join dbo.ProjectInstitution pi ON pi.ProjectId = p.ProjectId
left join dbo.Institution i ON i.InstitutionId = pi.InstitutionId
left join dbo.ProjectEthicsDocument ped on p.ProjectId = ped.ProjectId
left join dbo.Status st ON p.StatusId = st.StatusId
This will return all the projects and other relevant details from the relevant tables. Now, say I have 2 institutions for 'Project A'. This statement will return 2 rows for 'Project A', one for each institution. How do I set it so that it only returns the first row of each project it finds? I want one instance of every project with say the first institution found.
The easiest way is probably with the row_number() function:
select *
from (SELECT DISTINCT p.ProjectId, p.Title, i.Name, p.StartDate,p.EndDate,
ped.ProjectEthicsDocumentId, st.Description AS StatusText,
row_number() over (partition by p.ProjectId order by i.InstitutionId) as seqnum
FROM dbo.Project p
inner join dbo.WorkflowHistory w ON p.ProjectId = w.ProjectId
left join dbo.ProjectInstitution pi ON pi.ProjectId = p.ProjectId
left join dbo.Institution i ON i.InstitutionId = pi.InstitutionId
left join dbo.ProjectEthicsDocument ped on p.ProjectId = ped.ProjectId
left join dbo.Status st ON p.StatusId = st.StatusId
) p
where seqnum = 1;
You can move selecting institution name to a subquery. This way you it doesn't affect how other tables are joined.
SELECT DISTINCT
p.ProjectId,
p.Title,
(SELECT TOP 1 i.Name FROM dbo.Institution i
INNER JOIN dbo.ProjectInstitution pi ON i.InstitutionId = pi.InstitutionId
WHERE pi.ProjectId = p.ProjectId) AS Name,
p.StartDate,
p.EndDate,
ped.ProjectEthicsDocumentId,
st.Description AS StatusText
FROM
dbo.Project p
inner join dbo.WorkflowHistory w ON p.ProjectId = w.ProjectId
left join dbo.ProjectEthicsDocument ped on p.ProjectId = ped.ProjectId
left join dbo.Status st ON p.StatusId = st.StatusId
you could use
;with cte as
(
<your select statement> `,`
Row_number() over(partition by <column that has 2 records> order by ProjectId) as rn
)
--then do this
select * from cte where rn=1

Find a way to query a list of items that meet ALL of a criteria using SQL

I'm trying to do the following:
select i.FirstName, i.LastName, COUNT(1) from (
select u.Id, uw.WidgetId from [DB].[dbo].[Widgets] w inner join
UserWidgets uw on w.Id = uw.WidgetId inner join
Users u on uw.UserId = u.Id
where uw.WidgetId in ('29017318-FD89-4952-A3A2-8405BD5C5C44',
'BDB7D25C-0794-4965-842D-E6D03A250418',
'CB4553AC-A47B-4AA6-9231-5C59C8F97655')
group by u.Id, uw.WidgetId
) a
inner join [Db2].[dbo].[Identities] i on a.Id = i.IdentityId
group by i.LastName, i.FirstName
order by i.LastName, i.FirstName
What I want is to ensure that the "In" statement requires that the User ONLY has those 3 Id's. No more, no less.
What is the best way to do this?
Try:
select i.FirstName, i.LastName, COUNT(1) from (
select u.Id, uw.WidgetId from [DB].[dbo].[Widgets] w inner join
UserWidgets uw on w.Id = uw.WidgetId
and uw.WidgetId in ('29017318-FD89-4952-A3A2-8405BD5C5C44',
'BDB7D25C-0794-4965-842D-E6D03A250418',
'CB4553AC-A47B-4AA6-9231-5C59C8F97655')
inner join Users u on uw.UserId = u.Id
left join UserWidgets uw2 on uw2.userid = u.id
and uw2.WidgetId not in ('29017318-FD89-4952-A3A2-8405BD5C5C44',
'BDB7D25C-0794-4965-842D-E6D03A250418',
'CB4553AC-A47B-4AA6-9231-5C59C8F97655')
where uw2.widgetid is null
group by u.Id, uw.WidgetId
) a
inner join [Db2].[dbo].[Identities] i on a.Id = i.IdentityId
group by i.LastName, i.FirstName
having count(1) = 3
order by i.LastName, i.FirstName
select i.FirstName, i.LastName, COUNT(1) from (
select u.Id, uw.WidgetId from [DB].[dbo].[Widgets] w inner join
UserWidgets uw on w.Id = uw.WidgetId inner join
Users u on uw.UserId = u.Id
where uw.WidgetId in ('29017318-FD89-4952-A3A2-8405BD5C5C44',
'BDB7D25C-0794-4965-842D-E6D03A250418',
'CB4553AC-A47B-4AA6-9231-5C59C8F97655')
AND count(WidgetId) = 3
group by u.Id, uw.WidgetId
) a
inner join [Db2].[dbo].[Identities] i on a.Id = i.IdentityId
group by i.LastName, i.FirstName
order by i.LastName, i.FirstName
I added the 'AND count(WidgetId) = 3' in to the query...i believe that would work?
This should work:
SELECT i.firstname,
i.lastname,
COUNT(1)
FROM (SELECT u.id,
uw.widgetid
FROM [DB].[dbo].[Widgets] w
INNER JOIN userwidgets uw
ON w.id = uw.widgetid
INNER JOIN users u
ON uw.userid = u.id
WHERE uw.widgetid IN ( '29017318-FD89-4952-A3A2-8405BD5C5C44',
'BDB7D25C-0794-4965-842D-E6D03A250418'
,
'CB4553AC-A47B-4AA6-9231-5C59C8F97655' )
AND NOT EXISTS (SELECT *
FROM [DB].[dbo].[Widgets] w2
INNER JOIN userwidgets uw2
ON w2.id = uw2.id
WHERE w2.id = w.id
AND uw2.widgetid NOT IN (
'29017318-FD89-4952-A3A2-8405BD5C5C44',
'BDB7D25C-0794-4965-842D-E6D03A250418'
,
'CB4553AC-A47B-4AA6-9231-5C59C8F97655' ))
GROUP BY u.id,
uw.widgetid) a
HAVING COUNT(DISTINCT uw.widgetid) = 3
INNER JOIN [Db2].[dbo].[Identities] i
ON a.id = i.identityid
GROUP BY i.lastname,
i.firstname
ORDER BY i.lastname,
i.firstname