Order BY on column generated by select statement - sql

i'm running this query:
SELECT
claims.rID
,claims.client
,clients.cName
,cur.currency
,carriers.scac
,carriers.cName
,claims.client+claims.counter
,claims.dateOn
,(SELECT top 1 errorCode FROM entries WHERE entries.rID=claims.rID) as errorCode
,(SELECT SUM(refundDue) FROM entries WHERE entries.rID=claims.rID) as amount
,auditors.initials
FROM claims
INNER JOIN clients ON clients.code = claims.client
INNER JOIN currency cur ON claims.currency = cur.currencyID
INNER JOIN entries ON claims.rID = entries.rid
INNER JOIN carriers ON carriers.carrierID = claims.carrierID
INNER JOIN auditors ON claims.auditorID=auditors.auditorID
GROUP BY
claims.rID
,claims.client
,claims.counter
,claims.dateOn
,carriers.scac
,carriers.cName
,clients.cName
,cur.currency
,auditors.initials
,errorCode
ORDER BY errorCode ASC
The focus should be on the orderBy errorCode. For some reason, it's not actually ordering Alphabetically by errorCode. Any ideas as to why?

Most databases don't allow you to use aliases created in the SELECT-list in the GROUP BY, ORDER BY and WHERE clauses. Instead, you have to repeat the expression used in the SELECT-list:
SELECT
claims.rID,
...,
(SELECT top 1 errorCode FROM entries WHERE entries.rID=claims.rID) AS errorCode,
...
FROM
claims INNER JOIN ...
GROUP BY
claims.rID,
...,
(SELECT top 1 errorCode FROM entries WHERE entries.rID=claims.rID)
ORDER BY
(SELECT top 1 errorCode FROM entries WHERE entries.rID=claims.rID) ASC
But I think that is better to use a sub-select as data source in the FROM-clause:
SELECT
claims.rID,
...,
E.errCode, E.amount,
...
FROM claims
INNER JOIN clients ON clients.code = claims.client
INNER JOIN currency cur ON claims.currency = cur.currencyID
INNER JOIN (SELECT rID, MIN(errorCode) AS errCode, SUM(refundDue) AS amount
FROM entries
GROUP BY rID) AS E
ON claims.rID = E.rid
INNER JOIN carriers ON carriers.carrierID = claims.carrierID
INNER JOIN auditors ON claims.auditorID=auditors.auditorID
GROUP BY
claims.rID,
...,
E.errCode
ORDER BY
E.errCode ASC
Note that I included the SUM(refundDue) AS amount as well. If the error codes can be NULL within entries, use MAX(errorCode) instead of MIN(errorCode).

Related

Why is SQL not letting me access information in inner queries?

I'm writing a query to find solve the following question:
"For those customer – product combinations where the product belongs to one of the product lines that have ‘Ethernet’ in their name, list the name of the customer, name of the product, the sales last year and total sales year to date."
Now, I have four tables that I need to use to solve this: xproduct, xprodline, and xsales, and xcustomer. These are related in the following ways:
And they have the following columns:
Since xcustomer and xproduct are not directly related, I'm using xsales to join them, but I'm having issues accessing the information I get from inner queries. This is the code I have so far, but it throws "ORA-00904: "S"."SALES_YEAR_TO_DATE": invalid identifier":
SELECT xcustomer.cust_name, PL.prod_name, S.sales_last_year, S.sales_year_to_date FROM xcustomer
JOIN(
SELECT xsales.sales_cust_nbr FROM xsales
JOIN (
SELECT xproduct.prod_name, xprodline.prodline_pyear_sales, xprodline.prodline_ytd_sales, xproduct.prod_nbr FROM xproduct
INNER JOIN xprodline
ON xproduct.prod_prodline = xprodline.prodline_nbr
WHERE prod_prodline= 1
) PL
ON xsales.sales_prod_nbr = PL.prod_nbr
)S
ON S.sales_cust_nbr = xcustomer.cust_nbr;
Try this:
SELECT xcustomer.cust_name, PL.prod_name, S.sales_last_year, S.sales_year_to_date FROM xcustomer
JOIN(
SELECT xsales.sales_cust_nbr, xsales.sales_last_year, xsales.sales_year_to_date FROM xsales
JOIN (
SELECT xproduct.prod_name, xprodline.prodline_pyear_sales, xprodline.prodline_ytd_sales, xproduct.prod_nbr FROM xproduct
INNER JOIN xprodline
ON xproduct.prod_prodline = xprodline.prodline_nbr
WHERE prod_prodline= 1
) PL
ON xsales.sales_prod_nbr = PL.prod_nbr
)S
ON S.sales_cust_nbr = xcustomer.cust_nbr;
You were missing selecting the columns you needed.
You are missing one more column (PROD_NAME) in your subquery. Here is the updated query:
SELECT xcustomer.cust_name,
S.prod_name,
S.sales_last_year,
S.sales_year_to_date
FROM xcustomer
JOIN(
SELECT xsales.sales_cust_nbr, xsales.sales_last_year, xsales.sales_year_to_date, pl.prod_name
FROM xsales
JOIN (
SELECT xproduct.prod_name, xprodline.prodline_pyear_sales, xprodline.prodline_ytd_sales, xproduct.prod_nbr
FROM xproduct
INNER JOIN xprodline
ON xproduct.prod_prodline = xprodline.prodline_nbr
WHERE prod_prodline= 1
) PL
ON xsales.sales_prod_nbr = PL.prod_nbr
)S
ON S.sales_cust_nbr = xcustomer.cust_nbr;
Also, I think you can directly join the tables and simplify your query as follows:
SELECT C.cust_name,
PL.prod_name,
S.sales_last_year,
S.sales_year_to_date
FROM xcustomer C
JOIN xsales S
ON C.cust_nbr = S.sales_cust_nbr
JOIN xprodline PL
ON S.sales_prod_nbr = PL.prod_nbr
JOIN xproduct P
ON PL.prodline_nbr = P.prod_prodline
WHERE P.prod_prodline= 1

SELECT statement where rows are omitted based on another table

Table with orders has another table with positions. I want the orders table to show but then only have the most up to-date position on it. Below is a picture of the 3 rows I want showing. Omit the rest.
SELECT DispatchTable.ordernumber, DispatchTable.truck,
DispatchTable.driver, DispatchTable.actualpickup,
DispatchTable.actualdropoff, orders.pickupdateandtime,
orders.dropoffdateandtime, Truck002.lastposition,
Truck002.lastdateandtime
FROM DispatchTable
INNER JOIN orders ON DispatchTable.ordernumber = orders.id
INNER JOIN Truck002 ON DispatchTable.truck = Truck002.name
WHERE (orders.status = 'onRoute')
Assuming that you want the row having the latest lastdateandtime for the truck name, this should work:
SELECT DispatchTable.ordernumber,
DispatchTable.truck,
DispatchTable.driver,
DispatchTable.actualpickup,
DispatchTable.actualdropoff,
orders.pickupdateandtime,
orders.dropoffdateandtime,
TruckLatest.lastposition,
TruckLatest.lastdateandtime
FROM DispatchTable
INNER JOIN orders ON DispatchTable.ordernumber = orders.id
INNER JOIN (SELECT name,
lastposition,
lastdateandtime
FROM Truck002 Truck1
WHERE lastdateandtime =
(SELECT MAX(lastdateandtime)
FROM Truck002 Truck2
WHERE Truck2.name = Truck1.name)) TruckLatest
ON DispatchTable.truck = TruckLatest.name
WHERE (orders.status = 'onRoute')
If I understand correctly, you can get the most recent record for a truck using ROW_NUMBER():
SELECT dt.ordernumber, dt.truck,
dt.driver, dt.actualpickup,
dt.actualdropoff, o.pickupdateandtime,
o.dropoffdateandtime, t.lastposition,
t.lastdateandtime
FROM DispatchTable dt INNER JOIN
orders o
ON dt.ordernumber = o.id INNER JOIN
(SELECT t.*,
ROW_NUMBER() OVER (PARTITION BY t.name ORDER BY t.lastdateandtime DESC) as seqnum
FROM Truck002 t
) t
ON dt.truck = t.name
WHERE o.status = 'onRoute' AND seqnum = 1;
Firstly, why are you using Truck002's name field rather than its id field as the link to DispacthTable? This is considered a less efficient way of doing it than using id (which is either a numerical field or a shorter string than name).
Secondly, you should mention in your Question that each Order can have many DispatchTable's and that each DispacthTable can have many Truck002's, otherwise many people will start by assuming that it is the other way round between DispatchTable and Truck002.
Thirdly, please try...
SELECT DispatchTable.ordernumber,
DispatchTable.truck,
DispatchTable.driver,
DispatchTable.actualpickup,
DispatchTable.actualdropoff,
orders.pickupdateandtime,
orders.dropoffdateandtime,
Truck002.lastposition,
Truck002.lastdateandtime
FROM DispatchTable
INNER JOIN orders ON DispatchTable.ordernumber = orders.id
INNER JOIN Truck002 ON DispatchTable.truck = Truck002.name
WHERE (orders.status = 'onRoute')
GROUP BY ordernumber
HAVING lastdateandtime = MAX( lastdateandtime )
If you have any questions or comments, then please feel free to post a Comment accordingly.
Further Reading
https://msdn.microsoft.com/en-us/library/bb177906(v=office.12).aspx (on HAVING)
https://www.w3schools.com/sql/sql_having.asp (on HAVING)
https://msdn.microsoft.com/en-us/library/bb177905(v=office.12).aspx (on GROUP BY)
https://www.w3schools.com/sql/sql_groupby.asp (on GROUP BY)

SQL Join only if all records have a match

I have 3 tables:
CP_carthead (idOrder)
CP_cartrows (idOrder, idCartRow)
CP_shipping (idCartRow, idShipping, dateShipped)
There can be multiple idCartRows per idOrder.
I want to get all orders where all its idCartRows exist in CP_shipping. This seems like it should be simple, but I haven't found much on the web.
Here's my query now:
SELECT
s.idOrder
, s.LatestDateShipped
FROM
CP_carthead o
LEFT OUTER JOIN (
SELECT
MAX(s.dateShipped) [LatestDateShipped]
, r.idOrder
FROM
CP_shipping s
LEFT OUTER JOIN CP_cartrows r ON s.idCartRow = r.idCartRow
GROUP BY
r.idOrder
) s ON o.idOrder = s.idOrder
Your query is returning rows from "s" and not the orders. Based on your question, I came up with this query:
select o.*
from CP_Carthead o
where o.orderId in (select cr.idOrder
from cp_cartrows cr left outer join
cp_shipping s
on cr.idCartRow = s.IdCartrow
group by cr.idOrder
having count(s.idCartRow) = COUNT(*)
)
The subquery in the in statement is getting orders all of whose cartrows are in shipping.

Joining top records in T-SQL

SELECT MD.*, Contact.FirstName
FROM MerchantData MD
JOIN Merchant M ON M.MerchID = MD.MerchID
JOIN (SELECT TOP 1 * FROM Location WHERE Location.BusID = MD.BusID) L ON L.BusID=MD.BusID
AND L.Deleted = 0
JOIN Contact ON Contact.ContactID = L.PrincipalID
I am using SQLSERVER 2008 and trying to write this SQL statement. There is some times multiple locations for a busid and I want to join in only the first found. I am getting an error on the part "Location.BusID = MD.BusID" as MD.BusID cannot be bound. Is it possible to use the MD table in the nested select statment in this join or is there another way of accomplishing this?
I am contiplating putting the data using nested querys in the column list to grab the contact data driectly there.
It would be simpler I think to have a subquery of the full result set:
SELECT MD.*, Contact.FirstName
FROM MerchantData MD
JOIN Merchant M ON M.MerchID = MD.MerchID
JOIN (SELECT BusID, MAX(PrincipalID)
FROM Location
WHERE Deleted = 0
GROUP BY BusID) L ON L.BusID=MD.BusID
JOIN Contact ON Contact.ContactID = L.PrincipalID
You still get one record per BusID in the JOIN but it's not correlated.
SELECT MD.*, Contact.FirstName
FROM MerchantData MD
JOIN Merchant M ON M.MerchID = MD.MerchID
CROSS APPLY (SELECT TOP 1 * FROM Location WHERE BusID = MD.BusID AND DELETED = 0) L
JOIN Contact ON Contact.ContactID = L.PrincipalID
This is a case of the "top n per group" problem. This question will guide you:
SQL Server query select 1 from each sub-group
You'll want to be doing something like this:
SELECT MD.* ,
Contact.FirstName
FROM MerchantData MD
JOIN Merchant M ON M.MerchID = MD.MerchID
JOIN ( select * ,
seq = rank() over( partition by BusID order by BusID , ... )
from Location
where L.Deleted = 0
) L on L.BusID = MD.BusID
and seq = 1
JOIN Contact ON Contact.ContactID = L.PrincipalID
The virtual table expression should return at most 1 Location per BusID (0 if the BusID has no non-deleted Locations).
To try and isolate out the error I would try. See if it can match Location.BusID = MD.BusID.
SELECT MD.*, Contact.FirstName
FROM MerchantData MD
JOIN Merchant M ON M.MerchID = MD.MerchID
JOIN Location On Location.BusID = MD.BusID
You do not use the * so use
SELECT TOP 1 Location.BusID FROM Location WHERE Location.BusID = MD.BusID
Once you get the syntax working.
You do know that once you get this working it will only match if the "first" row and then check if it is deleted. The problem is that without an order by the "first" row is arbitrary. Even with a clustered index on a table there is no guaranteed sort without an order by clause. To get a repeatable answer you need a sort. But if you are sorting and only want the top row then a MAX or MIN and a group be more straight forward.
If you want just business that have one or more deleted locations then the following should work but you need to break out the columns for the group by. If two deleted locations have differenct contact name then it will report each. So, this may not be what you are looking for.
SELECT MD.col1, MD.col2, Contact.FirstName
FROM MerchantData MD
JOIN Merchant M ON M.MerchID = MD.MerchID
JOIN Location L
ON L.BusID = MD.BusID
AND L.Deleted = 0
JOIN Contact ON Contact.ContactID = L.PrincipalID
GROUP BY MD.col1, MD.col2, Contact.FirstName

Max date in view on left outer join

Thanks to a previous question, I found out how to pull the most recent data based on a linked table. BUT, now I have a related question.
The solution that I found used row_number() and PARTITION to pull the most recent set of data. But what if there's a possibility for zero or more rows in a linked table in the view? For example, the table FollowUpDate might have 0 rows, or 1, or more. I just want the most recent FollowUpDate:
SELECT
EFD.FormId
,EFD.StatusName
,MAX(EFD.ActionDate)
,EFT.Name AS FormType
,ECOA.Account AS ChargeOffAccount
,ERC.Name AS ReasonCode
,EAC.Description AS ApprovalCode
,MAX(EFU.FollowUpDate) AS FollowUpDate
FROM (
SELECT EF.FormId, EFD.ActionDate, EFS.Name AS StatusName, EF.FormTypeId, EF.ChargeOffId, EF.ReasonCodeId, EF.ApprovalCodeId,
row_number() OVER ( PARTITION BY EF.FormId ORDER BY EFD.ActionDate DESC ) DateSortKey
FROM Extension.FormDate EFD INNER JOIN Extension.Form EF ON EFD.FormId = EF.FormId INNER JOIN Extension.FormStatus EFS ON EFD.StatusId = EFS.StatusId
) EFD
INNER JOIN Extension.FormType EFT ON EFD.FormTypeId = EFT.FormTypeId
LEFT OUTER JOIN Extension.ChargeOffAccount ECOA ON EFD.ChargeOffId = ECOA.ChargeOffId
LEFT OUTER JOIN Extension.ReasonCode ERC ON EFD.ReasonCodeId = ERC.ReasonCodeId
LEFT OUTER JOIN Extension.ApprovalCode EAC ON EFD.ApprovalCodeId = EAC.ApprovalCodeId
LEFT OUTER JOIN (Select EFU.FormId, EFU.FollowUpDate, row_number() OVER (PARTITION BY EFU.FormId ORDER BY EFU.FollowUpDate DESC) FUDateSortKey FROM Extension.FormFollowUp EFU INNER JOIN Extension.Form EF ON EFU.FormId = EF.FormId) EFU ON EFD.FormId = EFU.FormId
WHERE EFD.DateSortKey = 1
GROUP BY
EFD.FormId, EFD.ActionDate, EFD.StatusName, EFT.Name, ECOA.Account, ERC.Name, EAC.Description, EFU.FollowUpDate
ORDER BY
EFD.FormId
If I do a similar pull using row_number() and PARTITION, I get the data only if there is at least one row in FollowUpDate. Kinda defeats the purpose of a LEFT OUTER JOIN. Can anyone help me get this working?
I rewrote your query - you had unnecessary subselects, and used row_number() for the FUDateSortKey but didn't use the column:
SELECT t.formid,
t.statusname,
MAX(t.actiondate) 'actiondate',
t.formtype,
t.chargeoffaccount,
t.reasoncode,
t.approvalcode,
MAX(t.followupdate) 'followupdate'
FROM (
SELECT t.formid,
fs.name 'StatusName',
t.actiondate,
ft.name 'formtype',
coa.account 'ChargeOffAccount',
rc.name 'ReasonCode',
ac.description 'ApprovalCode',
ffu.followupdate,
row_number() OVER (PARTITION BY ef.formid ORDER BY t.actiondate DESC) 'DateSortKey'
FROM EXTENSION.FORMDATE t
JOIN EXTENSION.FORM ef ON ef.formid = t.formid
JOIN EXTENSION.FORMSTATUS fs ON fs.statusid = t.statusid
JOIN EXTENSION.FORMTYPE ft ON ft.formtypeid = ef.formtypeid
LEFT JOIN EXTENSION.CHARGEOFFACCOUNT coa ON coa.chargeoffid = ef.chargeoffid
LEFT JOIN EXTENSION.REASONCODE rc ON rc.reasoncodeid = ef.reasoncodeid
LEFT JOIN EXTENSION.APPROVALCODE ac ON ac.approvalcodeid = ef.approvalcodeid
LEFT JOIN EXTENSION.FORMFOLLOWUP ffu ON ffu.formid = t.formid) t
WHERE t.datesortkey = 1
GROUP BY t.formid, t.statusname, t.formtype, t.chargeoffaccount, t.reasoncode, t.approvalcode
ORDER BY t.formid
The change I made to allow for FollowUpDate was to use a LEFT JOIN onto the FORMFOLLOWUP table - you were doing an INNER JOIN, so you'd only get rows with FORMFOLLOWUP records associated.
It's pretty hard to guess what's going on without table definitions and sample data.
Also, this is confusing: "the table FollowUpDate might have 0 rows" and you "want the most recent FollowUpDate." (especially when there is no table named FollowUpDate) There is no "most recent FollowUpDate" if there are zero FollowUpDates.
Maybe you want
WHERE <follow up date row number> in (1,NULL)
I figured it out. And as usual, I need a nap. I just needed to change my subselect to something I would swear I'd tried with no success:
SELECT field1, field2
FROM Table1 t1
LEFT JOIN (
SELECT field3, max(dateColumn)
FROM Table2
GROUP BY
field3
) t2
ON (t1.field1 = t2.field3)