Having issues with Subqueries and derived tables in SQL - sql-server

I am still extremely new at SQL and have recently been having issues with these 3 specific subqueries for several days.
This one is supposed to return 3 columns, only returns records of vendors from "CA" and uses a derived table.
-- Query 6
SELECT VendorID, VendorName, Vendors.VendorState
FROM Vendors
JOIN (SELECT VendorState
FROM Vendors) AS SubVendors ON Vendors.VendorID = SubVendors.VendorID
WHERE Vendors.VendorState = 'CA'
ORDER BY VendorID;
But when I run it, I get this error:
Msg 207, Level 16, State 1, Line 6
Invalid column name 'VendorID'.
This one returns 4 columns, also uses a derived table and the subquery is coded in the 'FROM' clause.
-- Query 7
SELECT
InvoicesMain.VendorID,
MAX(InvoiceTotal) AS MaxInvoice,
MIN(InvoiceTotal) AS MinInvoice,
AVG(InvoiceTotal) AS AvgInvoice
FROM
Invoices AS InvoiceMain
JOIN
(SELECT TOP 10
VendorID, AVG(InvoiceTotal) AS AvgInvoice
FROM Invoices
GROUP BY VendorID
ORDER BY AvgInvoice DESC) AS TopVendor ON InvoicesMain.VendorID = TopVendor.VendorID)
GROUP
InvoicesMain.VendorID
ORDER BY
MaxInvoice DESC;
But I get this error:
Msg 102, Level 15, State 1, Line 9
Incorrect syntax near ')'.
And lastly, I am honestly not sure how to do this one; it is supposed to be restated as a subquery:
SELECT
InvoiceNumber, InvoiceDate, InvoiceLineItemAmount
FROM
Invoices
JOIN
InvoiceLineItems ON Invoices.InvoiceID = Invoicelineitems.InvoiceID
WHERE
VendorID = 122
ORDER BY
InvoiceDate;
Any tips, please?

On Query 6, you didn't select the VendorID on your subquery, that's why the error occurred. Try this query instead:
SELECT VendorID, VendorName, Vendors.VendorState
FROM Vendors JOIN
(SELECT VendorState, VendorID
FROM Vendors) AS SubVendors
ON Vendors.VendorID = SubVendors.VendorID
WHERE V.VendorState = 'CA'
ORDER BY VendorID;
For Query 7 you are just missing a ) on your subquery.
SELECT InvoicesMain.VendorID, MAX(InvoiceTotal) AS MaxInvoice, MIN(InvoiceTotal) AS MinInvoice, AVG(InvoiceTotal) AS AvgInvoice
FROM Invoices AS InvoiceMain
JOIN
(SELECT TOP 10 VendorID, AVG(InvoiceTotal)) AS AvgInvoice
FROM Invoices
GROUP BY VendorID
ORDER BY AvgInvoice DESC) AS TopVendor
ON InvoicesMain.VendorID = TopVendor.VendorID)
GROUP InvoicesMain.VendorID
ORDER BY MaxInvoice DESC;
For the last item, you can reinstate this as subquery by selecting InvoiceID from the table InvoiceLineItems just like in Query 6
SELECT InvoiceNumber, InvoiceDate, InvoiceLineItemAmount
FROM Invoices JOIN
(SELECT InvoiceID FROM Invoicelineitems) As I
ON Invoices.InvoiceID = I.InvoiceID
WHERE VendorID = 122
ORDER BY InvoiceDate;

Related

Print results side by side SQL Server

I have following result set,
Now with above results i want to print the records via select query as below attached image
Please note, I will have only two types of columns in output Present Employee & Absent Employees.
I tried using pivot tables, temporary table but cant achieve what I want.
One method would be to ROW_NUMBER each the the "statuses" and then use a FULL OUTER JOIN to get the 2 datasets into the appropriate columns. I use a FULL OUTER JOIN as I assume you could have a different amount of employees who were present/absent.
CREATE TABLE dbo.YourTable (Name varchar(10), --Using a name that doesn't require delimit identification
Status varchar(7), --Using a name that doesn't require delimit identification
Days int);
GO
INSERT INTO dbo.YourTable(Name, Status, Days)
VALUES('Mal','Present',30),
('Jess','Present',20),
('Rick','Absent',30),
('Jerry','Absent',10);
GO
WITH RNs AS(
SELECT Name,
Status,
Days,
ROW_NUMBER() OVER (PARTITION BY Status ORDER BY Days DESC) AS RN
FROM dbo.YourTable)
SELECT P.Name AS PresentName,
P.Days AS PresentDays,
A.Name AS AbsentName,
A.Days AS AbsentDays
FROM (SELECT R.Name,
R.Days,
R.Status,
R.RN
FROM RNs R
WHERE R.Status = 'Present') P
FULL OUTER JOIN (SELECT R.Name,
R.Days,
R.Status,
R.RN
FROM RNs R
WHERE R.Status = 'Absent') A ON P.RN = A.RN;
GO
DROP TABLE dbo.YourTable;
db<>fiddle
2 CTE's is actually far neater:
WITH Absents AS(
SELECT Name,
Status,
Days,
ROW_NUMBER() OVER (ORDER BY Days DESC) AS RN
FROM dbo.YourTable
WHERE Status = 'Absent'),
Presents AS(
SELECT Name,
Status,
Days,
ROW_NUMBER() OVER (ORDER BY Days DESC) AS RN
FROM dbo.YourTable
WHERE Status = 'Present')
SELECT P.Name AS PresentName,
P.Days AS PresentDays,
A.Name AS AbsentName,
A.Days AS AbsentDays
FROM Absents A
FULL OUTER JOIN Presents P ON A.RN = P.RN;

Invalid Column Names and An Expression of non-boolean type specified in a context where a condition is expected

In query 3 and 4 I'm getting the following errors. For 3 my SupplierID and Average is an invalid column name and I cannot figure out why. Also my WHERE has Incorrect syntax near WHERE. Expecting AS, ID or QUOTED_ID and row_number is Invalid Column Name.
As for query 4, my AVG(UnitPrice) throws an
An expression of non-boolean type specified in a context where a condition is expected
and again I'm lost and not sure why.
Query 3
SELECT
SupplierID, average
FROM
(SELECT DISTINCT
SupplierID, AVG(UnitPrice) average
FROM
Products
GROUP BY
SupplierId
ORDER BY
average DESC)
WHERE
row_number <= 2
Query 4
SELECT TOP 3
Products.SupplierID AS SupplierID, AVG(UnitPrice) AVG_UnitPrice,
CompanyName, Phone
FROM
Products
JOIN
Suppliers ON Products.SupplierID = Suppliers.SupplierID
GROUP BY
Products.SupplierID, CompanyName, Phone
HAVING
AVG(UnitPrice) >= 15 AND AVG(UnitPrice)
SQL Code
Query 3
Try to give subquery a alias name
SELECT t1.SupplierID, t1.average
FROM (SELECT distinct SupplierID, AVG(UnitPrice) average FROM Products group
by SupplierId order by average desc) as t1
WHERE row_number <= 2
Query 4
remove sql statement behind having and AVG(UnitPrice)
Select top 3 Products.SupplierID as SupplierID, AVG(UnitPrice) AS AVG_UnitPrice, CompanyName, Phone
From Products join Suppliers on Products.SupplierID = Suppliers.SupplierID
Group by Products.SupplierID, CompanyName, Phone
having
AVG(UnitPrice) >= 15
Query 3
error means row_number column is not create in query , so here is the solution
Correct Sql Query:
SELECT
SupplierID, average ,row_no
FROM
(SELECT DISTINCT
SupplierID, AVG(UnitPrice) average ,
ROW_NUMBER()over (order by supplierid ) as row_no
FROM
Products
GROUP BY
SupplierId
) temp
WHERE row_no<= 2
ORDER BY average DESC
Query 4
An expression of non-boolean type specified in a context where a condition is expected
Error means condition is required with last and clause
Correct Sql Query :
SELECT TOP 3
Products.SupplierID AS SupplierID, AVG(UnitPrice) AVG_UnitPrice,
CompanyName, Phone
FROM
Products
JOIN
Suppliers ON Products.SupplierID = Suppliers.SupplierID
GROUP BY
Products.SupplierID, CompanyName, Phone
HAVING
AVG(UnitPrice) >= 15

subquery to retrieve a query

My Sales.Customer table includes the following columns:
CustomerID (integer)
FirstName (nvarchar(50))
LastName (nvarchar(50))
My Sales.SalesOrder table includes the following columns:
SalesOrderNumber (integer)
OrderDate (date)
CustomerID (integer)
Amount (money)
Some customers have placed multiple orders over a period of years. I've written the following query to retrieve the last date on which each customer placed an order:
SELECT c.CustomerID, c.FirstName, c.LastName,
-- correlated subquery goes here
AS LastOrderDate
FROM Sales.Customer AS c;
Why does my subquery not complete my actual query? Am I missing something or should it be something different?
(SELECT MAX(o.OrderDate)
FROM Sales.SalesOrder AS o
WHERE o.CustomerID = c.CustomerID)
I considered whether I might be over-complicating my solution so maybe this should work instead?
(SELECT MAX(c.OrderDate)
FROM Sales.Customer AS c)
You should be able to rewrite it as a CTE like this.
;WITH LastOrderDate AS
(
SELECT CustomerID,MAX(OrderDate) AS LastOrderDate
FROM Sales.SalesOrder
GROUP BY CustomerID
)
SELECT c.CustomerID, c.FirstName, c.LastName, LastOrderDate
AS LastOrderDate
FROM Sales.Customer AS c
LEFT JOIN LastOrderDate l
ON c.CustomerID = l.CustomerID;

Join two tables on matching ID?

Question
I have a query and would like to join one column from another table based on its matching ID
Query So far
SELECT DENumber, AcquiredDate, ItemDescription, ItemName, LocationID FROM dbo.Assets JOIN LocationName FROM dbo.Locations on ProductID
WHERE DATEDIFF(YEAR, AcquiredDate, GetDate()) >= 6
The matching ID in both tables is LocationID
Problem
My query is wrong and throws errors
Msg 156, Level 15, State 1, Line 1
Incorrect syntax near the keyword 'FROM'.
Rewrite your query to this:
SELECT DENumber, AcquiredDate, ItemDescription, ItemName, LocationName
FROM dbo.Assets INNER JOIN dbo.Locations ON Assets.LocationId = Locations.LocationId
WHERE DATEDIFF(YEAR, AcquiredDate, GetDate()) >= 6
To clarify: In a JOIN, you must specify the names of both tables on either side of the JOIN keyword. Then, you specify the join criteria after the ON keyword:
FROM <<table1>> INNER JOIN <<table2>> ON <<join criteria>>
Here, we only want records that exist in both tables, which is why we use the INNER join, but you can also take all records from either the LEFT table, the RIGHT table or BOTH tables. In that case, you would do a LEFT JOIN, RIGHT JOIN our OUTER JOIN respectively.
SELECT L.DENumber, A.AcquiredDate, A.ItemDescription, A.ItemName, L.LocationID
FROM dbo.Assets A JOIN
ON dbo.Locations L on
L.ProductID = A.ProductID
WHERE DATEDIFF(YEAR, A.AcquiredDate, GetDate()) >= 6

Alias name issue in SQL

I was trying to write a query for the SQL Server sample DB Northwind. The question was: "Show the most recent five orders that were purchased by a customer who has spent more than $25,000 with Northwind."
In my query the Alias name - "Amount" is not being recognized. My query is as follows:
select top(5) a.customerid, sum(b.unitprice*b.quantity) as "Amount", max(c.orderdate) as Orderdate
from customers a join orders c
on a.customerid = c.customerid
join [order details] b
on c.orderid = b.orderid
group by a.customerid
--having Amount > 25000 --throws error
having sum(b.unitprice*b.quantity) > 25000 --works, but I don't think that this is a good solution
order by Orderdate desc
Pls let me know what I am doing wrong here, as I am a newbie in writing T Sql. Also can this query and my logic be treated as production level query?
TIA,
You must use the aggregate in the query you have. This all has to do with the order in which a SELECT statement is executed. The syntax of the SELECT statement is as follows:
SELECT
FROM
WHERE
GROUP BY
HAVING
ORDER BY
The order in which a SELECT statement is executed is as follows. Since the SELECT clause isn't executed until after the HAVING clause, you can't use the alias like you can in the ORDER BY clause.
FROM
WHERE
GROUP BY
HAVING
SELECT
ORDER BY
Reference Article: http://www.bennadel.com/blog/70-sql-query-order-of-operations.htm
This is a known limitation in SQL Server, at least, but no idea if it's a bug, intentional or even part of the standard. But the thing is, neither the WHERE or HAVING clauses accept an alias as part of their conditions, you must use only columns from the original source tables, which means that for filtering by calculated expressions, you must copy-paste the very same thing in both the SELECT and WHERE parts.
A workaround for avoiding this duplication can be to use a subquery or cte and apply the filter on the outer query, when the alias is just an "input" table:
WITH TopOrders AS (
select a.customerid, sum(b.unitprice*b.quantity) as "Amount", max(c.orderdate) as Orderdate
from customers a join orders c
on a.customerid = c.customerid
join [order details] b
on c.orderid = b.orderid
group by a.customerid
--no filter here
order by Orderdate desc
)
SELECT TOP(5) * FROM TopOrders WHERE Amount > 25000 ;
Interesting enough, the ORDER BY clause does accepts aliases directly.
You must use Where b.unitprice*b.quantity > 25000 instead of having Amount > 25000.
Having used for aggregate conditions. Your business determine your query condition. If you need to calculate sum of prices that have above value than 25000, must be use Where b.unitprice*b.quantity > 25000 and if you need to show customer that have total price above than 25000 must be use having Amount > 25000 in your query.
select top(5) a.customerid, sum(b.unitprice*b.quantity) as Amount, max(c.orderdate) as Orderdate
from customers a
JOIN orders c ON a.customerid = c.customerid
join [order details] b ON c.orderid = b.orderid
group by a.customerid
having sum(b.unitprice*b.quantity) > 25000 --works, but I don't think that this is a good solution
Order by Amount
I don't have that schema at hand, so table' and column' names might go a little astray, but the principle is the same:
select top (5) ord2.*
from (
select top (1) ord.CustomerId
from dbo.Orders ord
inner join dbo.[Order Details] od on od.OrderId = ord.OrderId
group by ord.CustomerId
having sum(od.unitPrice * od.Quantity) > $25000
) sq
inner join dbo.Orders ord2 on ord2.CustomerId = sq.CustomerId
order by ord2.OrderDate desc;
The Having Clause will works with aggregate function like SUM,MAX,AVG..
You may try like this
SELECT TOP 5 customerid,SUM(Amount)Amount , MAX(Orderdate) Orderdate
FROM
(
SELECT A.customerid, (B.unitprice * B.quantity) As "Amount", C.orderdate As Orderdate
FROM customers A JOIN orders C ON A.customerid = C.customerid
JOIN [order details] B ON C.orderid = B.orderid
) Tmp
GROUP BY customerid
HAVING SUM(Amount) > 25000
ORDER BY Orderdate DESC
The question is little ambiguos.
Show the most recent five orders that were purchased by a customer who
has spent more than $25,000 with Northwind.
Is it asking to show the 5 recent orders by all the customers who have spent more than $25,000 in all of their transactions (which can be more than 5).
The following query shows all the customers who spent $25000 in all of their transactions (not just the recent 5).
In one of the Subquery BigSpenders it gets all the Customers who spent more than $25000.
Another Subquery calculates the total amount for each order.
Then it gets rank of all the orders by OrderDate and OrderID.
Then it filters it by Top 5 orders for each customer.
--
SELECT *
FROM (SELECT C.customerid,
C.orderdate,
C.orderid,
B3.amount,
Row_number()
OVER(
partition BY C.customerid
ORDER BY C.orderdate DESC, C.orderid DESC) Rank
FROM orders C
JOIN
--Get Amount Spend Per Order
(SELECT b2.orderid,
Sum(b2.unitprice * b2.quantity) AS Amount
FROM [order details] b2
GROUP BY b2.orderid) B3
ON C.orderid = B3.orderid
JOIN
--Get Customers who spent more than 25000
(SELECT c.customerid
FROM orders c
JOIN [order details] b
ON c.orderid = b.orderid
GROUP BY c.customerid
HAVING Sum(b.unitprice * b.quantity) > 25000) BigSpenders
ON C.customerid = BigSpenders.customerid) X
WHERE X.rank <= 5

Resources