How to properly join TOP 1 fields to SQL query - sql

How can I properly join the fields I have in the comment block to the SQL query? I have a billing phone number on the order header, but on the order lines, there is a shipping phone number for each line. The billing and shipping can be different.
On every order line of one order, it's 99% the same shipping number, but I want to do Top 1 and not group by just incase some data gets messed up.
I think UNION might get me what I want, but it seems like there is a better way to get everything in one query and not copying & pasting the same "where" clauses.
SELECT a.Order_no
,a.Customer_no
,a.BILL_LAST_NAME
,a.BILL_FIRST_NAME
,b.email
,a.BILL_ADDRESS1
,a.BILL_ADDRESS2
,a.BILL_CITY
,a.BILL_STATE
,a.BILL_POSTAL_CODE
,a.BILL_COUNTRY
,b.Address_Type
,a.BILL_PHONE
,a.BILL_PHONE_EXT
,a.Order_Date
,a.billing_status
,a.PO_Number
,a.Customer_comments
,a.ShipMethodShipperDesc
,a.ShipRate
,a.CouponDiscountCode
,a.CouponDiscount
,a.CustomerDiscount
,a.CustomerDiscountPercent
,a.SalesTaxTotal
,a.Payment_Method
,a.Credit_Card_Type
,a.Credit_Card_Number
,a.Order_Date
,a.BILL_TYPE
,a.Order_Net
/* I added these lines but would like them joined properly */
/*-------->*/
, (select top 1 SHIP_ADDRESS1 from LineItems C where c.ORDER_NO = a.ORDER_NO)
, (select top 1 SHIP_ADDRESS2 from LineItems C where c.ORDER_NO = a.ORDER_NO)
, (select top 1 SHIP_CITY from LineItems C where c.ORDER_NO = a.ORDER_NO)
, (select top 1 SHIP_STATE from LineItems C where c.ORDER_NO = a.ORDER_NO)
, (select top 1 SHIP_POSTAL_CODE from LineItems C where c.ORDER_NO = a.ORDER_NO)
, (select top 1 SHIP_COUNTRY from LineItems C where c.ORDER_NO = a.ORDER_NO)
/*<-----------*/
FROM Orders AS a
,Customers AS b
WHERE a.customer_no = b.customer_no
AND a.AccountName = 'mywebaccount'
AND a.billing_status <> 'Canceled'
AND a.transferred = 0
AND a.order_status <> 'Canceled'
AND EXISTS (
SELECT *
FROM LineItems c
WHERE c.order_no = a.order_no
)
ORDER BY a.order_date
,a.order_no

Firstly, don't do FROM sometable1 AS t1, sometable2 AS t2. Always join explicitly. Secondly, from the way your case looks, some variant of APPLY would suit nicely.
Here's my version:
SELECT a.Order_no
,a.Customer_no
,a.BILL_LAST_NAME
,a.BILL_FIRST_NAME
,b.email
,a.BILL_ADDRESS1
,a.BILL_ADDRESS2
,a.BILL_CITY
,a.BILL_STATE
,a.BILL_POSTAL_CODE
,a.BILL_COUNTRY
,b.Address_Type
,a.BILL_PHONE
,a.BILL_PHONE_EXT
,a.Order_Date
,a.billing_status
,a.PO_Number
,a.Customer_comments
,a.ShipMethodShipperDesc
,a.ShipRate
,a.CouponDiscountCode
,a.CouponDiscount
,a.CustomerDiscount
,a.CustomerDiscountPercent
,a.SalesTaxTotal
,a.Payment_Method
,a.Credit_Card_Type
,a.Credit_Card_Number
,a.Order_Date
,a.BILL_TYPE
,a.Order_Net
,li.SHIP_ADDRESS1
,li.SHIP_ADDRESS2
,li.SHIP_CITY
,li.SHIP_STATE
,li.SHIP_POSTAL_CODE
,li.SHIP_COUNTRY
FROM Orders AS a
INNER JOIN Customers AS b
ON a.customer_no = b.customer_no
CROSS APPLY
(
SELECT TOP 1 c.SHIP_ADDRESS1
,c.SHIP_ADDRESS2
,c.SHIP_CITY
,c.SHIP_STATE
,c.SHIP_POSTAL_CODE
,c.SHIP_COUNTRY
FROM LineItems c
WHERE c.ORDER_NO = a.ORDER_NO
ORDER BY c.Id -- or whatever
) AS li
WHERE a.AccountName = 'mywebaccount'
AND a.billing_status <> 'Canceled'
AND a.transferred = 0
AND a.order_status <> 'Canceled'
-- no need for that exists since CROSS APPLY works like INNER JOIN
ORDER BY a.order_date,a.order_no

I suggest something like:
SELECT Order_no
,Customer_no
,BILL_LAST_NAME
,BILL_FIRST_NAME
...
,SHIP_ADDRESS1
,SHIP_ADDRESS2
,SHIP_CITY
,SHIP_STATE
,SHIP_POSTAL_CODE
,SHIP_COUNTRY
FROM (
SELECT a.Order_no
,a.Customer_no
,a.BILL_LAST_NAME
,a.BILL_FIRST_NAME
,b.email
,a.BILL_ADDRESS1
,a.BILL_ADDRESS2
,a.BILL_CITY
,a.BILL_STATE
,a.BILL_POSTAL_CODE
,a.BILL_COUNTRY
,b.Address_Type
,a.BILL_PHONE
,a.BILL_PHONE_EXT
,a.Order_Date
,a.billing_status
,a.PO_Number
,a.Customer_comments
,a.ShipMethodShipperDesc
,a.ShipRate
,a.CouponDiscountCode
,a.CouponDiscount
,a.CustomerDiscount
,a.CustomerDiscountPercent
,a.SalesTaxTotal
,a.Payment_Method
,a.Credit_Card_Type
,a.Credit_Card_Number
,a.Order_Date
,a.BILL_TYPE
,a.Order_Net
,c.SHIP_ADDRESS1
,c.SHIP_ADDRESS2
,c.SHIP_CITY
,c.SHIP_STATE
,c.SHIP_POSTAL_CODE
,c.SHIP_COUNTRY
, row_number() over (partition by a.order_no
order by SHIP_ADDRESS1, SHIP_ADDRESS2, SHIP_CITY, SHIP_STATE
, SHIP_POSTAL_CODE, SHIP_COUNTRY) as rn
FROM Orders AS a
JOIN Customers AS b
ON a.customer_no = b.customer_no
JOIN numberLineItems c
ON c.order_no = a.order_no
WHERE a.AccountName = 'mywebaccount'
AND a.billing_status <> 'Canceled'
AND a.transferred = 0
AND a.order_status <> 'Canceled'
) as x
WHERE rn = 1
ORDER BY order_date
, order_no;

I created a simple example and posted
HERE on SQL Fiddle
The simplest way is just to join with the top 1 from a subquery
select cus.*, ord.*, sub.*
from Customer cus
join [Order] ord on ord.CustomerId = cus.Id
join (
select it.OrderId, it.ItemName, it.ItemAddress
from Items it
where it.Id in (select MAX(id) from Items group by OrderId)
) as sub on sub.OrderId = ord.Id
If you want to see the first item instead the last you can replace MAX(id) with MIN(id)
Note the only "problem" is if there are no items for the order it ill not show.
But if you got orders without a item and still want to show it you can use a left join
Se the fiddle for a running example.
I hope you don't mind I used explicit joins =)

Related

Getting Latest 3 orders by Supplier ID

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

How I can select highest review from a user?

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

Sql Query : Next Last Rows

I have tables Customer and Calls like this:
Customers table:
CustomerId : uniqueidentifier
BusinessName : nvarchar(MAX)
Calls table:
CallId : uniqueidentifier
CallDate :datetime
CustomerId: uniqueidentifier
Status: int (call has been answered or no)
I want to get all the customers with last called and next call columns.
Last Called is the last call where status = 1. Next Call is the call where status = 0 and the Calldate > now date. Null if they do not have any. This is a non working example just to show the format
Select *
From Customers,
Calls.CallId as LastCalledId
Calls.CallDate as LastCalledDate
Calls.CallId as NextCallId
Calls.CallDate as NextCallDate
LEFT JOIN
Calls ON Customers.CustomerId = Calls.CustomerId
How can I do this?
Is this what you mean?
Update
select c.id
, lastcall.calldate
, lastcall.id
, firstcall.calldate
, firstcall.id
from #customers c
outer apply (select top 1 calls.calldate, calls.id from #calls calls where calls.custid = c.id and calldate < getdate() order by calldate desc) lastcall(calldate, id)
outer apply (select top 1 calls.calldate, calls.id from #calls calls where calls.custid = c.id and calldate >= getdate() order by calldate asc) firstcall(calldate, id)
original answer:
select c.id
, answered.calldate LastCalledDate
, answered.id LastCalledID
, unaswered.calldate NextCalledDate
, unaswered.id NextCalledID
from #customers c
left join #calls answered ON answered.custid = c.id AND status = 1
left join #calls unaswered ON unaswered.custid = c.id AND unaswered.status = 0 and unaswered.calldate > getdate()
You can use WHERE clause in LEFT JOIN for getting required data rows.Here you can use where clause with Calls.status = 0 or 1 (as per your requirement)
Since you didn't provide DDL and DML for sample data I couldn't test my answer, so It's not guaranteed to produce the desired results.
Having said that, here is my attempt that should work on versions 2008 or higher.
Using a cte to calculate the last answered call for each customer (assuming calldate is unique) and a couple of left joins I came up with this:
;WITH LastCall AS
(
SELECT CustomerId,
CallId,
CallDate
FROM Calls t0
WHERE [Status] = 1
AND CallDate =
(
SELECT MAX(CallDate)
FROM Calls t1
WHERE [Status] = 1
AND t1.CustomerId = t0.CustomerId
)
)
SELECT cu.CustomerId,
cu.Name,
lc.CallId as LastCalledId,
lc.CallDate as LastCalledDate,
nc.CallId as NextCallId,
nc.CallDate as NextCallDate
FROM Customers as cu
LEFT JOIN LastCall lc ON cu.CustomerId = lc.CustomerId
LEFT JOIN Calls nc ON cu.CustomerId = nc.CustomerId AND nc[Status] = 0 AND nc.CallDate > GETDATE()
Please note that for versions 2012 and higher you might be able to use FIRST_VALUE or LAST_VALUE built in functions.
You can use AND in join query. Then order calls and get last one with limit.
SELECT *
FROM Customers,
Calls.CallId as LastCalledId
Calls.CallDate as LastCalledDate
Calls.CallId as NextCallId
LEFT JOIN
Calls ON (Customers.CustomerId = Calls.CustomerId AND Calls.Status = 1)
ORDER BY Calls.CallDate DESC
UNION
SELECT *
FROM Customers,
Calls.CallId as LastCalledId
Calls.CallId as NextCallId
Calls.CallDate as NextCallDate
LEFT JOIN
Calls ON (Customers.CustomerId = Calls.CustomerId AND Calls.Status = 0 AND CallDate > NOW())
ORDER BY Calls.CallDate DESC;
You can do this using different ways :
Select cu.CustomerId,LastCall.Callid,LastCall.calldate,NextCall.Callid,NextCall.calldate from Customers cu,
(select max(callid) Callid,max(calldate) calldate,CustomerId from Calls c where status = 1 group by CustomerId) LastCall,
(select max(callid) Callid,max(calldate) calldate,CustomerId from Calls c where status = 0 and calldate > sysdate group by CustomerId) NextCall
where cu.CustomerId = LastCall.CustomerId and cu.CustomerId = NextCall.CustomerId
order by 1
Or by replacing in your query :
Select (select max(callid) from Calls c where c.CustomerId = Cu.CustomerId and status = 1) as LastCalledId,
(select max(CallDate) from Calls c where c.callid = (select max(callid) from Calls c where c.CustomerId = Cu.CustomerId and status = 1)) as LastCalledDate,
(select max(callid) from Calls c where c.CustomerId = Cu.CustomerId and status = 0 and CallDate > sysdate ) as NextCallId,
(select max(CallDate) from Calls c where c.callid = (select max(callid) from Calls c where c.CustomerId = Cu.CustomerId and status = 0 and CallDate > sysdate )) as NextCallDate
From Customers Cu,Calls c ON Cu.CustomerId = c.CustomerId

Struggling with left join

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

How can i eliminate dups?

select C.Id as candidateId,C.Name, C.Phone, Status.ResultStatusText , Status.TimeStamp, Status.notes ,
(Select count(*) from CandidateCallHistory where CandiateId = candidateId) AS numbCalls,
(SELECT SUBSTRING((SELECT ',' + Name
FROM Jobs
WHERE Id in (select value from fn_Split(c.JobIds,','))
FOR XML PATH('')),2,200000)) AS jobsList
from Candidate2 C
outer APPLY (select top 1 CH.CandiateId, CH.ResultStatusText , CH.TimeStamp , CH.notes
from CandidateCallHistory CH
where CH.CandiateId = C.Id
order by TimeStamp desc) as Status
where Status.ResultStatusText <> 'completed' and Status.ResultStatusText <> 'canceled' and c.isactive = 1
I have multiple records in the CandidateCallHistory table and seems this is causing issue with the outer apply ( i may be wrong) as it should only get the most recent record in the table since it selects top 1.
Try to add distinct in first line after select:
select distinct [...]