I need to select reviews for product, but unique by user (i.e. one review from user).
With my code, I select all reviews, and I can see few reviews left by one user.
SELECT
tr.reviewText, tr.reviewDate, tr.reviewRating,
u.userName AS userName,
u.userFirstName AS userFirstName, u.userSurname AS userSurname,
u.countryId AS countryId
FROM
tblReviews tr
INNER JOIN
tblOrderProduct op ON op.orderProductId = tr.orderProductId
AND op.productOptionId IN (SELECT productOptionId
FROM tblProductOption
WHERE productSubCuId = 111
AND productOptionActive = 1)
LEFT JOIN
tblOrder o ON o.orderId = op.orderId
LEFT JOIN
tblUser u ON u.userRandomId = o.userRandomId
WHERE
tr.reviewsStatusId = 2
ORDER BY
tr.reviewRating DESC, tr.reviewDate DESC
OFFSET 0 ROWS FETCH NEXT 100 ROWS ONLY
Can I get just one review from each user?
Maybe I need select userId -> group results by userId and select one per group? [I tried to do so, but I didn't succeed :( ]
You can use row_number to number the reviews and select any one like below:
;with per_user_one_review
as
(SELECT tr.reviewText, tr.reviewDate, tr.reviewRating,
u.userName as userName,
u.userFirstName as userFirstName, u.userSurname as userSurname,
u.countryId as countryId, row_number() over (partition by u.userRandomId order by tr.reviewDate desc) rn
FROM tblReviews tr
INNER JOIN tblOrderProduct op
ON op.orderProductId = tr.orderProductId
AND op.productOptionId IN (
SELECT productOptionId FROM tblProductOption
WHERE productSubCuId = 111 AND productOptionActive = 1
)
LEFT JOIN tblOrder o ON o.orderId = op.orderId
LEFT JOIN tblUser u ON u.userRandomId = o.userRandomId
WHERE tr.reviewsStatusId = 2
ORDER BY tr.reviewRating DESC, tr.reviewDate DESC
OFFSET 0 ROWS FETCH NEXT 100 ROWS ONLY
)
select * from per_user_one_review where rn = 1
It will pick the latest review (reviewDate desc) from the user.
If you need the last review you could use a join with the suquery for max review date grouped by orderProductId
(and as a suggestion you could use a inner join instead of a IN clasue based on a subquery)
select tr.reviewText
, tr.reviewDate
, tr.reviewRating
, u.userName
, u.userFirstName
, u.userSurname
, u.countryId
from tblReviews tr
INNER JOIN (
select max(reviewDate) max_date, orderProductId
from tblReviews
group by orderProductId
) t1 on t1.orderProductId = tr.orderProductId and t1.max_date = tr.reviewDate
INNER JOIN tblOrderProduct op ON op.orderProductId = tr.orderProductId
INNER JOIN (
SELECT productOptionId
FROM tblProductOption
WHERE productSubCuId = 111 AND productOptionActive = 1
) t2 ON op.productOptionId = t2.productOptionId
LEFT JOIN tblOrder o ON o.orderId = op.orderId
LEFT JOIN tblUser u ON u.userRandomId = o.userRandomId
WHERE tr.reviewsStatusId = 2
ORDER BY tr.reviewRating DESC, tr.reviewDate DESC
OFFSET 0 ROWS FETCH NEXT 100 ROWS ONLY
Related
I have the following SQL Server code to get information from a combination of 4 tables.
I would like to modify it to only retrieve the latest 3 orders (pmpOrderDate) by supplier (pmpSupplierOrganizationID).
SELECT
PO.pmpPurchaseOrderID, PO.pmpOrderDate, PO.pmpSupplierOrganizationID, O.cmoName
FROM
PurchaseOrders PO
INNER JOIN
PurchaseOrderLines POL ON PO.pmpPurchaseOrderID = POL.pmlPurchaseOrderID
INNER JOIN
Organizations O ON PO.pmpSupplierOrganizationID = O.cmoOrganizationID
INNER JOIN
Parts P ON POL.pmlPartID = P.impPartID
WHERE
P.impPartClassID LIKE 'PUMP%'
Can you please help?
EDIT:
I wasn't fully clear on my actual requirements. To clarify further, what I need in the end is to display the latest 3 unique Purchase Orders by Supplier ID based on at least one of the PartClassID for the PartID in the PurchaseOrderLines to have criteria of beginning with string 'PUMP'
Use a ROW_NUMBER to partition by pmpSupplierOrganizationID and order by pmpOrderDate.
with cteTopOrders AS (
SELECT PO.pmpPurchaseOrderID, PO.pmpOrderDate, PO.pmpSupplierOrganizationID, O.cmoName,
ROW_NUMBER() OVER(PARTITION BY pmpSupplierOrganizationID ORDER BY pmpOrderDate DESC) AS RowNum
FROM PurchaseOrders PO
Inner Join PurchaseOrderLines POL ON PO.pmpPurchaseOrderID = POL.pmlPurchaseOrderID
Inner Join Organizations O On PO.pmpSupplierOrganizationID = O.cmoOrganizationID
Inner Join Parts P ON POL.pmlPartID = P.impPartID
WHERE P.impPartClassID Like 'PUMP%'
)
SELECT pmpPurchaseOrderID, pmpOrderDate, pmpSupplierOrganizationID, cmoName
FROM cteTopOrders
WHERE RowNum <= 3;
I'm a fan of lateral joins for this . . . cross apply:
select p.*, O.cmoName
from Organizations O cross apply
(select top (3) PO.pmpPurchaseOrderID, PO.pmpOrderDate, PO.pmpSupplierOrganizationID
from PurchaseOrders PO join
PurchaseOrderLines POL
on PO.pmpPurchaseOrderID = POL.pmlPurchaseOrderID join
Parts P
on POL.pmlPartID = P.impPartID
where PO.pmpSupplierOrganizationID = O.cmoOrganizationID and
P.impPartClassID Like 'PUMP%'
order by PO.pmpOrderDate desc
) p
You need a nested row_number to get the three rows per supplier and another OLAP-function on top of it:
with OrderRowNum as
(
SELECT PO.pmpPurchaseOrderID, PO.pmpOrderDate, PO.pmpSupplierOrganizationID, O.cmoName, P.impPartClassID,
row_number()
over (partition by PO.pmpSupplierOrganizationID
order by pmpOrderDate desc) as rn
FROM PurchaseOrders PO
Inner Join PurchaseOrderLines POL ON PO.pmpPurchaseOrderID = POL.pmlPurchaseOrderID
Inner Join Organizations O On PO.pmpSupplierOrganizationID = O.cmoOrganizationID
Inner Join Parts P ON POL.pmlPartID = P.impPartID
)
, CheckPUMP as
(
select *,
-- check if at least one of the three rows contains PUMP
max(case when impPartClassID Like 'PUMP%' then 1 else 0 end)
over (partition by PO.pmpSupplierOrganizationID) as PUMPflag
from OrderRowNum
where rn <= 3 -- get the last three rows per supplier
)
select *
from CheckPUMP
where flag = 1
I'm struggling with left joining the earliest row in this left join.
The results are showing a 2011 date, but i know for a fact this particular row should be returning 2008.
SELECT TOP 1000
f.name as [Franchisee]
,p.paid_date as paid_date
FROM franchisees_franchisee f
OUTER APPLY (SELECT TOP 1 *
FROM era_project_invoice_payment p
WHERE f.franchiseeid = p.franchiseeid
and p.deleted = 0 and p.payment_confirmed = 1
ORDER BY p.eraprojectinvoicepaymentid ASC) p
where
f.deleted = 0
and f.name LIKE '%VKlinkosch%'
Below returns the correct, 2008 date.
SELECT TOP 1000
f.name as [Franchisee]
,min(p.paid_date) as paid_date
from [era_uat_shared].[dbo].[franchisees_franchisee] f
left join era_project_invoice_payment p
on f.franchiseeid = p.franchiseeid
where f.deleted = 0
and f.name LIKE '%VKlinkosch%'
GROUP BY f.name
Problem is, I need more than just the Paid Date from the payments table! :(
SELECT
f.name as [Franchisee]
, p.*
FROM franchisees_franchisee f
INNER JOIN
(
SELECT
ROW_NUMBER() OVER (PARTITION BY franchiseeid ORDER BY paid_date ASC) rn
, p.*
FROM
era_project_invoice_payment p
WHERE
deleted = 0
AND payment_confirmed = 1
) p
ON
f.franchiseeid = p.franchiseeid
AND f.deleted = 0
AND f.name LIKE '%VKlinkosch%'
AND p.rn = 1
I have created this code to pull tickets from our database and also pull the last ticket note date and last ticket user. It works except for if two people (or more) created a note on the last date (no matter the time difference) it will create multiple lines for each ticket. How do I fix this? here is the code:
Select distinct t.ticketID,
t.OpenDate,
c.categoryname,
s.statusname,
p.priorityname,
u.firstname,
u.lastname,
tu.firstname as 'tech_firstname',
tu.lastname as 'tech_lastname',
ltn.maxdate as 'last date',
ltu.firstname + ' ' + ltu.lastname as 'Last User'
from ticket t
left join category c on t.categoryid = c.categoryid
left join [status] s on t.statusid = s.statusid
left join [priority] p on t.priorityid = p.priorityid
left join [user] u on t.userid = u.userid
left join [user] tu on t.technicianid = tu.userid
left join ticketnote tn on t.ticketid = tn.ticketid
inner join (
Select Max(TicketNoteDate) as MaxDate, max(cast(ticketnotedate as time)) as MaxTime, ticketid, userid
From ticketNote
group by ticketid, userid) ltn on tn.ticketid = ltn.ticketid and tn.ticketnotedate = ltn.maxdate and cast(tn.ticketnotedate as time) = ltn.maxtime
left join [user] ltu on ltn.userid = ltu.userid
where t.statusid = 1
and t.LocationID = 1
order by t.ticketid
As suggested in comments, use row_number in your subquery where you get the maximum value
SELECT row_number() over
(partition by tiketid, userid
order by TicketNoteDate desc ) as rn,
ticketid,
userid,
ticketnotedate
and then join with outer query with condition being rn=1
I rewrote it getting the max date of all tickets, then finding all tickets with that max date and ordering by time descending and getting the first row and then retrieving extra data (like user) for that entry. I had no way of testing this, but here goes...
Select distinct t.ticketID,
t.OpenDate,
c.categoryname,
s.statusname,
p.priorityname,
u.firstname,
u.lastname,
tu.firstname as 'tech_firstname',
tu.lastname as 'tech_lastname',
ltn.maxdate as 'last date',
ltu.firstname + ' ' + ltu.lastname as 'Last User'
from ticket t
left join category c on t.categoryid = c.categoryid
left join [status] s on t.statusid = s.statusid
left join [priority] p on t.priorityid = p.priorityid
left join [user] u on t.userid = u.userid
left join [user] tu on t.technicianid = tu.userid
inner join (
select maxdate, maxtime, ticketid, userid, rownum from (
select maxdate, maxtime, ticketid, userid, row_number() over (order by maxdate desc, maxtime desc) as rownum
from ticketnote tn,
(Select Max(TicketNoteDate) as MaxDate
From ticketNote) mx
where mx.maxdate = tn.ticketnotedate
) x where x.rownum = 1
) ltn
left join [user] ltu on ltn.userid = ltu.userid
where t.statusid = 1
and t.LocationID = 1
order by t.ticketid
SELECT P.*,R.PlaceCategory,Pr.RatingAverage,
ROW_NUMBER() OVER (PARTITION BY R.PlaceCategoryId ORDER BY Pr.RatingAverage DESC) AS RatingRank
FROM dbo.Places AS P
INNER JOIN
dbo.Cities AS C
ON P.CityId = C.CityId
LEFT JOIN
(SELECT SUM(Rat.Rating) / Count(Rat.Rating) AS RatingAverage,
Rat.PlaceId
FROM PlaceReview AS Rat
GROUP BY PlaceId) AS Pr
ON Pr.PlaceId = P.PlaceId
INNER JOIN
(SELECT TOP 2 PlaceCategoryId,PlaceCategory
FROM dbo.PlaceCategory
WHERE [Status] = 1
ORDER BY DisplayOrder) AS R
ON R.PlaceCategoryId = P.PlaceCategoryId
WHERE (P.CityId = #cityId OR C.City LIKE '%'+#cityName+'%')
AND
(P.[Status]=1 AND P.IsVerified = 1);
I want to add WHERE RatingRank<5. Is it possible without making this a subQuery? Sorry for the direct question.
Add the condition in an outer block since it can't be specified in the same query.
SELECT * FROM
(
SELECT P.*,R.PlaceCategory,Pr.RatingAverage,
ROW_NUMBER() OVER (PARTITION BY R.PlaceCategoryId ORDER BY Pr.RatingAverage DESC) AS RatingRank
FROM dbo.Places AS P
INNER JOIN
dbo.Cities AS C
ON P.CityId = C.CityId
LEFT JOIN
(SELECT SUM(Rat.Rating) / Count(Rat.Rating) AS RatingAverage,
Rat.PlaceId
FROM PlaceReview AS Rat
GROUP BY PlaceId) AS Pr
ON Pr.PlaceId = P.PlaceId
INNER JOIN
(SELECT TOP 2 PlaceCategoryId,PlaceCategory
FROM dbo.PlaceCategory
WHERE [Status] = 1
ORDER BY DisplayOrder) AS R
ON R.PlaceCategoryId = P.PlaceCategoryId
WHERE (P.CityId = #cityId OR C.City LIKE '%'+#cityName+'%')
AND
(P.[Status]=1 AND P.IsVerified = 1)
)x WHERE RatingRank<5;
What I'm trying to do is this:
SELECT *
FROM MainTable m
INNER JOIN JoinedTable j on j.ForeignID = m.ID
INNER JOIN (SELECT TOP 1 *
FROM SubQueryTable sq
WHERE sq.ForeignID = j.ID
ORDER BY VersionColumn DESC)
So basically, from SubQueryTable, I only want to retrieve a single row which has the maximum value for VersionColumn for all rows with a certain ID that I can get from JoinedTable.
T-SQL doesn't let me do this, what's a good way to solve this problem?
What I'm trying to prevent is loading the entire SubQueryTable and doing the filtering when it's too late as in....
SELECT *
FROM MainTable m
INNER JOIN JoinedTable j on j.ForeignID = m.ID
INNER JOIN (SELECT TOP 1 *
FROM SubQueryTable sq
ORDER BY VersionColumn DESC) sj ON sj.ForeignID = j.ID
I fear this second version performs the very slow subquery first and only filters it when it has loaded all the rows, but I want to filter sooner.
Any thoughts?
This will perform well if you have index on VersionColumn
SELECT *
FROM MainTable m
INNER JOIN JoinedTable j on j.ForeignID = m.ID
CROSS APPLY (SELECT TOP 1 *
FROM SubQueryTable sq
WHERE sq.ForeignID = j.ID
ORDER BY VersionColumn DESC) sj
Answer :
Hi,
Below query I have created as per your requirement using Country, State and City tables.
SELECT * FROM (
SELECT m.countryName, j.StateName,c.CityName , ROW_NUMBER() OVER(PARTITION BY c.stateid ORDER BY c.cityid desc) AS 'x'
FROM CountryMaster m
INNER JOIN StateMaster j on j.CountryID = m.CountryID
INNER JOIN dbo.CityMaster c ON j.StateID = c.StateID
) AS numbered WHERE x = 1
Below is your solution and above is only for your reference.
SELECT * FROM (
SELECT m.MainTablecolumnNm, j.JoinedTablecolumnNm,c.SubQueryTableColumnName , ROW_NUMBER()
OVER(PARTITION BY sj.ForeignID ORDER BY c.sjID desc) AS 'abc'
FROM MainTable m
INNER JOIN JoinedTable j on j.ForeignID = m.ID
INNER JOIN SubQueryTable sj ON sj.ForeignID = j.ID
) AS numbered WHERE abc = 1
Thank you,
Vishal Patel