SQL use nested select in middle of inner join - sql

Is it possible to use a select in the middle of joining...
I am trying to do the following:
FROM
tblorders o
INNER JOIN tblunits u on o.id = u.orderid
INNER JOIN ((SELECT
,Min(n.date) as [MinDate]
from tblNotes n
Where n.test = 'test') te
INNER JOIN tblnotes n on te.id = n.id
and te.[MinDate] = n.AuditinsertTimestamp)
INNER Join tblClient c ON o.ClientId = c.Id
Basically in the select in the middle of the query it is selecting only the notes with min date. The problem is I need to do this here because I need from tblOrders to be the first table.......
Suggestions?

The INNER JOIN failed because you have a leading comma here:
,Min(n.date) as [MinDate]
I think you are looking for something like this:
SELECT ...
FROM tblorders o
INNER JOIN tblunits u on o.id = u.orderid
INNER JOIN (
SELECT id, Min(date) as [MinDate]
from tblNotes
Where test = 'test'
group by id
) te <-- not sure what JOIN clause to use here, please post schema
INNER JOIN tblnotes n on te.id = n.id
and te.[MinDate] = n.AuditinsertTimestamp
INNER Join tblClient c ON o.ClientId = c.Id

You are missing an alias and join condition:
FROM
tblorders o
INNER JOIN tblunits u on o.id = u.orderid
INNER JOIN ((SELECT Min(n.date) as [MinDate]
from tblNotes n
Where n.test = 'test') te
INNER JOIN tblnotes n on te.id = n.id
and te.[MinDate] = n.AuditinsertTimestamp)
-- missing
AS z
ON <join conditions haere>
INNER Join tblClient c ON o.ClientId = c.Id

Yes, you can have a Select in a Join.

Related

SQL - Multiple join left with sum doesn't give expected result

Here is my request
SELECT j.* ,
c.name as client_name ,
s.name as supplier_name,
s.ID as supplier_id ,
mt.* ,
SUM(pb.require_followup) as nb_followup,
SUM(ws.worked_time) as hours_on_job,
SUM(iv.total) as total_price,
SUM(iv.hour_expected) as hours_planned,
j.ID as ID
FROM $wpdb->posts j
LEFT JOIN ".Job::$META_TABLE." mt ON mt.post_id = j.ID
LEFT JOIN ".Job::$LINK_TABLE_JOB_CONTACT." l1 ON l1.job_id = j.ID
LEFT JOIN ".Contact::$TABLE_NAME." c ON c.ID = l1.contact_id
LEFT JOIN ".Supplier::$TABLE_NAME." s ON s.ID = c.supplier_id
LEFT JOIN ".Problem::$TABLE_NAME." pb ON pb.job_id = j.ID
LEFT JOIN ".Worksheet::$TABLE_NAME." ws ON ws.job_id = j.ID
LEFT JOIN ".Invoice::$TABLE_NAME." iv ON iv.job_id = j.ID
WHERE j.post_status = 'publish'
AND j.post_type = 'job'
".implode(' ',$where_condition)."
GROUP BY j.ID
ORDER BY j.post_date DESC
the Problem is that result for SUM is wrong when I LEFT JOIN other table.
The row 53 for example give 105 for nb_followup instead of 1
Where this request return the right result simply by removing the last 2 LEFT JOIN : LEFT JOIN ".Worksheet::$TABLE_NAME." ws ON ws.job_id = j.ID and
LEFT JOIN ".Invoice::$TABLE_NAME." iv ON iv.job_id = j.ID
SELECT j.* ,
c.name as client_name ,
s.name as supplier_name,
s.ID as supplier_id ,
mt.* ,
SUM(pb.require_followup) as nb_followup,
j.ID as ID
FROM $wpdb->posts j
LEFT JOIN ".Job::$META_TABLE." mt ON mt.post_id = j.ID
LEFT JOIN ".Job::$LINK_TABLE_JOB_CONTACT." l1 ON l1.job_id = j.ID
LEFT JOIN ".Contact::$TABLE_NAME." c ON c.ID = l1.contact_id
LEFT JOIN ".Supplier::$TABLE_NAME." s ON s.ID = c.supplier_id
LEFT JOIN ".Problem::$TABLE_NAME." pb ON pb.job_id = j.ID
WHERE j.post_status = 'publish'
AND j.post_type = 'job'
".implode(' ',$where_condition)."
GROUP BY j.ID
ORDER BY j.post_date DESC
Also removing only LEFT JOIN ".Invoice::$TABLE_NAME." iv ON iv.job_id = j.ID will give 15 as result for the row 53
To resume
Full request give 105 -> wrong should be 1
removing the last join give 15 -> wrong should be 1
removing the last 2 join give 1 -> Correct
You need to calculate the SUM()s BEFORE you join, otherwise the rows multiply because of the joins and this in turn leads to errors in summation. e.g.
SELECT
j.ID as ID
, pb.nb_followup
FROM $wpdb->posts j
LEFT JOIN (select pb.job_id, SUM(pb.require_followup) as nb_followup from ".Problem::$TABLE_NAME." pb GROUP BY pb.job_id) pb ON pb.job_id = j.ID
The other problem you are facing is that MySQL permits "lazy syntax" for group by. Don't use this lazy syntax or you will get unexpected error/bugs. It is very simple to avoid, REPEAT every column of the select clause in the group by clause UNLESS the column is using an aggregate function such as SUM(), COUNT(), MIN(), MAX() and so on.e.g.
select a.col1, b.col2, c.col3 , sum(d.col4)
from a
inner join b on a.id = b.aid
inner join c on b.id = c.bid
inner join d on c.id = d.cid
group by a.col1, b.col2, c.col3

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.

Too many results in query

I'm fetching some data from our database in MSSQL. Out of this data I want to determine who created the client entry and who took the first payment from this client.
There can be many payment entries for a client on a single booking/enquiry and at the moment, my query shows results for each payment. How can I limit the output to only show the first payment entry?
My query:
SELECT
c.FirstName,
c.LastName,
c.PostalCode,
o.OriginOfEnquiry,
s.SuperOriginName,
c.DateOfCreation,
DATEDIFF(day, c.DateOfCreation, p.DateOfCreation) AS DaysToPayment,
pc.PackageName,
CONCAT(u.FirstName, ' ', u.LastName) AS CreateUser,
(SELECT CONCAT(u.FirstName, ' ', u.LastName)
WHERE u.UserID = p.UserID ) AS PaymentUser
FROM tblBookings b
INNER JOIN tblPayments p
ON b.BookingID = p.BookingID
INNER JOIN tblEnquiries e
ON e.EnquiryID = b.EnquiryID
INNER JOIN tblCustomers c
ON c.CustomerID = e.CustomerID
INNER JOIN tblOrigins o
ON o.OriginID = e.OriginID
INNER JOIN tblSuperOrigins s
ON s.SuperOriginID = o.SuperOriginID
INNER JOIN tblBookingPackages bp
ON bp.bookingID = p.BookingID
INNER JOIN tblPackages pc
ON pc.PackageID = bp.packageID
INNER JOIN tblUsers u
ON u.UserID = c.UserID
WHERE c.DateOfCreation >= '2016-06-01' AND c.DateOfCreation < '2016-06-30'
AND p.PaymentStatusID IN (1,2)
AND e.CustomerID = c.CustomerID
AND p.DeleteMark != 1
AND c.DeleteMark != 1
AND b.DeleteMark != 1
;
I tried adding a "TOP 1" to the nested select statement for PaymentUser, but it made no difference.
you can use cross apply with top 1:
FROM tblBookings b
cross apply
(select top 1 * from tblPayments p where b.BookingID = p.BookingID) as p
Instead of table tblPayments specify sub-query like this:
(SELECT TOP 1 BookingID, UserID, DateOfCreation
FROM tblPayments
WHERE DeleteMark != 1
AND PaymentStatusID IN (1,2)
ORDER BY DateOfCreation) as p
I'm assuming that tblPayments has a primary key column ID. If it is true, you can use this statment:
FROM tblBookings b
INNER JOIN tblPayments p ON p.ID = (
SELECT TOP 1 ID
FROM tblPayments
WHERE BookingID = b.BookingID
AND DeleteMark != 1
AND PaymentStatusID IN (1,2)
ORDER BY DateOfCreation)

rewrite query in orden to avoid some records

I dont know how to rewrite the the query in order to avoid records with ISPRIMARY = 0. In other words the values allowed must be ISPRIMARY = 1 and ISPRIMARY = NULL.
The query is the next:
select
S.assetnum, S.description, S.serialnum,
S.epp_codactfij, S.location, L.description,
S.EPP_NUMCONTRATO, S.PURCHASEPRICE, U.personid, U.isprimary
from maximo.asset S
left join maximo.locations L on S.location=L.location
left join maximo.assetusercust U on S.assetnum=U.assetnum
where
S.siteid ='TI'
order by S.assetnum
Try adding the following line to the WHERE clause:
select
S.assetnum, S.description, S.serialnum, S.epp_codactfij, S.location, L.description, S.EPP_NUMCONTRATO, S.PURCHASEPRICE, U.personid, U.isprimary
from maximo.asset S
left join maximo.locations L on S.location=L.location
left join maximo.assetusercust U on S.assetnum=U.assetnum
where
S.siteid ='TI'
-- add this line
AND (U.isprimary = 1 OR U.isprimary IS NULL)
order by S.assetnum
select
S.assetnum, S.description, S.serialnum, S.epp_codactfij,
S.location, L.description, S.EPP_NUMCONTRATO,
S.PURCHASEPRICE, U.personid, U.isprimary
from maximo.asset S
left join maximo.locations L on S.location=L.location
left join maximo.assetusercust U on S.assetnum=U.assetnum
WHERE S.siteid ='TI' AND (U.isprimary = 1 OR U.isprimary IS NULL)
order by S.assetnum;
or:
select
S.assetnum, S.description, S.serialnum, S.epp_codactfij,
S.location, L.description, S.EPP_NUMCONTRATO,
S.PURCHASEPRICE, U.personid, U.isprimary
from maximo.asset S
left join maximo.locations L on S.location=L.location
left join maximo.assetusercust U on S.assetnum=U.assetnum
WHERE S.siteid ='TI' AND (U.isprimary <> 0 OR U.isprimary IS NULL)
order by S.assetnum;

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