Query AdventureWorks 2019 - sql-server

I have 2 tables:
production.product
sales.salesorderdetail
In the AdventureWorks 2019 sample database.
Write a report that displays the most expensive item from each invoice along with its amount
Invoice number, product title, product amount

You can try something like this:
Build a CTE (Common Table Expression) to list the orders, details, and products
Create a row number - based on ROW_NUMBER() function - to find the most expensive product
Select from that CTE and show only the most expensive product per order
I used this code (your column names aren't always accurate and don't always exist - I tried to guess as best I could. Adapt if needed):
WITH BaseData AS
(
SELECT
soh.SalesOrderNumber,
ProductName = p.Name,
sod.OrderQty,
p.ListPrice,
RowNum = ROW_NUMBER() OVER (PARTITION BY soh.SalesOrderID ORDER BY p.ListPrice DESC)
FROM
Sales.SalesOrderHeader soh
INNER JOIN
Sales.SalesOrderDetail sod ON sod.SalesOrderID = soh.SalesOrderID
INNER JOIN
Production.Product p ON p.ProductID = sod.ProductID
)
SELECT
*
FROM
BaseData
WHERE
RowNum = 1
ORDER BY
SalesOrderNumber;
and I get an output something like this (only first few rows):
SalesOrderNumber
ProductName
OrderQty
ListPrice
SO43659
Mountain-100 Silver, 44
2
3399.99
SO43660
Road-450 Red, 52
1
1457.99
SO43661
Mountain-100 Silver, 44
2
3399.99
SO43662
Road-150 Red, 62
1
3578.27
SO43663
Road-650 Red, 60
1
782.99

Related

Query for Customers, total value of purchases, and total discounts given to Customers by Year

I using the Northwind database. I wanted to write a query that displays the Customers, total value of purchases, and total discounts given to Customers by the Year. I wanted to show all Customers for the year 1996, 1997, 1998.
Down below are the tables I am using.
SELECT TOP (1000) [CustomerID]
,[CompanyName]
,[ContactName]
,[ContactTitle]
,[Address]
,[City]
,[Region]
,[PostalCode]
,[Country]
,[Phone]
,[Fax]
FROM [Northwind].[dbo].[Customers]
SELECT TOP (1000) [OrderID]
,[CustomerID]
,[EmployeeID]
,[OrderDate]
,[RequiredDate]
,[ShippedDate]
,[ShipVia]
,[Freight]
,[ShipName]
,[ShipAddress]
,[ShipCity]
,[ShipRegion]
,[ShipPostalCode]
,[ShipCountry]
FROM [Northwind].[dbo].[Orders]
SELECT TOP (1000) [OrderID]
,[ProductID]
,[UnitPrice]
,[Quantity]
,[Discount]
FROM [Northwind].[dbo].[Order Details]
Here is the query I wrote:
SELECT o.customerID, year(o.OrderDate) as Year,
sum(od.UnitPrice * od.Quantity) as Total_value_of_purchase,
sum(od.UnitPrice * od.Quantity * od.Discount) as Total_discount_received,
sum((od.UnitPrice * od.Quantity)-(od.UnitPrice * od.Quantity * od.Discount)) as Total_after_discount
FROM Customers as c
cross JOIN Orders as o
left outer join [Order Details] as od
on o.OrderID=od.OrderID
GROUP BY o.CustomerID, Year(o.OrderDate)
order by o.CustomerID, Year(o.OrderDate)
Here is the picture of the result I got.
You can notice that, in some cases, customers might not have ordered in every year, so I only got 234 results. Is there is any way I can display all customers and all 3 years? so if I have 91 customers, the result would show 91*3=273 result.
Can you please provide me with a guideline or some help, so I can follow to remedy this?
I have some update. I tried using union.
SELECT c.CustomerID, y.YR,
sum(od.UnitPrice * od.Quantity) as Total_value_of_purchase,
sum(od.UnitPrice * od.Quantity * od.Discount) as Total_discount_received,
sum((od.UnitPrice * od.Quantity)-(od.UnitPrice * od.Quantity * od.Discount)) as Total_after_discount
FROM (SELECT 1996 YR UNION ALL SELECT 1997 UNION ALL SELECT 1998) y(YR)
CROSS JOIN Customers c
LEFT OUTER JOIN Orders o ON c.CustomerID = o.CustomerID
LEFT OUTER JOIN [Order Details] od ON o.OrderID = od.OrderID
GROUP BY c.CustomerID, y.YR
ORDER BY c.CustomerID, y.YR
The result: https://ibb.co/N9T7pk2
I did get 273 results, but instead of giving me the total for each year, it gave me the total for all years.
Any suggestions?
PS: I am not doing this for the assignment. I am genuinely interested in finding the solution.

SQL Query to retrieve total order count for each user

Write an SQL command that retrieves last name and first name of all customers and the order numbers of orders they have placed…
CustDetails TABLE: http://prntscr.com/msicdp
OrderDetails TABLE: http://prntscr.com/msichp
I am trying to display list of all users from CustDetails (table), with an additional column, "TotalOrders", that counts how many orders each user have from OrderDetails (table) with COUNT(*), but it seems like I have no idea what am I doing.
I've tried LEFT JOIN paired with COUNT(*) AS [Total Orders] and I am getting all kind of errors whatever I try
SELECT DISTINCT CustDetails.*, OrderDetails.CustRef,COUNT(*) AS [Order_number]
FROM CustDetails
LEFT JOIN OrderDetails ON CustDetails.CustRef = OrderDetails.CustRef
GROUP BY CustDetails.FName
--SELECT CustDetails.CustRef, count(*) AS NUM
-- FROM CustDetails GROUP BY CustRef
You can't put * with GROUP BY. If you are using GROUP BY, all non-aggregated columns should be present in your GROUP BY clause.
You need to write your query like the following.
select c.CustRef,
c.LName,
c.Fname,
sum(case when od.CustRef is null then 0 else 1 end) TotalOrders
from CustDetails c
left join OrderDetails od on od.CustRef =c.CustRef
group by c.CustRef ,c.LName, C.Fname
In case you need all the columns you can try like the following without GROUP BY.
select *,
(select count(*) from OrderDetails od where od.CustRef =c.CustRef) TotalOrders
from CustDetails c
Another way of doing it using PARTITION BY
select * from
(
select c.*,
sum(case when od.CustRef is null then 0 else 1 end) over(partition by c.CustRef) as TotalOrders,
row_number() over (partition by c.CustRef order by (select 1)) rn
from CustDetails c
left join OrderDetails od on od.CustRef =c.CustRef
) t
where rn=1

Provide a unique list of customer id’s and account numbers which have ordered both products 711 and 712 after July 1, 2008

The question is from AdventureWorks2008R2
I tried this query, but I only want Customers who ordered both the products 711 and 712. For example, CustomerID 11441 has ordered productid 711 and 712, so only 11441 shall be displayed
Select DISTINCT(oh.CustomerID), oh.AccountNumber, CAST(oh.OrderDate as DATE) OrderDates, od.ProductID
From Sales.SalesOrderHeader oh
Inner Join Sales.SalesOrderDetail od
ON od.SalesOrderID = oh.SalesOrderID
WHERE od.ProductID BETWEEN 711 AND 712
AND CAST(oh.OrderDate as DATE) > '2008-07-01'
ORDER BY oh.CustomerID
Screenshot of my output
SELECT
oh.CustomerID, oh.AccountNumber,
CAST(oh.OrderDate as DATE) OrderDates, /* doesn't really make sense as a plural */
od.ProductID
FROM Sales.SalesOrderHeader oh
WHERE CAST(oh.OrderDate as DATE) > '2008-07-01'
AND (
SELECT COUNT(DISTINCT ProductID)
FROM SalesOrderDetail od
WHERE od.SalesOrderID = oh.SalesOrderID
AND od.ProductID IN (711, 712)
) = 2
ORDER BY oh.CustomerID;
Here's one way using a correlated subquery. By the way, what you were trying to do with DISTINCT in your original query won't work.

CASE Statement with condition

I think this will be easier to show an example first and then explain:
SELECT P.ID,
(CASE WHEN PC.NewCostPrice IS NULL
THEN P.Cost ELSE MAX(PC.Date) PC.NewCostPrice
END)
FROM price AS P
LEFT OUTER JOIN priceChange as PC
ON P.ID = PC.ID
So in the example, if the NewCostPrice IS NULL, meaning there wasn't a price change, then I want the normal cost (P.Cost). However, if there was a price change, I want the most recent (MAX(Date)) price change. I am not sure how to incorporate that into the CASE statement.
I feel like it can be done with a subquery and having clause but that didn't really work out when I tried. Any suggestions?
Thanks!
There are 2 approaches you might consider - I would test both to see which performs better for your situation.
Use ROW_NUMBER() in subquery to find most recent price change of all price changes, then join that to prices to get correct price.
Use correlated subquery (many ways of this, either in SELECT as in other answer or with OUTER APPLY) to get only most recent price change for each row of prices
If your price table is very large and you are getting a large number of prices at once, method #1 will likely be better so the correlated subquery doesn't run for every single row of the result set.
If your final query pulls back a relatively small number of records instead of huge result sets for your server, then the correlated subquery could be better for you.
1. The ROW_NUMBER() approach
SELECT
P.ID,
COALESCE(PC.NewCostPrice, P.Cost) AS LatestPrice
FROM Price AS P
LEFT OUTER JOIN (
SELECT
ID,
ROW_NUMBER() OVER (PARTITION BY ID
ORDER BY [Date] DESC) AS RowId,
NewCostPrice
FROM PriceChange
) PC
ON P.ID = PC.ID
AND PC.RowId = 1 -- Only most recent
2a. Correlated subquery (SELECT)
SELECT
P.ID,
COALESCE((
SELECT TOP 1
NewCostPrice
FROM PriceChange PC
WHERE PC.ID = P.ID
ORDER BY PC.[Date] DESC
), P.Cost) AS LatestPrice
FROM Price AS P
2b. Correlated subquery with OUTER APPLY
SELECT
P.ID,
COALESCE(PC.NewCostPrice, P.Cost) AS LatestPrice
FROM Price AS P
OUTER APPLY (
SELECT TOP 1
NewCostPrice
FROM PriceChange PC
WHERE PC.ID = P.ID
ORDER BY PC.[Date] DESC
) PC
Whether you use 2a or 2b is more likely a preference in how you want to maintain the query going forward.
Easy way
SELECT distinct P.ID,
ISNULL((SELECT TOP 1 PC1.NewCostPrice FROM priceChange as PC1 WHERE PC1.ID = p.id ORDER BY PC1.Date DESC), p.cost)
FROM price AS P
Here I assume PC.ID is not a primary key, or it makes no sense to join with ID while there could be different price on the same item.
From your query I assume you just want to fetch the latest NewCostPrice sorted by Date, by joining priceChange
SELECT
P.ID,
CASE
WHEN PC.NewCostPrice IS NULL THEN P.Cost
ELSE PC.NewCostPrice
END AS NewPrice
FROM
price AS P
LEFT JOIN
(SELECT *, RANK() OVER (PARTITION BY ID ORDER BY [Date] DESC) as rk FROM priceChange) PC ON P.ID = PC.ID AND PC.rk = 1
SELECT P.ID
,(CASE
WHEN PC.NewCostPrice IS NULL
THEN P.Cost
ELSE (SELECT TOP 1 PC1.NewCostPrice
FROM priceChange PC1
WHERE PC1.ID = PC.ID
GROUP BY PC1.NewCostPrice, PC1.Date
ORDER BY PC1.Date DESC
)
END
)
FROM price AS P
LEFT OUTER JOIN priceChange as PC
ON P.ID = PC.ID

SQL select top 10 for each year

I have a fact database from which I want to make a trendline based on top 10 items based on sum quantity for each item per year.
I've done the following, but it does for example select more than 10 entities for my year 2007:
select TOP 10 sum(Quantity) as Quantity,DIM_Time.Year, DIM_Item.Name as Name
from Fact_Purchase
join DIM_Item on DIM_Item.BKey_ItemId = Fact_Purchase.DIM_Item
join DIM_Time on DIM_Time.ID = Fact_Purchase.DIM_Time_DeliveryDate
where Fact_Purchase.DIM_Company = 2 and DIM_Time.ID = FACT_Purchase.DIM_Time_DeliveryDate
Group by dim_item.Name, DIM_Time.Year
Order by Quantity DESC
How do I select top 10 items with the highest quantity through all my years, with only 10 top entities for each year?
As you can guess, the company is individual, and Is going to be a parameter in my report
I think this is what you're going for. My apologies if I messed up on translating your tables across.
select *
from (
select DIM_Time.[Year], dim_item.Name, SUM(Quantity) Quantity, RANK() OVER (PARTITION BY DIM_Time.[Year] ORDER BY SUM(Quantity) DESC) salesrank
from Fact_Purchase
join DIM_Item on DIM_Item.BKey_ItemId = Fact_Purchase.DIM_Item
join DIM_Time on DIM_Time.ID = Fact_Purchase.DIM_Time_DeliveryDate
where Fact_Purchase.DIM_Company = 2 and DIM_Time.ID = FACT_Purchase.DIM_Time_DeliveryDate
group by dim_item.Name, DIM_Time.[Year]
) tbl
where salesrank <= 10
order by [Year], salesrank
The subquery groups by name/year, and the RANK() OVER part sets up a sort of row index that increments by SUM(Quantity) and restarts for each Year. From there you just have to filter out anything with a salesrank (index) that's over 10.
SELECT
_year,
Name,
_SUM,
RANK_iD
FROM
(
SELECT
_year,
Name,
_SUM,
DENSE_RANK()OVER(PARTITION BY _year,_Month ORDER BY _SUM DESC) AS RANK_iD
FROM(
Select
DIM_Time AS _year,
DIM_Item as Name,
sum(Quantity) AS _SUM
from
#ABC
GROUP BY
_year,
Name
)A
)B
WHERE RANK_iD<=10

Resources