Joining over an interim table SQL Server - sql

I have a table of rules for pricing. I am retrieving the max discount for each ProductTypeID, which indicates which type a product is, using this query :
SELECT MAX(discount) as BiggestDiscount, ProductTypeID FROM dbo.SellingPriceRules
WHERE ProductTypeID is not null
GROUP by ProductTypeID
ORDER BY ProductTypeID
This works perfectly, however I need to expand on this and, for a list of ProductIDs retrieve my biggest discount. So I need to find what ProductTypeID each ProductID belongs to and check my SellPriceRules database for the max discount for this ProductTypeID.
So, in my Discounts table, I have :
ProductID, Margin
And in my Products Table I have :
ProductID, ProductTypeID
In order to get the ProductTypeID of each product, I have :
select * from Discounts m
INNER JOIN Product p on p.ProductID = m.ProductID
WHERE ProductTypeID is not null
I am now struggling with joining these two queries together. I simply want to get the max discount for each product in the discounts table and subtract this from my margin. How can I join these two retirevals together?
Thanks very much

You have all the logic correct. You just need the syntax of embedding one query inside another.
SELECT
p.ProductID,
p.ProductTypeID,
m.Margin,
d.BiggestDiscount,
m.Margin - d.BiggestDiscount AS AdjustedMargin
FROM Product p
INNER JOIN Discounts m ON (p.ProductID = d.ProductID)
INNER JOIN (
SELECT
ProductTypeID,
MAX(discount) as BiggestDiscount
FROM SellingPriceRules
GROUP BY ProductTypeID
) d ON (p.ProductTypeID = d.ProductTypeID)
WHERE p.ProductID IS NOT NULL

Generally, you can use CTE in such situations. Something like this:
;WITH current_discounts (BiggestDiscount, ProductTypeID)
AS (
SELECT MAX(discount) as BiggestDiscount, ProductTypeID FROM dbo.SellingPriceRules
WHERE ProductTypeID is not null
GROUP by ProductTypeID
ORDER BY ProductTypeID
)
SELECT
m.ProductID,
m.Margin - c.BiggestDiscount
FROM Discounts m
INNER JOIN Product p ON p.ProductID = m.ProductID
INNER JOIN current_discounts c ON p.ProductTypeID = c.ProductTypeID

You can try something like this :
select *, Margin-BiggestDiscount from Discounts m
INNER JOIN Product p on p.ProductID = m.ProductID AND p.ProductTypeID is not null
inner join (
SELECT MAX(discount) as BiggestDiscount, ProductTypeID
FROM dbo.SellingPriceRules
GROUP by ProductTypeID) as r on p.ProductTypeID = r.ProductTypeID

Use correlated subquery
SELECT m.ProductID, m.Margin, p.ProductTypeID,
m.Margin - (SELECT MAX(discount)
FROM dbo.SellingPriceRules
WHERE ProductTypeID = p.ProductTypeID)
FROM Discounts m INNER JOIN Product p on p.ProductID = m.ProductID
WHERE p.ProductTypeID IS NOT NULL
The execution plan especially for #Annon

Related

Using MS Access SQL - how would I select the top 5 food items by group (restaurant) based on the number of orders?

Relationship Diagram
What I want to be able to do is return a query that shows the top 5 items/products on the menu for each of the 3 restaurants in the dataset. I've attached an example of the relationship diagram for some more context. The columns I would like to see in the query are:
RestaurantName
ItemName
NumberofOrders (alias column)
This is what I have at the moment but it doesn't work as expected for the top 5.
SELECT RestaurantName, ItemName, COUNT(Orders.OrderNumber) AS NumberofOrders
FROM ((((Restaurants INNER JOIN
Orders ON Restaurants.RestID = Orders.RestID) INNER JOIN
OrderDetails ON Orders.OrderNumber=OrderDetails.OrderNumber) INNER JOIN
Products ON OrderDetails.ItemID = Products.ItemID) INNER JOIN
FoodType ON Products.ProdTypeID = FoodType.ProdType)
WHERE ItemName IN
(SELECT TOP 5 ItemName
FROM Products
WHERE ItemName IS NOT NULL)
GROUP BY RestaurantName, ItemName
ORDER BY COUNT(Orders.OrderNumber) DESC;
This just repeats the same 5 items across all the restaurants. Any point in the right direction would be awesome.
EDIT 1:
Based on a response I got yesterday, I have made some amendments to the code. This the query is returning the full list, as though ignoring the top 5 in the subquery. I can see all the items are sorted by Total Orders (I have also changed the formula for this) Any ideas what I am doing wrong here?
SELECT RestaurantName, ItemName, SUM(Quantity)*COUNT(Orders.OrderNumber) AS TotalOrders
FROM ((((Restaurants INNER JOIN
Orders ON Restaurants.RestID = Orders.RestID) INNER JOIN
OrderDetails ON Orders.OrderNumber=OrderDetails.OrderNumber) INNER JOIN
Products ON OrderDetails.ItemID = Products.ItemID) INNER JOIN
FoodType ON Products.ProdTypeID = FoodType.ProdType)
WHERE ItemName IN
(SELECT TOP 5 p2.ItemName
FROM Products AS p2
WHERE p2.ItemName = Products.ItemName
GROUP BY p2.ItemName
ORDER BY COUNT(*) DESC)
GROUP BY RestaurantName, ItemName
ORDER BY RestaurantName, SUM(Quantity) DESC;
Thanks
You want a correlated subquery:
WHERE ItemName IN (SELECT TOP 5 p2.ItemName
FROM Products as p2
WHERE p2.RestaurantName = products.RestaurantName
GROUP BY p2.ItemName
ORDER BY COUNT(*) DESC
)
It seems really odd to me that a table called products would have a column called RestaurantName. But you claim that your query works and it has the same reference.
Your filter in the outer WHERE is only using the ItemName field. For your purpose it should contain both fields.
Like so:
SELECT
RestaurantName,
ItemName,
COUNT(Orders.OrderNumber) AS NumberofOrders
FROM Restaurants
INNER JOIN Orders ON Restaurants.RestID = Orders.RestID
INNER JOIN OrderDetails ON Orders.OrderNumber=OrderDetails.OrderNumber
INNER JOIN Products ON OrderDetails.ItemID = Products.ItemID
INNER JOIN FoodType ON Products.ProdTypeID = FoodType.ProdType
WHERE (RestaurantName, ItemName) IN
(SELECT TOP 5 RestaurantName, ItemName
FROM Products
WHERE RestaurantName IS NOT NULL)
GROUP BY RestaurantName, ItemName
ORDER BY COUNT(Orders.OrderNumber) DESC;

How to multiply 2 columns and then SUM the results?

I'm learning SQL and I couldn't find the solution to my problem anywhere on he forums. Anyway, I'm using a www.dofactory.com SQL Sandbox and I've made a query:
select customer.LastName, Product.ProductName, OrderItem.Quantity, Product.UnitPrice, OrderItem.Quantity*Product.UnitPrice AS "Quantity x UnitPrice"
FROM Customer
JOIN [Order] ON [Order].CustomerID = Customer.ID
JOIN OrderItem ON OrderItem.OrderId = [Order].ID
JOIN Product ON Product.Id = OrderItem.ProductId
where
[Order].TotalAmount = (select max([Order].TotalAmount) FROM [Order])
And result:
LastNam ProductName Quantity UnitPrice Quantity x UnitPrice
Kloss Côte de Blaye 60 263.50 15810.00
Kloss Chartreuse verte 80 18.00 1440.00
Now I want to SUM the whole Column "Quantity x UnitPrice". What should I do?
You use SUM(). Something like this:
SELECT SUM(oi.Quantity * oi.UnitPrice) AS Summary
FROM Customer c JOIN
[Order] o
ON o.CustomerID = c.ID JOIN
OrderItem oi
ON oi.OrderId = o.ID JOIN
Product p
ON p.Id = oi.ProductId
WHERE o.TotalAmount = (select max(o2.TotalAmount) FROM [Order] o2);
Note the use of table aliases. They make the query easier to write and to read.
This is SQL Server (T-SQL). You can use SUM and GROUP BY like so. I've done it in a subquery, which is how I'd approach this sort of thing.
select LastName, ProductName, Quantity, UnitPrice, SUM(Summary)
from (
select customer.LastName, Product.ProductName, OrderItem.Quantity, Product.UnitPrice, OrderItem.Quantity*OrderItem.UnitPrice AS Summary, [Order].TotalAmount
FROM Customer
JOIN [Order] ON [Order].CustomerID = Customer.ID
JOIN OrderItem ON OrderItem.OrderId = [Order].ID
JOIN Product ON Product.Id = OrderItem.ProductId
where
[Order].TotalAmount = (select max([Order].TotalAmount) FROM [Order])) x
group by LastName, ProductName, Quantity, UnitPrice

Self join and inner join to remove duplicates

I am stuck on this and I am relatively new to SQL.
Here is the question we were given:
List the productname and vendorid for all products that we have
purchased from more than one vendor (Hint: you’ll need a Self-Join and
an additional INNER JOIN to solve, don't forget to remove any
duplicates!!)
Here is a screenshot of tables we are working with:
Here is what I have.....I know it is wrong. It works to a degree, just not exactly how the prof wants it.
SELECT DISTINCT productname, product_vendors.vendorid
FROM products INNER JOIN Product_Vendors
ON products.PRODUCTNUMBER = PRODUCT_VENDORS.PRODUCTNUMBER
INNER JOIN vendors ON Product_Vendors.VENDORID = vendors.VENDORID
ORDER BY products.PRODUCTNAME;
Expected output provided the prof:
I agree with #jarlh that additional information would be helpful- i.e. are there triplicates in the data or just duplicates, etc.
That said, this should get your started
SELECT
c.productname AS 'Product'
,a.vendorid AS 'Vendor1'
,b.vendorid AS 'Vendor2'
FROM
product_vendors AS a
JOIN
product_vendors AS b
ON
a.productnumber = b.productnumber
AND a.vendorid <> b.vendorid
JOIN
dbo.products AS c
ON
a.productnumber = c.productnumber
This will limit the population of 'Product Vendors' just to products with unmatching vendors.
From there you are joining to products to pull back product name.
Also- work on coding format, clean code makes the dream work :)
The solution to this problem is usually to count vendors per product with COUNT OVER and only stick with products with more than one. Simply:
select productname, vendorid
from
(
select
p.productname,
pv.vendorid,
count(*) over (partition by product) as cnt
from products p
join product_vendors pv using (productnumber)
)
where cnt > 1;
If this shall be done without window functions, then one option is to aggregate product_vendors and use this result:
select p.productname, pv.vendorid
from
(
select productid
from product_vendors
group by productname
having count(*) > 1
) px
join products p using (productid)
join product_vendors pv using (productid);
or check whether exists another vendor for the product:
select
p.productname,
pv.vendorid,
count(*) over (partition by product) as cnt
from products p
join product_vendors pv on pv.productnumber = p.productnumber
where exists
(
select *
from product_vendors other
where other.productnumber = pv.productnumber
and other.vendorid <> pv.vendorid
);
In neither of these approaches I see the need to eliminate duplicates, as there should be one row per product in products and one row per product and vendor in product_vendors. So I guess what your prof was thinking of is:
select distinct
p.productname,
pv.vendorid
from products p
join product_vendors pv on pv.productnumber = p.productnumber
join product_vendors other on other.productnumber = pv.productnumber
and other.vendorid <> pv.vendorid
This, however, is an approach I don't recommend. You'd combine all vendors for a product (e.g. with 10 vendors for one product you already have 45 combinations for that product only, if I'm not mistaken). So you'd create a large intermediate result only to dismiss most of it with DISTINCT later. Don't do that. Remember: SELECT DISTINCT is often an indicator for a poorly written query (i.e. unnecessary joins leading to too many combinations you are not actually interested in).
SELECT DISTINCT p.name AS product, v.id
FROM products p
INNER JOIN product_vendors pv ON p.id = pv.productid
INNER JOIN product_vendors pv2 ON pv.productid = pv2.productid AND pv.vendorid != pv2.vendorid
INNER JOIN vendors v ON v.id = pv.vendorid
ORDER BY p.name

sql issue to query product name

I am new to SQL , I have been looking for on line resources but have not find anything yet to solve my problem. Basically i have three tables below:
ORDERTBL(ORDERID,ORDERDATE,ORDERSTATUS),
PRODUCT(PRODUCTID,PRODUCT_NAME),
ORDERLINKPRODUCTS(ORDERID,PRODUCTID,QUANTITY,ORDERLINKPRODUCTID)
I want to get all the product name from the products tables for a specific order which i should query from ORDERLINKPRODUCTS since it contains all the orders.
for instance please find test data available in 3 tables:
Table ORDERTBL:
ORDERID=1,ORDERDATE= 24May, ORDERSTATUS=Process
Table PRODUCTS:
PRODUCID =1 PRODUCT_NAME = spoon PRODUCID =2 PRODUCT_NAME = soap
Table :ORDERLINKPRODUCTS:
ORDERID=1 PRODUCTID = 1 QUANTITY=3 ORDERLINKPRODUCTID=1 ORDERID=1 PRODUCTID = 2 QUANTITY=1 ORDERLINKPRODUCTID=2
I am trying to make a select statements that display all the product name of an order. For instance display all product name of orderid=1 which will return spoon,soap.
Any suggestion how to do it please?
Thanks in advance.
You should use JOIN keyword in SQL to join the three tables to be able to access PRODUCTNAME and show that table data. More about Join clause
SELECT prod.PRODUCT_NAME
FROM ORDERTBL ord
INNER JOIN ORDERLINKPRODUCTS link ON ord.ORDERID = link.ORDERID
INNER JOIN PRODUCT prod ON link.PRODUCTID = prod.PRODUCTID
WHERE ord.ORDERID = 1
SQLfiddle Demo
You can find orders with products ilsted using this
SELECT O.ORDERID, RTRIM(
XMLAGG(XMLELEMENT(e, P.PRODUCT_NAME ||',') ORDER BY O.ORDERID).EXTRACT('//text()'), ',') as Products
FROM ORDERTBL O
INNER JOIN ORDERLINKPRODUCTS L
ON O.ORDERID = L.ORDERID
INNER JOIN PRODUCT P
ON L.PRODUCTID = P.PRODUCTID
GROUP BY O.ORDERID
and products listed with its quantity using this:
SELECT O.ORDERID, RTRIM(
XMLAGG(XMLELEMENT(e, P.PRODUCT_NAME || '(' || L.QUANTITY || '),') ORDER BY O.ORDERID).EXTRACT('//text()'), ',') as Products
FROM ORDERTBL O
INNER JOIN ORDERLINKPRODUCTS L
ON O.ORDERID = L.ORDERID
INNER JOIN PRODUCT P
ON L.PRODUCTID = P.PRODUCTID
GROUP BY O.ORDERID
You can add WHERE clause clause to filted orders as you whish.
SQL FIDDLE DEMO

SQL: Using a subquery to compare Averages

I've been trying to set up this query.
SELECT P.ProductNo,P.Description, AVG(OrderLine.ActualPrice)
FROM Product P
INNER JOIN OrderLine
ON OrderLine.ProductNo = P.ProductNo
WHERE AVG(ActualPrice) >
(SELECT AVG(ActualPrice)
FROM OrderLine)
My goal here is to compare the average actual price for a product to the average price of all products together. But I don't seem to be getting it to work. Where am I going wrong here?
use GROUP BY
when using aggregated condition, use HAVING instead of WHERE
query,
SELECT P.ProductNo, P.Description, AVG(OrderLine.ActualPrice)
FROM Product P
INNER JOIN OrderLine
ON OrderLine.ProductNo = P.ProductNo
GROUP BY P.ProductNo, P.Description
HAVING AVG(ActualPrice) > (SELECT AVG(ActualPrice) FROM OrderLine)