Sql Query : Next Last Rows - sql

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

Related

How to get biggest value from 2 or more fields in a subquery

I have a table with customers that I join with a fact table with sales, based on invoices.
What I need from my report is to get in first part the biggest value of sales based on the incoming order type (1,2,3,C,D) for a customer for last year. And in the second part to get the same but for current year. What I get as result from my current query is all incoming order types with the customer revenue made for each of them. I tried with outer apply as subquery to get only the top 1 value ordered by revenue descending, but in the result I get the same - For all order types the customer revenue. Please help! I hope my explanation isn't understood only by me (happens a lot..)
use dwh01;
WITH OrderTypeUsedLY AS
(
SELECT
c.CustomerKey
,c.BranchId
,c.CustomerId
,c.CustomerName
,ISNULL(SUM(y.[Sale_Revenue]), 0) as [Sale_Revenue_LY]
,ISNULL(SUM(y.[Sale_GrossMarginTotal]), 0) as [Sale_GrossMarginTotal_LY]
,y.IncomingOrderTypeId as IncomingOrderType_LY
FROM live.DimCustomer c
left join (SELECT s.CustomerKey,iot.IncomingOrderTypeid, s.[Sale_Revenue], s.[Sale_GrossMarginTotal] FROM [dwh01].[live].[FactSales] s
inner join live.DimDate d
on d.DateId = s.PostingDateKey
inner join live.DimIncomingOrderType iot on iot.IncomingOrderTypeKey = s.IncomingOrderTypeKey
where s.ReportCurrencyId = 'LC'
and D.Year = YEAR(GETDATE())-1 --- Last Year
) y on c.CustomerKey = y.CustomerKey
where c.CustomerKey = '157053'
group by c.CustomerKey, c.CustomerId, c.CustomerName, c.BranchId, y.IncomingOrderTypeId
),
--*********************************************************************************************************************************--
OrderTypeCY as(
SELECT
c.CustomerKey
,c.BranchId
,c.SalesRepKey
,c.CustomerId
,c.CustomerName
,ISNULL(SUM(y.[Sale_Revenue]), 0) as [Sale_Revenue_CY]
,ISNULL(SUM(y.[Sale_GrossMarginTotal]), 0) as [Sale_GrossMarginTotal_CY]
,y.IncomingOrderTypeId as IncomingOrderType_CY
FROM live.DimCustomer c
left join (SELECT s.CustomerKey,iot.IncomingOrderTypeid, s.[Sale_Revenue], s.[Sale_GrossMarginTotal] FROM [dwh01].[live].[FactSales] s
inner join live.DimDate d
on d.DateId = s.PostingDateKey
inner join live.DimIncomingOrderType iot on iot.IncomingOrderTypeKey = s.IncomingOrderTypeKey
where s.ReportCurrencyId = 'LC'
and D.Year = YEAR(GETDATE()) --- Current Year
) y on c.CustomerKey = y.CustomerKey
where c.CustomerKey = '157053'
group by c.CustomerKey, c.CustomerId, c.CustomerName, c.BranchId, y.IncomingOrderTypeId, c.SalesRepKey
)
--*********************************************************************************************************************************--
SELECT
otly.BranchId,
rep.SalesRepId,
rep.SalesRepName,
otly.CustomerId,
otly.CustomerName,
otly.Sale_Revenue_LY,
otly.Sale_GrossMarginTotal_LY,
IncomingOrderType_LY,
otcy.Sale_Revenue_CY,
otcy.Sale_GrossMarginTotal_CY,
IncomingOrderType_CY
from OrderTypeUsedLY otly
left join OrderTypeCY otcy
on otly.CustomerKey = otcy.CustomerKey
join live.DimCustomer cus on cus.CustomerKey = otcy.CustomerKey
join live.DimSalesRep rep on rep.SalesRepKey = otcy.SalesRepKey
order by otcy.Sale_Revenue_CY desc, otly.Sale_Revenue_LY desc
,rep.SalesRepId
And here is the outer apply I tried:
outer apply (
SELECT top 1
iot.IncomingOrderTypeId,
Sale_Revenue
FROM [dwh01].[live].DimIncomingOrderType iot
where iot.IncomingOrderTypeKey = y.IncomingOrderTypeKey
order by Sale_Revenue desc) x
In the first select ( with OrderTypeUsed_LY ) I get this:
And I get the same in the second select, but with the values for current year.
The purpose of the report is to see the difference in the incoming order type most used (most profit made with it) for a customer last year and to see if he continues to use it this year, or uses another incoming order type this year.
Again I'm sorry for the bad explanation, I'm trying my best (I understand myself very well)
Here is the expected result:
Expected Result
I marked in red the last year part and in green the current year part.
If you change the subquery to:
SELECT
iot.IncomingOrderTypeKey,
MAX(Sale_Revenue)
FROM [dwh01].[live].DimIncomingOrderType iot
GROUP BY iot.IncomingOrderTypeKey
then you can JOIN (not APPLY) directly on IncomingOrderTypeKey AND Sale_Revenue.
Try this:
USE dwh01;
DECLARE #CustomerKey varchar(6) = '157053'
, #ReportCurrencyId varchar(2) = 'LC'
, #CurrentYear int = YEAR(GETDATE())
, #TargetYear int = YEAR(GETDATE())-1
;
WITH FactsTable AS
(
SELECT
s.CustomerKey
, i.IncomingOrderTypeid
, [Sale_Revenue] = ISNULL(s.[Sale_Revenue] , 0)
, [Sale_GrossMarginTotal] = ISNULL(s.[Sale_GrossMarginTotal], 0)
, d.[Year]
FROM [dwh01].[live].[FactSales] s
inner join live.DimDate d on d.DateId = s.PostingDateKey
inner join live.DimIncomingOrderType i on i.IncomingOrderTypeKey = s.IncomingOrderTypeKey
where
s.CustomerKey = #CustomerKey
and s.ReportCurrencyId = #ReportCurrencyId
)
, OrderTypeTable
(
SELECT
c.CustomerKey
, c.BranchId
, c.CustomerId
, c.CustomerName
, c.SalesRepKey
, IncomingOrderType_LY = SUM(CASE WHEN y.[Year] = #TargetYear THEN y.IncomingOrderTypeId ELSE 0 END)
, [Sale_Revenue_LY] = SUM(CASE WHEN y.[Year] = #TargetYear THEN y.[Sale_Revenue] ELSE 0 END)
, [Sale_GrossMarginTotal_LY] = SUM(CASE WHEN y.[Year] = #TargetYear THEN y.[Sale_GrossMarginTotal] ELSE 0 END)
, IncomingOrderType_LY = SUM(CASE WHEN y.[Year] = #CurrentYear THEN y.IncomingOrderTypeId ELSE 0 END)
, [Sale_Revenue_CY] = SUM(CASE WHEN y.[Year] = #CurrentYear THEN y.[Sale_Revenue] ELSE 0 END)
, [Sale_GrossMarginTotal_CY] = SUM(CASE WHEN y.[Year] = #CurrentYear THEN y.[Sale_GrossMarginTotal] ELSE 0 END)
FROM live.DimCustomer c
left join FactsTable y on y.CustomerKey = c.CustomerKey
group by
c.CustomerKey
, c.BranchId
, c.CustomerId
, c.CustomerName
, y.IncomingOrderTypeId
, c.SalesRepKey
)
SELECT
O.BranchId
, R.SalesRepId
, R.SalesRepName
, O.CustomerId
, O.CustomerName
, O.Sale_Revenue_LY
, O.Sale_GrossMarginTotal_LY
, O.IncomingOrderType_LY
, O.Sale_Revenue_CY
, O..Sale_GrossMarginTotal_CY
, O.IncomingOrderType_CY
from OrderTypeTable O
join live.DimSalesRep R on R.SalesRepKey = O.SalesRepKey
order by
O.Sale_Revenue_CY desc
, O.Sale_Revenue_LY desc
, R.SalesRepId
The solution is with using row_number() function in the inner query of the first CTE:
(
select *, row_number() over (partition by x0.CustomerKey order by x0.Sale_Revenue desc) as rn
from
(
select fs.CustomerKey, iot.IncomingOrderTypeKey,
sum(fs.Sale_Revenue) as Sale_Revenue
FROM [dwh01].[live].DimIncomingOrderType iot
join live.FactSales fs
on iot.IncomingOrderTypeKey = fs.IncomingOrderTypeKey
join live.DimDate d
on d.DateId = fs.PostingDateKey
where d.[year] = #CurrentYear
GROUP BY fs.CustomerKey, iot.IncomingOrderTypeKey
) as x0
) as x1 on x1.CustomerKey = s.CustomerKey
The row_number() function gets only the first row from the result for each customer, and that is with the biggest sale revenue ( order by sale_revenue desc).

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

How can I optimize the SQL query?

I have a query an SQL query as follows, can anybody suggest any optimization for this; I think most of the effort is being done for the Union operation - is there anything else can be done to get the same result ?
Basically I wanna query first portion of the UNION and if for each record there is no result then the second portion need to be run. Please help.
:
SET dateformat dmy;
WITH incidentcategory
AS (
SELECT 1 ord, i.IncidentId, rl.Description Category FROM incident i
JOIN IncidentLikelihood l ON i.IncidentId = l.IncidentId
JOIN IncidentSeverity s ON i.IncidentId = s.IncidentId
JOIN LikelihoodSeverity ls ON l.LikelihoodId = ls.LikelihoodId AND s.SeverityId = ls.SeverityId
JOIN RiskLevel rl ON ls.RiskLevelId = rl.riskLevelId
UNION
SELECT 2 ord, i.incidentid,
rl.description Category
FROM incident i
JOIN incidentreportlikelihood l
ON i.incidentid = l.incidentid
JOIN incidentreportseverity s
ON i.incidentid = s.incidentid
JOIN likelihoodseverity ls
ON l.likelihoodid = ls.likelihoodid
AND s.severityid = ls.severityid
JOIN risklevel rl
ON ls.risklevelid = rl.risklevelid
) ,
ic AS (
SELECT ROW_NUMBER() OVER (PARTITION BY i.IncidentId ORDER BY (CASE WHEN incidentTime IS NULL THEN GETDATE() ELSE incidentTime END) DESC,ord ASC) rn,
i.incidentid,
dbo.Incidentdescription(i.incidentid, '',
'',
'', '')
IncidentDescription,
dbo.Dateconverttimezonecompanyid(closedtime,
i.companyid)
ClosedTime,
incidenttime,
incidentno,
Isnull(c.category, '')
Category,
opencorrectiveactions,
reportcompleted,
Isnull(classificationcompleted, 0)
ClassificationCompleted,
Cast (( CASE
WHEN closedtime IS NULL THEN 0
ELSE 1
END ) AS BIT)
IncidentClosed,
Cast (( CASE
WHEN investigatorfinishedtime IS NULL THEN 0
ELSE 1
END ) AS BIT)
InvestigationFinished,
Cast (( CASE
WHEN investigationcompletetime IS NULL THEN 0
ELSE 1
END ) AS BIT)
InvestigationComplete,
Cast (( CASE
WHEN investigatorassignedtime IS NULL THEN 0
ELSE 1
END ) AS BIT)
InvestigatorAssigned,
Cast (( CASE
WHEN (SELECT Count(*)
FROM incidentinvestigator
WHERE incidentid = i.incidentid
AND personid = 1588
AND tablename = 'AdminLevels') = 0
THEN 0
ELSE 1
END ) AS BIT)
IncidentInvestigator,
(SELECT dbo.Strconcat(osname)
FROM (SELECT TOP 10 osname
FROM incidentlocation l
JOIN organisationstructure o
ON l.locationid = o.osid
WHERE incidentid = i.incidentid
ORDER BY l.locorder) loc)
Location,
Isnull((SELECT TOP 1 teamleader
FROM incidentinvestigator
WHERE personid = 1588
AND tablename = 'AdminLevels'
AND incidentid = i.incidentid), 0)
TeamLeader,
incidentstatus,
incidentstatussearch
FROM incident i
LEFT OUTER JOIN incidentcategory c
ON i.incidentid = c.incidentid
WHERE i.isdeleted = 0
AND i.companyid = 158
AND incidentno <> 0
--AND reportcompleted = 1
--AND investigatorassignedtime IS NOT NULL
--AND investigatorfinishedtime IS NULL
--AND closedtime IS NULL
),
ic2 AS (
SELECT * FROM ic WHERE rn=1
)
SELECT * FROM ic2
--WHERE rownumber >= 0
-- AND rownumber < 0 + 10
--WHERE ic2.incidentid in(53327,53538)
--WHERE ic2.incidentid = 53338
ORDER BY incidentid DESC
Following is the execution plan I got:
https://www.dropbox.com/s/50dcpelr1ag4blp/Execution_Plan.sqlplan?dl=0
There are several issues:
1) use UNION ALL instead of UNION ALL to avoid the additional operation to aggregate the data.
2) try to modify the numerous function calls (e.g. dbo.Incidentdescription() ) to be an in-lie table valued function so you can reference it using CROSS APPLY or OUTER APPLY. Especially, if those functions referencing a table again.
3) move the subqueries from the SELECT part of the query to the FROM part using CROSS APPLY or OUTER APPLY again.
4) after the above is done, check the execution plan again for any missing indexes. Also, run the query with STATISTICS TIME, IO on to verify that the number of times a table
is referenced is correct (sometimes the execution plan put you in the wrong direction, especially if function calls are involved)...
Since the first inner query produces rows with ord=1 and the second produces rows with ord=2, you should use UNION ALL instead of UNION. UNION will filter out equal rows and since you will never get equal rows it is more efficient to use UNION ALL.
Also, rewrite your query to not use the WITH construct. I've had very bad experiences with this. Just use regular derived tables instead. In the case the query is still abnormally slow, try to serialize some derived tables to a temporary table and query the temporary table instead.
Try alternate approach by removing
(SELECT dbo.Strconcat(osname)
FROM (SELECT TOP 10 osname
FROM incidentlocation l
JOIN organisationstructure o
ON l.locationid = o.osid
WHERE incidentid = i.incidentid
ORDER BY l.locorder) loc)
Location,
Isnull((SELECT TOP 1 teamleader
FROM incidentinvestigator
WHERE personid = 1588
AND tablename = 'AdminLevels'
AND incidentid = i.incidentid), 0)
TeamLeader
from the SELECT. Avoid using complex functions/sub-queries in select.

How to properly join TOP 1 fields to SQL query

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

Using MAX for date but adding column to group on 'breaks' the query - sub query?

I Have a table which holds date but I need to know the latest date where a condition is true per location, only issue is once I add a column called 'notes' it breaks the query and returns too many rows, current query is ...
SELECT
Location,
MAX(date) AS date,
type,
notes
FROM NotesTable a
INNER JOIN Location b on a.LocationID = b.LocationID
INNER JOIN Type c on a.typeid = c.typeid
WHERE typeid <> 8
GROUP BY Location, type, notes
If I comment out the notes column then it works fine but as soon as I add that to the grouping it then returns more rows than required.
Have tried using a subquery but still cant get it working, subquery below
SELECT
r.location,
r.date,
r.type,
t.notes
FROM (SELECT Location, MAX(date), type
FROM NotesTable a INNER JOIN Location b on a.LocationID = b.LocationID
INNER JOIN Type c on a.typeid = c.typeid
WHERE typeid <> 8
GROUP BY location,type
) r
INNER JOIN NotesTable t ON t.date = r.date
Anyone got any other suggestions?
select * from
(
SELECT Location,Date, Type, Notes, Row_Number() Over (Partition By Location, Type order by date desc) RN
FROM
NotesTable a
INNER JOIN Location b on a.LocationID = b.LocationID
INNER JOIN Type c on a.typeid = c.typeid
WHERE typeid <> 8
) v
WHERE rn = 1
Your query is almost correct, you need to add this additional condition in ON clause
AND
t.location = r.location AND
t.type = r.type
full query,
SELECT r.location
, r.DATE
, r.type
, t.notes
FROM (
SELECT Location
, MAX(DATE) maxDate
, type
FROM NotesTable a
INNER JOIN Location b
ON a.LocationID = b.LocationID
INNER JOIN Type c
ON a.typeid = c.typeid
WHERE typeid <> 8
GROUP BY location
, type
) r
INNER JOIN NotesTable t
ON t.DATE = r.maxDate AND
t.location = r.location AND
t.type = r.type