I am trying to retrieve data from columns of different tables using Where-In clause but unable to do so. The required task is: "List name, package Weight and units Produced and Batch no of all the products that are expired"
The database looks like this
create table Products(
ProductId int,
ProductName varchar(20) not null,
PackageWeight float not null,
PricePerUnit int not null,
AmountInStore int,
Primary Key(ProductId)
)
insert into Products values(1, 'Chicken Cutlet', 0.5, 500, 600)
insert into Products values(2, 'Chicken Cutlet', 0.25, 250, 0)
insert into Products values(3, 'Chicken Cutlet', 1, 1000, 500)
insert into Products values(4, 'Chicken Cutlet', 5, 4500, 600)
insert into Products values(5, 'Chicken Tenders', 300, 500, 500)
insert into Products values(6, 'Chicken Tenders', 600, 500, 100)
insert into Products values(7, 'Potato Fries', 1, 150, 0)
insert into Products values(8, 'Potato Fries', 5, 750, 800)
create table Production(
ProductId int,
BatchNo char(6) ,
UnitsProduced int not null,
DateOfProduction date not null,
ExpiryDate date not null,
Foreign Key(ProductId) references Products(ProductId),
Primary Key(ProductId, BatchNo)
)
insert into Production values(1, '1-1001', 1000, '2014-01-01', '2014-01-10')
insert into Production values(2, '1-1001', 1000, '2014-01-01', '2014-01-10')
insert into Production values(3, '1-1001', 1000, '2014-02-01', '2014-02-10')
insert into Production values(4, '1-2001', 500, '2014-05-01', '2014-10-30')
insert into Production values(5, '1-2001', 500, '2014-05-10', '2030-11-10')
So far I have this:
select ProductName, PackageWeight from Products where ProductId in(
select ProductId from Products
Intersect
select ProductId from Production where ExpiryDate < GETDATE())
I am getting the correct Product name and package weight because they are both from the same table i.e Products, but how can I also retrieve BatchNo, UnitsProduced from a different table i.e Production in the same query?
I would simply use JOIN:
SELECT P.ProductName, P.PackageWeight, PD.BatchNo, PD.UnitsProduced
FROM Products P
JOIN Production PD ON P.ProductId = PD.ProductId
WHERE ExpiryDate < CONVERT(DATE,GETDATE())
You need to join the tables based on ProductId, try like following query.
If you want to modify your existing query it should be like.
SELECT productname,
packageweight,
prdn.batchno,
prdn.unitsproduced
FROM products prd
INNER JOIN production prdn
ON prd.productid = prdn.productid
WHERE prd.productid IN(SELECT productid
FROM products
INTERSECT
SELECT productid
FROM production
WHERE expirydate < Getdate())
But if you want, you can re-factor your query like following.
SELECT productname,
packageweight,
prdn.batchno,
prdn.unitsproduced
FROM products prd
INNER JOIN production prdn
ON prd.productid = prdn.productid
WHERE expirydate < Getdate()
You can easily achieve this by using sql server views.
Here is how you can do it:
Create a view
-- Note that Table Alias is Used For Query Clarity
-- 'P' => For Product Table
-- 'Pr' => For Production Table
CREATE VIEW viewName
AS
SELECT P.ProductId AS ID,
P.ProductName AS Name,
P.PackageWeight,
P.PricePerUnit,
P.AmountInStore,
Pr.BatchNo,
Pr.ExpiryDate,
Pr.UnitsProduced,
Pr.DateOfProduction
FROM Production AS Pr
INNER JOIN
Products AS P
ON Pr.ProductId = P.ProductId;
Get The Results as follows
SELECT Name,
PackageWeight,
BatchNo,
UnitsProduced
FROM viewName
WHERE ExpiryDate < GETDATE();
Related
I have 2 different tables and I would like an average page count per hour for each employee. I am new to subqueries so I'm still trying to wrap my head around them. This is what I have so far but I need the average page count for each employee but I am getting the
average total.
SELECT
Employee,
((SELECT SUM(Pagecount) FROM Table2) /
(SELECT SUM(Duration) FROM Table1))
FROM Table1;
You should not need a subquery here, rather you would want to JOIN the tables together.
DECLARE #Table1 TABLE (PrepDate DATE, PickupNumber INT, BoxNumber INT, JobType INT, Duration DECIMAL(5, 2), Employee VARCHAR(100), BoxStatus VARCHAR(5));
DECLARE #Table2 TABLE (ScanDate DATE, PickupNumber INT, BoxNumber INT, PageCount INT);
INSERT #Table1 (PrepDate, PickupNumber, BoxNumber, JobType, Duration, Employee, BoxStatus)
VALUES ('20220707', 123, 8, 0, 3.75, 'Jdoe', 'I'),
('20220808', 456, 9, 0, 5.25, 'Msmith', 'C');
INSERT #Table2 (ScanDate, PickupNumber, BoxNumber, PageCount)
VALUES ('20220807', 123, 8, 525),
('20220823', 456, 9, 785);
SELECT t1.Employee, (t2.PageCount / t1.Duration) AS AvgPageCount
FROM #Table1 t1
INNER JOIN #Table2 t2 ON t2.PickupNumber = t1.PickupNumber
AND t2.BoxNumber = t1.BoxNumber;
This will produce:
Employee | AvgPageCount
-------------+-----------------
Jdoe | 140.000000
Msmith | 149.523809
i have bellow two tables
so i need to ge the TOP 1 item with the highest totalsales for each item group
i have written the following query sum up total sales from each item but not sure how move from here
select IM.ItemCode, IM.ItemName, IM.ItemGroup, SUM(ID.LineTotal) as TotalSales
from InvoiceDetail ID
inner join ItemMaster IM
on IM.ItemCode = ID.ItemCode
group by IM.ItemCode, IM.ItemName, IM.ItemGroup
sample data as text
CREATE TABLE InvoiceHeader
(InvoiceNo varchar(255),
CustCode varchar(255),
InvoiceDate date,
primary key (InvoiceNo))
CREATE TABLE CustomerMaster
(CustCode varchar(255),
CustomerName varchar(255),
primary key (CustCode))
CREATE TABLE InvoiceDetail
(InvoiceNo varchar(255),
ItemCode INT,
Quantity INT,
SalesPrice INT,
LineTotal INT,
foreign key (InvoiceNo) References InvoiceHeader(InvoiceNo))
CREATE TABLE ItemMaster
(ItemCode INT,
ItemName varchar(255),
ItemGroup varchar(255))
INSERT INTO InvoiceHeader(InvoiceNo, CustCode, InvoiceDate) VALUES
('INV001', 'A001', '2015-01-01'),
('INV002', 'B001', '2015-03-02'),
('INV003', 'B001', '2015-03-05')
INSERT INTO CustomerMaster(CustCode, CustomerName) VALUES
('A001', 'ABC Pte Ltd'),
('B001', 'CDE Pte Ltd')
INSERT INTO ItemMaster(ItemCode, ItemName, ItemGroup) VALUES
(1001, 'Laptop A1', 'Computer'),
(1002, 'PC A1', 'Computer'),
(1003, 'Mouse', 'Accessories'),
(1004, 'Keyboard', 'Accessories')
INSERT INTO InvoiceDetail(InvoiceNo, ItemCode, Quantity,SalesPrice, LineTotal) VALUES
('INV001', 1001, 2, 3000, 6000),
('INV001', 1003, 2, 25, 50),
('INV001', 1004, 1, 30, 30),
('INV002', 1002, 10, 2500, 25000),
('INV002', 1003, 20, 20, 400),
('INV003', 1001, 1, 3000, 3000),
('INV003', 1002, 1, 2500, 2500),
('INV003', 1003, 1, 20, 20)
You can just add a ROW_NUMBER() window function to your query and then select the rows with the #1 row for each category:
If for some reason you would have identical sales amounts for a category for the period and you need all of the rows you could replace ROW_NUMBER with DENSE_RANK().
SELECT *
FROM
(
select IM.ItemCode, IM.ItemName, IM.ItemGroup, SUM(ID.LineTotal) as TotalSales,
ROW_NUMBER() OVER(PARTITION BY IM.ItemGroup ORDER BY SUM(ID.LineTotal) desc) RN
from InvoiceDetail ID
inner join ItemMaster IM
on IM.ItemCode = ID.ItemCode
group by IM.ItemCode, IM.ItemName, IM.ItemGroup
) T1
WHERE T1.RN = 1
INNER JOIN Sales_Category c1 ON c1.MPCID = c1.Category_Id
WHERE Date_of_Purchase > DATEADD(d, -90, getdate())
ORDER BY c1.MPCID
Table1-OrderDetails contains fields=Bill_To_Id,MPC,Order_value,Category_Id
Table2-Sales_Category contains fields = `MPCID
I am still not clear on if you want the Category_Id in your select query results to come from Sales_Category rather than OrderDetails or if you actually want to update the data in OrderDetails with the corresponding value in Sales_Category. Therefore, I will provide both and you can take it from there. I will warn you to be careful when running update statements because you are actually altering the data.
create table OrderDetails
(
OrderLine_Id int
, OrderId int
, Bill_to_Id int
, Date_of_Purchase date
, MPC int
, Order_Value int
, Category_Id int
)
create table Sales_Category
(
Category_Id int
, MPC int
)
insert into OrderDetails values
(4, 711, 8566, '2018-05-11', 5450, 10000, null)
, (8, 5555, 8123, '2020-03-11', 6700, 20000, null)
, (103, 456, 123456, '2019-02-05', 71883, 30000, null)
, (1, 123, 67999, '2020-02-08', 7899, 40000, null)
, (2, 678, 9913, '2020-01-11', 9908, 50000, null)
, (3, 488, 98564, '2020-02-14', 999, 60000, null)
, (null, null, null, null, null, null, null)
insert into Sales_Category values
(1, 5450)
, (2, 6700)
, (3, 9908)
-- OrderDetails populated with sample data
select * from OrderDetails
-- Sales_Category populated with sample data
select * from Sales_Category
-- this will get Category_Id from Sales_Category rather than OrderDetails
select
od.Bill_to_Id
, od.MPC
, od.Order_Value
, sc.Category_Id
from OrderDetails od
left join Sales_Category sc on od.MPC = sc.MPC
where od.Date_of_Purchase > DATEADD(d, -90, getdate())
-- this shows nothing has actually been updated yet
select * from OrderDetails
update OrderDetails
set Category_Id = sc.Category_Id
from OrderDetails od
inner join Sales_Category sc on od.MPC = sc.MPC
where od.Date_of_Purchase > DATEADD(d, -90, getdate())
-- this shows that Category_Id in OrderDetails has been updated where the
-- MPC values match and the Date_of_Purchase is within the last 90 days
select * from OrderDetails
Here is the demo of this code. This is the type of thing you should provide when asking a question to make is as easy as possible for someone to help you.
(TransactionID, UserID, Type, OrderNumber, Amount)
1, 123, Purchase, 100, 100
2, 123, Refund, 100, 100
3, 234, Purchase, 101, 100
4, 345, Purchase, 102, 100
I want to create a query that will return all transactions, grouped by a userID that don't currently have a return for the given OrderNumber.
I need to make a refund transaction for every user, and I don't want to make a refund if one already exists.
Is it possible to order and group such that I can find those transactions?
You could use exists logic here:
SELECT t1.*
FROM yourTable t1
WHERE NOT EXISTS (SELECT 1 FROM yourTable t2
WHERE t2.UserID = t1.UserID AND t2.Type = 'Refund');
The logic of the above query reads simply as return all transaction records for a given user where we cannot find a single record belonging to the same user which was a refund.
You can also group records like this using conditional aggregation.
create table Transactions (TransactionID int, UserID int, [Type] varchar(20)
, OrderNumber int, Amount int)
insert into Transactions values
(1, 123, 'Purchase', 100, 100),
(2, 123, 'Refund', 100, 100),
(3, 234, 'Purchase', 101, 100),
(4, 345, 'Purchase', 102, 100)
Select UserId
, max(CASE WHEN [Type] ='Purchase' then Amount ELSE 0 END) as PurchaseAmount
, max(CASE WHEN [Type] ='Refund' then Amount ELSE 0 END) as RefundAmount
from Transactions
group by UserID
Live db<>fiddle demo.
With the tables suggested by #Suraj, the following SQL should give you the refund amount by user
select pur.UserID,
RefundAmount = sum(pur.Amount - Isnull(refund.Amount, 0))
from Transactions pur
left join Transactions refund
on pur.UserID = refund.UserID
and pur.OrderNumber = refund.OrderNumber
and refund.Type = 'Refund'
where pur.Type = 'Purchase'
group by pur.UserID
So, apparently, I have everything right according to my professor except for one column that shows the rank of the columns shown in the code below. I'm thinking that, essentially, it just has to show the row numbers off to the left side in its own column. Here are the instructions:
The sales manager would now like you to create a report that ranks her
products by both their total sales and total sales quantity (each will
be its own column). Create a stored procedure that returns the
following columns but also with the two new rank columns added.
Product Name | Orders Count | Total Sales Value | Total Sales
Quantity
I know that it doesn't have that extra column in the assignment description, but I guess I need it. Here is what I have so far:
USE OnlineStore
GO
CREATE PROC spManagerProductSalesCount
AS
BEGIN
SELECT
P.Name AS 'Product Name',
Isnull(Count(DISTINCT O.OrderID), 0) AS 'Orders Count',
Sum(Isnull(O.OrderTotal, 0)) AS 'Total Sales Value',
Sum (Isnull(OI.OrderItemQuantity, 0)) AS 'Total Sales Quantity'
FROM
Product P
INNER JOIN
OrderItem OI ON P.ProductID = OI.ProductID
INNER JOIN
Orders O on O.OrderID = OI.OrderID
GROUP BY
P.Name
ORDER BY
'Total Sales Value' DESC, 'Total Sales Quantity' DESC
END
Update: It does need to be in a stored procedure and CTEs can/should be used. I could use some help with the CTEs. Those are pretty difficult for me.
This is just the select part of the stored proc but it should show you what to do:
declare #products table
(
Name varchar(50),
id int
)
declare #orderitems table
(
id int,
orderid int,
productid int,
orderitemquantity int
)
declare #orders table
(
orderid int,
ordertotal decimal(18,2)
)
insert into #products VALUES ('apple', 1)
insert into #products VALUES ('orange', 2)
insert into #products VALUES ('pear', 3)
insert into #products VALUES ('melon', 4)
insert into #orders values(1, 19.0)
insert into #orders values(2, 25.5)
insert into #orders values(3, 9.5)
insert into #orders values(4, 13.5)
insert into #orders values(5, 8.5)
insert into #orderitems VALUES(1, 1, 1, 20)
insert into #orderitems VALUES(2, 1, 2, 10)
insert into #orderitems VALUES(3, 2, 3, 5)
insert into #orderitems VALUES(4, 2, 4, 4)
insert into #orderitems VALUES(5, 3, 1, 10)
insert into #orderitems VALUES(6, 3, 2, 5)
insert into #orderitems VALUES(7, 4, 3, 3)
insert into #orderitems VALUES(8, 4, 4, 2)
insert into #orderitems VALUES(9, 5, 1, 5)
insert into #orderitems VALUES(10, 5, 4, 2)
;WITH summary as
(
SELECT p.Name as ProductName,
COUNT(o.orderid) as 'Orders Count',
ISNULL(Sum(o.ordertotal),0) AS 'Total Sales Value',
ISNULL(Sum(oi.orderitemquantity),0) AS 'Total Sales Quantity'
FROM #products p
INNER JOIN #orderitems oi on oi.productid = p.id
INNER JOIN #orders o on o.orderid = oi.orderid
GROUP BY p.Name
)
SELECT ProductName, [Orders Count], [Total Sales Value], [Total Sales Quantity],
RANK() OVER (ORDER BY [Total Sales Value] DESC) AS ValueRanking,
RANK() OVER (ORDER BY [Total Sales Quantity] DESC) AS QuantityRanking FROM summary
Notice a few things here. This code can be cut and pasted into a Management Studio query window and run as such. It starts with some table declarations and insert of sample data. When asking a question it is always useful if you do this part of the work; people are much more likely to answer, if the most boring bit is done!
COUNT() does not need ISNULL protection; it returns 0, if there are no values.
Given the final data, you will see that the ValueRanking and QuantityRankings are different (I fiddled the data to get this, just to illustrate the point). What this means is that the final result can only be ordered by one of them (or indeed by any other column - order by is not dependent on ranking).
HTH