Execute a query foreach row from a result set. - sql

I have a query that needs to be executed for each row in a result set. I understand that I need to do this with cursors, but I dont know how.
The first query contains customers with a specific condition, I need to retreive the Id:s from them.
The second query selects the latest activity from these customers.
Query 1
SELECT DISTINCT c.Name, c.Id
FROM Persons p
JOIN Customers c ON c.id = p.Customer_Id
WHERE C.State = 3 AND c.SalesPerson_Id ='A3160011-CAA6-E411-A83E-AC7BA1B90A6D' AND c.Id NOT IN
(
SELECT Distinct p.Customer_Id
FROM Activities a
JOIN Persons p
ON a.Person_Id = p.Id
WHERE a.[Type] IN (5,6) AND a.[Time] > getdate()-30 AND a.[Time] < getdate()
)
AND c.id NOT IN
(
SELECT
DISTINCT p.Customer_Id
FROM
Persons p
JOIN HtmlEmails he ON p.Id = he.UserId
JOIN ReportLog rl ON he.Id = rl.HtmlEmails_Id
WHERE rl.[Time] > getdate()-30 AND rl.[Time] < getdate())
Query 2
SELECT TOP 1 *
FROM(
SELECT TOP 1 c.Name AS 'CustomerName', c.Id AS 'CustomerId', a.[Time] AS 'LastActivity',(p.FirstName + ' ' + p.LastName) AS 'UserFullName' , 'Login' AS 'Type'
FROM Activities a
JOIN Persons p ON p.Id = a.Person_Id
JOIN Customers c ON p.Customer_Id = c.Id
WHERE a.[Type] IN (5,6) AND a.[Time] < getdate()-30
ORDER BY a.[Time] DESC
UNION
SELECT TOP 1 c.Name AS 'CustomerName', c.Id AS 'CustomerId', a.[Time] AS 'LastActivity',(p.FirstName + ' ' + p.LastName) AS 'UserFullName' , 'Email' AS 'Type'
FROM Activities a
JOIN HtmlEmails he ON a.TargetId = he.Id
JOIN ReportLog rl on he.Id = rl.HtmlEmails_Id
JOIN Persons p ON p.Id = a.Person_Id
JOIN Customers c ON p.Customer_Id = c.Id
WHERE a.[Time] < getdate()-30
ORDER BY a.[Time] DESC
)AS x
ORDER BY 'LastActivity' DESC
Thanks!

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

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)

CTE Missing Records

The first query listed below returns some logistical data associated with hires that have been made within a particular period of time. The query returns 478 records.
SELECT c.candidate_id AS candidate_id
,o.name
,j.name AS job_title
,c.applied_from
,job_id AS job_id
,cjs.score AS smart_rank_score
,cjs.is_completed AS smartrank_completion_status
,c.hired_at
FROM candidate_jobs c
LEFT JOIN organizations o ON o.id = c.organization_id
LEFT JOIN candidate_job_surveys cjs ON cjs.candidate_job_id = c.id
LEFT JOIN jobs j ON j.id = c.job_id
WHERE o.name LIKE ANY ('{"%Tutor Doctor%"}')
AND c.hired_at :: date BETWEEN '2015-01-01' AND '2016-02-22'
ORDER BY 8 DESC
However, when I attempted to add a CTE (see below) that displays each hire's final "post hire check in score", the query only returns 236 records. Ideally, I'd like the query to either return a score or null value for each of the initial 478 hire records.
WITH final_post_hire_score (candidate_id, final_score) AS
(SELECT c.candidate_id
,p.score
FROM post_hire_followup_reviews p
LEFT JOIN candidate_jobs c ON c.id = p.candidate_job_id
WHERE p.check_in_number = 3)
SELECT c.candidate_id AS candidate_id
,o.name
,j.name AS job_title
,c.applied_from
,job_id AS job_id
,cjs.score AS smart_rank_score
,cjs.is_completed AS smartrank_completion_status
,c.hired_at
,final_score
FROM final_post_hire_score f
LEFT JOIN candidate_jobs c ON c.candidate_id = f.candidate_id
LEFT JOIN organizations o ON o.id = c.organization_id
LEFT JOIN candidate_job_surveys cjs ON cjs.candidate_job_id = c.id
LEFT JOIN jobs j ON j.id = c.job_id
WHERE o.name LIKE ANY ('{"%Tutor Doctor%"}')
AND c.hired_at :: date BETWEEN '2015-01-01' AND '2016-02-22'
ORDER BY 8 DESC
Missing records are due to the filter's, Move the filter's to ON condition else your LEFT OUTER JOIN will be implicitly converted to INNER JOIN
When you are using LEFT OUTER JOIN right table filter's should be present in ON condition else the NULL values for non matching records will get filtered
WITH final_post_hire_score (candidate_id, final_score)
AS (SELECT c.candidate_id,
p.score
FROM post_hire_followup_reviews p
LEFT JOIN candidate_jobs c
ON c.id = p.candidate_job_id
WHERE p.check_in_number = 3)
SELECT c.candidate_id AS candidate_id,
o.NAME,
j.NAME AS job_title,
c.applied_from,
job_id AS job_id,
cjs.score AS smart_rank_score,
cjs.is_completed AS smartrank_completion_status,
c.hired_at,
final_score
FROM final_post_hire_score f
LEFT JOIN candidate_jobs c
ON c.candidate_id = f.candidate_id
AND c.hired_at :: date BETWEEN '2015-01-01' AND '2016-02-22'
LEFT JOIN organizations o
ON o.id = c.organization_id
AND o.NAME LIKE ANY ( '{"%Tutor Doctor%"}' )
LEFT JOIN candidate_job_surveys cjs
ON cjs.candidate_job_id = c.id
LEFT JOIN jobs j
ON j.id = c.job_id
ORDER BY 8 DESC
I think there's an extra
WHERE p.check_in_number = 3
that isn't anywhere else.

Using a group by to group a select statement

Using a group by to group a select stament
SELECT
k.Ivalue, k.JOBDESCRIPTION ,
count( k.Ivalue) as TOTAL
FROM
(SELECT
a."ID" as Ivalue, b."JOBDESCRIPTION", rq."CURRENTSTATUS"
FROM
tblG2o_Requests a
INNER JOIN
tblG2o_JOBS b ON a."JOBPOSTID" = b."ID"
INNER JOIN
(SELECT
r.REQUESTID, ir."CURRENTSTATUS"
FROM
TBLG2O_RESULTSPOOL r
INNER JOIN
tblG2o_Requests ir ON r.RequestID = ir."ID"
WHERE
r.ShortListed = '1') rq ON rq.REQUESTID = a."ID"
WHERE
"ACTIVE" = '1'
AND "DATECOMPLETED" IS NULL
ORDER BY
"REQUESTDATE" DESC) k
GROUP BY
k.JOBDESCRIPTION
What is the question? You seem to be missing the group by clause, and you do not need double quotes around field names unless you have spaces in them, and even then, if TSQL for example, you would use [] in preference.
I had to remove an ORDER BY in the subquery, that isn't allowed unless other conditions demand it (like TOP n in TSQL)
SELECT
k.Ivalue
, k.JOBDESCRIPTION
, COUNT(k.Ivalue) AS TOTAL
FROM (
SELECT
a.ID AS Ivalue
, b.JOBDESCRIPTION
, rq.CURRENTSTATUS
FROM tblG2o_Requests a
INNER JOIN tblG2o_JOBS b
ON a.JOBPOSTID = b.ID
INNER JOIN (
SELECT
r.REQUESTID
, ir.CURRENTSTATUS
FROM TBLG2O_RESULTSPOOL r
INNER JOIN tblG2o_Requests ir
ON r.RequestID = ir.ID
WHERE r.ShortListed = '1'
) rqenter
ON rq.REQUESTID = a.ID
WHERE ACTIVE = '1'
AND DATECOMPLETED IS NULL
) k
GROUP BY
k.Ivalue
, k.JOBDESCRIPTION
Finally worked
SELECT
k.Ivalue
, l.JOBDESCRIPTION
, k.TOTAL,
k.CURRENTSTATUS
FROM (
SELECT
a.ID AS Ivalue
,b.ID as JobPostID
, rq."CURRENTSTATUS"
,COUNT(a.ID) AS TOTAL
FROM tblG2o_Requests a
INNER JOIN tblG2o_JOBS b
ON a."JOBPOSTID" = b.ID
INNER JOIN (
SELECT
r."REQUESTID"
, ir."CURRENTSTATUS"
FROM TBLG2O_RESULTSPOOL r
INNER JOIN tblG2o_Requests ir
ON r."REQUESTID" = ir.ID
WHERE r."SHORTLISTED" = 1
) rq
ON rq."REQUESTID" = a.ID
WHERE ACTIVE = '1'
AND DATECOMPLETED IS NULL
GROUP BY
a.ID ,b.ID
, rq."CURRENTSTATUS" ) k
inner join tblG2o_JOBS l on k.JobPostID =l.ID
enter code here

Two If selection while a select query

SELECT o.id, o.id as oid, o.id as orderId, o.cid, o.date, o.state,
o.price, o.currency, o.lastChange, o.url AS permalink, o.period,
o.bloggerId, o.bloggerShare, o.offerValidity, o.rebate, o.cid,
o.reason, o.bidReason, o.bidDate, o.bidPeriod, o.rate,
o.lastChange2, o.permalinkDate, o.cancelDate, o.bidValidDate,
o.acceptDate, o.approveDate, o.archived, o.bloggerPrice,
o.customerPrice, o.cancelReason, o.listPrice, o.adFormat,
o.lastPayDate, o.startDate, o.endDate, o.customerBidDate,
o.zoneId, c.campaignStartDate, c.campaignEndDate,
c.type as campaignType, c.test, c.test1, c.special, c.packageId,
c.fixPrice, c.type, c.priceBidding, c.textCreation, o.hiddenField,
o.startDate, p.url as producturlold, p.pressurl, p.companyurl,
p.blogurl, p.mediaurl, p.short,
p.description as productDescription, p.description2, p.image,
c.teaser, c.kind, c.title, mc.country as campaignCountry,
c.minlen, c.productPrice, c.currency as campaignCurrency,
c.productTitle, c.url, c.producturl, c.pressurl, c.companyurl,
c.blogurl, c.mediaurl, c.description, c.image, c.teaser,
c.productReturn, c.testProduct, c.mid as customerId, c.adText,
c.fixAdText, c.requiresBlog, c.bidStop, c.accountingPeriod,
c.actionCodeType, c.actionCodesDescription, ac.code,
ac2.code as massCode, b.title as blogtitle, b.url as bloggerurl,
b.pis as pis, b.uniqueVisitors as uvs, b.pisCounter as pisCounter,
b.uvsCounter as uvsCounter, b.aPI as aPI, b.aUV as aUV,
b.id as blogId, p.title as productTitleOld,
b.lastChange as blogLastChange, b.trRank, r1.rate as orderRate,
r2.Rate as memberRate, b.reviews
FROM rates r1, rates r2, orders o
left join blog b on (o.blogId = b.id)
left join codes ac on (ac.orderId = o.id), campaign c
left join product p on (c.productId = p.id)
left join codes ac2 on (ac2.campaignId = c.id and
c.actionCodeType = 2),
person mc
where o.cid = c.id
and mc.mid = c.mid
and o.id = '223704'
and o.state <> 0
and r1.currency = o.currency
and r2.currency = 'EUR'
and r1.date = FROM_UNIXTIME(o.date, '%Y-%m-%d')
and r2.date = r1.date
I wnat to test if memberRate and orderRate is Null it should continue how can i do that? Any idea?
Tack this on the end should do the trick:
--Within the WHERE clause
AND r2.Rate IS NOT NULL
AND r1.Rate IS NOT NULL
I'm not sure I understand what you are asking, but add and memberRate is not null and orderRate is not null to the end of your query and you will skip all results where either of those two fields are null.