Cannot reference subquery column by outer order by - sql

The ORDER BY clause below is causing an error. How can I reference the TotalPrice column in ORDER BY clause:
SELECT * FROM
(
SELECT O.OrderID,
(SELECT SUM(SubTotal) FROM DB_OrderDetails OD WHERE OD.OrderID = O.OrderID) AS TotalPrice,
ROW_NUMBER() OVER (ORDER BY TotalPrice) AS RowNum
FROM DB_Orders O
) Orders

You cannot reference it by its name in the same sub-query, you nave to do it in the outer query:
SELECT orders.*,
ROW_NUMBER() OVER (ORDER BY TotalPrice) AS RowNum FROM
(
SELECT O.OrderID,
(SELECT SUM(SubTotal) FROM DB_OrderDetails OD
WHERE OD.OrderID = O.OrderID) AS TotalPrice
FROM DB_Orders O
) Orders

How about this:
select
o.orderID,
sum(od.SubTotal) as TotalPrice,
row_number() over (order by sum(od.SubTotal)) as RowNum
from DB_Orders o
join DB_OrderDetails od
on o.OrderID = od.OrderID
group by o.OrderID
Here is the example: SQL Fiddle

Related

How can I select other table fields when using GROUP BY and MAX?

Let's say I have a table named Orders and fields CustomerId, EmployeeId, OrderDate
I want to select all those fields for each CustomerId by the latest OrderDate.
So far, I've managed to select CustomerId and OrderDate, but I don't know how to include EmployeeId
SELECT [CustomerId], MAX(OrderDate)
FROM [Orders]
GROUP BY [CustomerId]
ORDER BY [CustomerId]
Use row_number():
select o.*
from (select o.*,
row_number() over (partition by customerid order by orderdate desc) as seqnum
from orders o
) o
where seqnum = 1;
That is the "traditional" solution. If you have a list of all customers, I like the solution using cross apply:
select o.*
from customers c cross apply
(select top 1 o.*
from orders o
where o.customerid = c.customerid
order by o.orderdate desc
) o;

I wanted to create a SQL query to list of all customers who have placed an above average number of orders

SQL query to list of all customers who have placed an above average number of orders.
Order details are present in NW_orders table and Customer info is present in NW_Customers table.
First, I have calculated the Avg number of orders placed by all customers. Then i want to pull only customer who have placed order greater than the avg number of orders.
My query:
SELECT
C.customerid, C.companyname, COUNT(O.orderid) AS cnt
FROM
NW_customers C
LEFT JOIN
NW_orders O ON O.customerID = C.Customerid
GROUP BY
C.customerid
HAVING
cnt > (SELECT COUNT(O.OrderID) / COUNT(DISTINCT(c.customerid)) AS Avg
FROM NW_orders O
LEFT JOIN NW_customers C ON O.customerID = C.Customerid)
I am getting an error
ORA-00904: "CNT": invalid identifier
Can anyone please help to rectify the error?
Use a common table expression:
WITH cte AS (
SELECT o.customerid, COUNT(o.orderid) AS cnt
FROM NW_orders o
GROUP BY o.customerid
)
SELECT t.customerid
FROM cte t
WHERE t.cnt > (SELECT AVG(cnt) FROM cte)
If you want to bring in actual customer information, you can add a join to the above query:
SELECT t1.*, t2.*
FROM cte t1
INNER JOIN NW_customers t2
ON t1.customerid = t2.customerid
WHERE t1.cnt > (SELECT AVG(cnt) FROM cte)
This is a classic for analytic function.
cutomerId
select customerID
from (select customerID
,count(*) as customer_orders
,avg (count(*)) over () as avg_customer_orders
from NW_orders
group by customerID
)
where customer_orders > avg_customer_orders
;
Full customer information
select *
from NW_customers
where customerID in
(
select customerID
from (select customerID
,count(*) as customer_orders
,avg (count(*)) over () as avg_customer_orders
from NW_orders
group by customerID
)
where customer_orders > avg_customer_orders
)
;
Full customer information + Orders information
select o.customer_orders
,o.avg_customer_orders
,c.*
from NW_customers c
join (select customerID
,count(*) as customer_orders
,avg (count(*)) over () as avg_customer_orders
from NW_orders
group by customerID
) o
on o.customerID =
c.customerID
where o.customer_orders > o.avg_customer_orders
;

SQL query for top 10 selling sku's by brand in Volusion

been messing with this query for Volusion store, trying to get top selling sku's by brand.... and I have done so, but how can I also show only the top 10 PER brand....
If I add a top 10 its just 10 rows period.
select
products_joined.ProductManufacturer as brand,
Sum(OrderDetails.ProductPrice * OrderDetails.Quantity) AS TotalSold,
OrderDetails.ProductCode as sku
from
orderdetails, orders, products_joined
where
products_joined.ProductCode = OrderDetails.ProductCode
and Orders.OrderID = OrderDetails.OrderID
and Orders.OrderDate BETWEEN getdate() - 90 AND getdate()
and Orders.OrderStatus <> 'Cancelled'
and products_joined.ProductManufacturer is not null
group by
products_joined.ProductManufacturer, OrderDetails.ProductCode
order by
products_joined.ProductManufacturer,
Sum(OrderDetails.ProductPrice*OrderDetails.Quantity) DESC
if ROW_NUMBER is available, you might also be able to use CTE's and do something like this.
;WITH cteProductsSold AS (
SELECT pj.ProductManufacturer AS brand,
od.ProductCode AS sku,
SUM(od.ProductPrice * od.Quantity) AS TotalSold
FROM orders o
INNER JOIN orderdetails od ON od.OrderID = o.OrderID
INNER JOIN products_joined pj ON pj.ProductCode = od.ProductCode
WHERE o.OrderDate BETWEEN GETDATE() - 90 AND GETDATE()
AND o.OrderStatus <> 'Cancelled'
AND pj.ProductManufacturer IS NOT NULL
GROUP BY pj.ProductManufacturer,
od.ProductCode
), cteProductOrdered AS (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY brand ORDER BY TotalSold DESC) Rn
FROM cteProductsSold
)
SELECT brand,
sku,
TotalSold
FROM cteProductOrdered
WHERE Rn < 11
alternatively, you can use derived tables instead of CTEs.
SELECT brand,
sku,
TotalSold
FROM ( SELECT *,
ROW_NUMBER() OVER (PARTITION BY brand ORDER BY TotalSold DESC) Rn
FROM ( SELECT pj.ProductManufacturer AS brand,
od.ProductCode AS sku,
SUM(od.ProductPrice * od.Quantity) AS TotalSold
FROM orders o
INNER JOIN orderdetails od ON od.OrderID = o.OrderID
INNER JOIN products_joined pj ON pj.ProductCode = od.ProductCode
WHERE o.OrderDate BETWEEN GETDATE() - 90 AND GETDATE()
AND o.OrderStatus <> 'Cancelled'
AND pj.ProductManufacturer IS NOT NULL
GROUP BY pj.ProductManufacturer,
od.ProductCode
) p
) ps
WHERE Rn < 11
this should work too
select * from (
select
products_joined.ProductManufacturer as brand,
Sum(OrderDetails.ProductPrice*OrderDetails.Quantity) AS TotalSold,
OrderDetails.ProductCode as sku,
row_number() over ( partition by products_joined.ProductManufacturer, OrderDetails.ProductCode order by Sum(OrderDetails.ProductPrice*OrderDetails.Quantity) desc) rowid
from orderdetails, orders, products_joined
where
products_joined.ProductCode = OrderDetails.ProductCode and
Orders.OrderID = OrderDetails.OrderID and
Orders.OrderDate BETWEEN getdate() - 90 AND getdate()
AND Orders.OrderStatus <> 'Cancelled' and products_joined.ProductManufacturer is not null
GROUP BY products_joined.ProductManufacturer, OrderDetails.ProductCode
) as x
where rowid < 11
ORDER BY brand,TotalSold DESC

Select maximum value of one table depending on 2 other tables

I have 3 tables
Orders (orderID, CustomerID)
Orderlines (orderID, ProdID)
Products (ProdID, CategoryID)!
I want to find the customerID which has the most different "CategoryID" in one order!
To get you there, start with the basic query to get your info:
SELECT o.customer_id
,l.orderid
,COUNT(DISTINCT categoryid) category_cnt
FROM orders o
JOIN orderlines l on l.orderid = o.orderid
JOIN products p ON l.prodid = p.prodid
GROUP BY l.customner_id, l.orderid
order by COUNT(DISTINCT categoryid) desc;
Once you see that this works out, we will add an analytic to this to show you the rank() function
SELECT o.customer_id
,l.orderid
,COUNT(DISTINCT categoryid) category_cnt
, rank() over (order by COUNT(DISTINCT categoryid) desc) as count_rank
FROM orders o
JOIN orderlines l on l.orderid = o.orderid
JOIN products p ON l.prodid = p.prodid
GROUP BY l.customner_id, l.orderid
order by COUNT(DISTINCT categoryid) desc;
Following so far? OK, so now we just need to push this down into a sub-query to get the record(s) ranked #1 (in case more than one customer order matches the top count)
SELECT customer_id, order_id, category_cnt
FROM (
SELECT o.customer_id
,l.orderid
,COUNT(DISTINCT categoryid) category_cnt
, rank() over (order by COUNT(DISTINCT categoryid) desc) as count_rank
FROM orders o
JOIN orderlines l on l.orderid = o.orderid
JOIN products p ON l.prodid = p.prodid
GROUP BY l.customner_id, l.orderid)
WHERE count_rank = 1;
Try;
with data_a as ( --distinct CategoryID cnt
select
o.orderID,
o.customerID,
count(DISTINCT p.CategoryID) cnt
from Orders o
join Orderlines ol.orderID = o.orderID
join Products p on p.ProdID = ol.ProdID
group by o.orderID, o.customerID
),
data as ( --get all count rnk
select
orderID,
customerID,
rank() over (partition by orderID, customerID order by cnt desc) rnk
from data_a
)
select
orderID,
customerID
from data
where rnk = 1
Step by step: Count distinct categories per order first. Then rank your orders, so that the orders with the most categories get rank #1. Then find customers for all orders ranked #1.
select distinct cutomerid
from orders
where orderid in
(
select orderid
from
(
select orderid, rank() over (order by category_count desc) as rnk
from
(
select ol.orderid, count(distinct p.distinctcategroyid) as category_count
from orderlines ol
join products p on p.prodid = ol.prodid
group by ol.orderid
) counted
) ranked
where rnk = 1
);
Something like that i guess
SELECT o.customerID, t.category_cnt
FROM (SELECT l.orderid, COUNT(DISTINCT categoryid) category_cnt
FROM orderlines l
JOIN products p ON l.prodid = p.prodid
GROUP BY l.orderid
ORDER BY category_cnt DESC) t
JOIN orders o ON o.orderid = t.orderid
WHERE rownum < 2

SELECT TOP record for each year

I am trying to recap on my sql skill, now I am trying to run a simple query on northwinddb to show me the top customer for each year, but as soon as I use the TOP function only 1 record gets display no matter on what I partition by, This is my T-SQL code
SELECT DISTINCT TOP 1 C.CompanyName
, YEAR(O.OrderDate) AS Year
, SUM(Quantity) OVER(PARTITION BY C.CompanyName, YEAR(O.OrderDate)) AS Total
FROM Customers C JOIN Orders O
ON C.CustomerID = O.CustomerID JOIN [Order Details] OD
ON O.OrderID = OD.OrderID
You can do this bit more compactly in SQL Server 2008 as follows:
select top (1) with ties
C.CompanyName,
Year(O.OrderDate) as Yr,
sum(OD.Quantity) as Total
from Orders as O
join Customers as C on C.CustomerID = O.CustomerID
join "Order Details" as OD on OD.OrderID = O.OrderID
group by C.CompanyName, Year(O.OrderDate)
order by
row_number() over (
partition by Year(O.OrderDate)
order by sum(OD.Quantity) desc
);
Thank you for the help. I found a way that allows me to change the number of top customers i want to see for each year. By using Sub queries and Row_Number
SELECT CompanyName
,yr
,Total
FROM(SELECT CompanyName
, yr
, Total
, ROW_NUMBER() OVER(PARTITION BY yr ORDER BY yr, Total DESC) AS RowNumber
FROM(SELECT DISTINCT CompanyName
, YEAR(O.OrderDate) AS yr
, SUM(OD.Quantity) OVER(PARTITION BY CompanyName
, YEAR(O.OrderDate)) As Total
FROM Customers C JOIN Orders O
ON C.CustomerID = O.CustomerID JOIN [Order Details] OD
ON O.OrderID = OD.OrderID) Tr)Tr2
Where RowNumber <= 1
Three steps: first sum quantities grouped by company and year, then order the results by quantity, then filter first row by group only.
; WITH sums as (
SELECT C.Companyname, YEAR(O.OrderDate) AS Year, sum (Quantity) Total
FROM Customers C JOIN Orders O
ON C.CustomerID = O.CustomerID JOIN [Order Details] OD
ON O.OrderID = OD.OrderID
group by C.Companyname, YEAR(O.OrderDate)
)
with ordering as (
select Companyname, Year, Total,
row_number() over (partition by CompanyName, Year order by Total desc) rownum
from sums
)
select *
from ordering
where rownum = 1