JOIN two SELECT without UNION - sql

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)))

Related

Converting SQL to LINQ with left join and group by

I have done this query by SQL, and want to translate to LINQ
SELECT
ENT_DESC,
CAT_DESC,
PART_NUM,
PART_DESC,
GROUP_CONCAT(PART_OPTIONS.CODE, CHAR(13)) AS CODE,
PART_OPTIONS.PROMPT AS PROMPT,
PART_OPTIONS.DESCRIPTION AS DESCRIPTION,
PART_OPTIONS.PRICE AS PRICE,
ATTRIBUTE_DATA.NOTE,
ATTRIBUTE_DATA.DESCRIPTION AS ATTR_DESC
FROM ITEM_DATA
LEFT JOIN PART_OPTIONS ON ITEM_DATA.ID = PART_OPTIONS.PART_ID
LEFT JOIN ATTRIBUTE_DATA ON ITEM_DATA.ID = ATTRIBUTE_DATA.PART_ID
GROUP BY PART_OPTIONS.PART_ID
I know how to do this without GROUP_CONCAT,
from c in ITEM_DATA
join a in ATTRIBUTE_DATA on c.ID equals a.PART_ID into at
from a in at.DefaultIfEmpty()
join p in PART_OPTIONS on c.ID equals p.PART_ID into pa
from p in pa.DefaultIfEmpty()
select new {
ENT_DESC = c.ENT_DESC,
CAT_DESC = c.CAT_DESC,
PART_NUM = c.PART_NUM,
PART_DESC = c.PART_DESC,
CODE = p.CODE,
PROMPT = p.PROMPT,
DESCRIPTION = p.DESCRIPTION,
PRICE = p.PRICE,
NOTE = a.NOTE,
ATTR_DESC = a.DESCRIPTION,
}
and also I have done GROUP_CONCAT separately
from c in ITEM_DATA
join a in ATTRIBUTE_DATA on c.ID equals a.PART_ID into at
from a in at.DefaultIfEmpty()
join p in PART_OPTIONS on c.ID equals p.PART_ID into pa
from p in pa.DefaultIfEmpty()
group p by p.PART_ID into ps
select new {
CODE = ps.Select(g=>g.CODE),
}
but after that "c" and "a" are gone and i don"t know how to get access to ITEM_DATA and ATTRIBUTE_DATA tables.

Why does adding this field make my query run slow?

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'))

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.

Moodle SCORM SQL

What table and field I need to use in SQL to find SCORM activities that reports back score to Moodle and exclude the SCORM objects that are set as non scoring. Basically looking for the SCORM settings field: Require Minimum Score in SQL
The query:
SELECT
CONCAT(u.firstname,' ',u.lastname ) AS 'fullname',
cc.name AS 'Category',
c.fullname AS 'Course',
gi.itemname AS 'Item Name',
ROUND(gg.finalgrade / gg.rawgrademax * 100 ,2) AS 'Percentage',
asgm.status as 'Status',
p.status AS 'Completed',
gi.itemmodule as 'Type',
s.completionscorerequired AS require_minimum_score
FROM prefix_course AS c
JOIN prefix_course_categories AS cc ON cc.id = c.category
JOIN prefix_context AS ctx ON c.id = ctx.instanceid
JOIN prefix_role_assignments AS ra ON ra.contextid = ctx.id
JOIN prefix_user AS u ON u.id = ra.userid
JOIN prefix_grade_items AS gi ON gi.courseid = c.id
JOIN prefix_scorm AS s ON c.id = s.course
LEFT JOIN prefix_grade_grades AS gg ON gi.id = gg.itemid AND gg.userid = u.id
LEFT JOIN prefix_course_completions AS p ON p.userid = u.id AND p.course = c.id
LEFT JOIN (
SELECT asgm.status as status, c.id as cid, u.id as uid, gi.id as giid
FROM prefix_course AS c
JOIN prefix_course_categories AS cc ON cc.id = c.category
JOIN prefix_context AS ctx ON c.id = ctx.instanceid
JOIN prefix_role_assignments AS ra ON ra.contextid = ctx.id
JOIN prefix_user AS u ON u.id = ra.userid
JOIN prefix_grade_items AS gi ON gi.courseid = c.id
JOIN prefix_assign AS asg ON gi.iteminstance = asg.id and asg.course = c.id
JOIN prefix_assign_submission AS asgm ON asg.id = asgm.assignment and asgm.userid = u.id
) AS asgm ON c.id = asgm.cid and u.id = asgm.uid and gi.id = asgm.giid
WHERE (gi.itemmodule = 'quiz' OR gi.itemmodule= 'assign' OR gi.itemmodule= 'scorm') AND c.visible=1
SELECT s.id AS scormid, s.name AS scormname,
s.completionscorerequired AS require_minimum_score,
c.id AS courseid, c.fullname AS coursename
FROM mdl_scorm s
JOIN mdl_course c ON c.id = s.course

Subquery in from clause, Invalid Identifier in Where clause

select * from iiasa_inventory.inv_device d
join iiasa_inventory.inv_type ty on d.type_id = ty.id
join iiasa_inventory.inv_category c on ty.category_id = c.id
join iiasa_inventory.inv_device_2_barcode b on b.device_id = d.id
join iiasa_inventory.inv_barcodes bc on b.barcode_id = bc.id
join iiasa_inventory.inv_status s on d.status = s.id
join iiasa_inventory.inv_brand br on ty.brand_id = br.id
left join iiasa_inventory.inv_supplier su on su.id = d.supplier_id
left join iiasa_inventory.inv_supplier sup on sup.id = d.maintenance_with
left join (select distinct device_id from
iiasa_inventory.inv_device_2_persons_cc) dp
on dp.device_id = d.id
where dp.active = 1
I am trying to select my data but the where-clause says that "dp.active" is an INVALID Identifier. This is probably because the table dp is in the subquery. I have tried to give it an alias name and some other things I found while browsing stackoverflow, but I cant seem to find a solution. Any idea?
This is Oracle PL/SQL.
Put the check for active = 1 in the subquery as shown below.
select * from iiasa_inventory.inv_device d
join iiasa_inventory.inv_type ty on d.type_id = ty.id
join iiasa_inventory.inv_category c on ty.category_id = c.id
join iiasa_inventory.inv_device_2_barcode b on b.device_id = d.id
join iiasa_inventory.inv_barcodes bc on b.barcode_id = bc.id
join iiasa_inventory.inv_status s on d.status = s.id
join iiasa_inventory.inv_brand br on ty.brand_id = br.id
left join iiasa_inventory.inv_supplier su on su.id = d.supplier_id
left join iiasa_inventory.inv_supplier sup on sup.id = d.maintenance_with
left join (select distinct device_id from iiasa_inventory.inv_device_2_persons_cc where active = 1) dp on dp.device_id = d.id
That is because you are not selecting active column in dp.
select * from iiasa_inventory.inv_device d
join iiasa_inventory.inv_type ty on d.type_id = ty.id
join iiasa_inventory.inv_category c on ty.category_id = c.id
join iiasa_inventory.inv_device_2_barcode b on b.device_id = d.id
join iiasa_inventory.inv_barcodes bc on b.barcode_id = bc.id
join iiasa_inventory.inv_status s on d.status = s.id
join iiasa_inventory.inv_brand br on ty.brand_id = br.id
left join iiasa_inventory.inv_supplier su on su.id = d.supplier_id
left join iiasa_inventory.inv_supplier sup on sup.id = d.maintenance_with
left join (select distinct device_id,active from iiasa_inventory.inv_device_2_persons_cc) dp on dp.device_id = d.id
where dp.active = 1
OR you can just filter from the subquery itself. Like:
left join (select distinct device_id
from iiasa_inventory.inv_device_2_persons_cc
where active=1) dp on dp.device_id = d.id