SQL select highest sold item from group of items - sql-server

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

Related

Stored procedure in SnowSQL to combine 2 tables and avoid duplicates

DECLARE #tmp1 TABLE (ItemCode INT, Item VARCHAR(30), Qty INT)
INSERT INTO #tmp1 (ItemCode, Item, Qty)
VALUES(1, 'Item1', 300),
(2, 'Item2', 500)
DECLARE #tmp2 TABLE (JobNo INT, ItemCode INT, Item VARCHAR(30), Qty INT)
INSERT INTO #tmp2 (JobNo, ItemCode, Item, Qty)
VALUES(1, 1, 'Item1', 150),
(2, 1, 'Item1', 150),
(3, 2, 'Item1', 50),
(4, 2, 'Item1', 75),
(5, 2, 'Item1', 125),
(6, 2, 'Item1', 100)
;WITH MyCTE AS
(
SELECT t1.ItemCode, t1.Item, t1.Qty, t2.JobNo, t2.ItemCode AS ItemCode2, t2.Item AS Item2, t2.Qty AS Qty2, t2.MySeed
FROM #tmp1 AS t1 INNER JOIN (
SELECT *, ROW_NUMBER() OVER(PARTITION BY ItemCode ORDER BY JobNo) AS [MySeed]
FROM #tmp2
) AS t2 ON t1.ItemCode = t2.ItemCode
WHERE t2.MySeed = 1
UNION ALL
SELECT NULL AS ItemCode, NULL AS Item, NULL AS Qty, t2.JobNo, t2.ItemCode AS ItemCode2, t2.Item AS Item2, t2.Qty AS Qty2, t2.MySeed
FROM #tmp1 AS t1 INNER JOIN (
SELECT *, ROW_NUMBER() OVER(PARTITION BY ItemCode ORDER BY JobNo) AS [MySeed]
FROM #tmp2
) AS t2 ON t1.ItemCode = t2.ItemCode
WHERE t2.MySeed > 1
)
SELECT *
FROM MyCTE
ORDER BY ItemCode2, MySeed
I found a sample query online as above which is straight forward as it combines table 1 and table 2 and it assumes in table 2 if myseed count is 1 then input null values.
But, I need a query in snowflake for the scenario where table 1 can have any number of entries for each id and table 2 can also have multiple entries for each id. I need to join both the tables without duplicates and input null in the rows when the one table has multiple entries and other table don't have multiple entries.
How to write in snowflake(SNOWSQL)?
I need a result like below to combine two tables and avoid duplicates in either one of the table

Selecting Columns from different tables using where in

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();

How to convert two rows into one column using pivot in sql

I want to convert two columns into one row using PIVOT FUNCTION in SQL database.
But I'm facing problems. I can convert 1 column into 1 row but not 2 columns into 1 row.
You can try this
Create Table Article (ArticleId Int, [Description] Varchar(10))
Insert Into Article Values (1, 'Test')
Create Table OrderForecast(ArticleId Int, [Week] Int, [Order] Int, Amount Int)
Insert Into OrderForecast Values (1, 51, 1, 0),(1, 52, 2, 150), (1, 1, 3, 0),(1, 2, 4, 200), (1, 3, 5,0)
Select ArticleId, [Description], Week51, Week52, Week1, Week2, Week3
from
(
select ArticleId, [Description], Amount, [Week]
from
(
SELECT OrderForecast.ArticleId, 'Week' + Convert(Varchar(10), OrderForecast.[Week]) as [Week], [Order], Amount,
Article.[Description] as [Description] FROM OrderForecast
Inner Join Article On OrderForecast.ArticleId = Article.ArticleId
)a
) d
pivot
(
max(Amount)
for [Week] in (Week51, Week52, Week1, Week2, Week3)
) piv;
You can find the demo Here

How to get the Previous & next row based on condition

I am trying to get the statement on fetching the previous and next rows of a selected row.
Declare #OderDetail table
(
Id int primary key,
OrderId int,
ItemId int,
OrderDate DateTime2,
Lookup varchar(15)
)
INSERT INTO #OderDetail
VALUES
(1, 10, 1, '2018-06-11', 'A'),
(2, 10, 2, '2018-06-11', 'BE'), --this
(3, 2, 1, '2018-06-04', 'DR'),
(4, 2, 2, '2018-06-04', 'D'), --this
(5, 3, 2, '2018-06-14', 'DD'), --this
(6, 4, 2, '2018-06-14', 'R');
DECLARE
#ItemId int = 2,
#orderid int = 10
Required output:
Input for the procedure is order id =10 and item id =2 and i need to check item-2 is in the any other order i.e only previous and next item of matched record/order as per order date
Is this what your after? (Updated to reflect edit [OrderDate] to question)
Declare #OderDetail table
(
Id int primary key,
OrderId int,
ItemId int,
OrderDate DateTime2,
Lookup varchar(15)
)
INSERT INTO #OderDetail
VALUES
(1, 10, 1, '2018-06-11', 'A'),
(2, 10, 2, '2018-06-11', 'BE'), --this
(3, 2, 1, '2018-06-04', 'DR'),
(4, 2, 2, '2018-06-04', 'D'), --this
(5, 3, 2, '2018-06-14', 'DD'), --this
(6, 4, 2, '2018-06-14', 'R');
declare #ItemId int=2 , #orderid int = 10;
Query
With cte As
(
Select ROW_NUMBER() OVER(ORDER BY OrderDate) AS RecN,
*
From #OderDetail Where ItemId=#ItemId
)
Select Id, OrderId, ItemId, [Lookup] From cte Where
RecN Between ((Select Top 1 RecN From cte Where OrderId = #orderid) -1) And
((Select Top 1 RecN From cte Where OrderId = #orderid) +1)
Order by id
Result:
Id OrderId ItemId Lookup
2 10 2 BE
4 2 2 D
5 3 2 DD
Another possible approach is to use LAG() and LEAD() functions, that return data from a previous and subsequent row form the same resul tset.
-- Table
DECLARE #OrderDetail TABLE (
Id int primary key,
OrderId int,
ItemId int,
OrderDate DateTime2,
Lookup varchar(15)
)
INSERT INTO #OrderDetail
VALUES
(1, 10, 1, '2018-06-11', 'A'),
(2, 10, 2, '2018-06-11', 'BE'), --this
(3, 2, 1, '2018-06-04', 'DR'),
(4, 2, 2, '2018-06-04', 'D'), --this
(5, 3, 2, '2018-06-14', 'DD'), --this
(6, 4, 2, '2018-06-14', 'R');
-- Item and order
DECLARE
#ItemId int = 2,
#orderid int = 10
-- Statement
-- Get previois and next ID for every order, grouped by ItemId, ordered by OrderDate
;WITH cte AS (
SELECT
Id,
LAG(Id, 1) OVER (PARTITION BY ItemId ORDER BY OrderDate) previousId,
LEAD(Id, 1) OVER (PARTITION BY ItemId ORDER BY OrderDate) nextId,
ItemId,
OrderId,
Lookup
FROM #OrderDetail
)
-- Select current, previous and next order
SELECT od.*
FROM cte
CROSS APPLY (SELECT * FROM #OrderDetail WHERE Id = cte.Id) od
WHERE (cte.OrderId = #orderId) AND (cte.ItemId = #ItemId)
UNION ALL
SELECT od.*
FROM cte
CROSS APPLY (SELECT * FROM #OrderDetail WHERE Id = cte.previousId) od
WHERE (cte.OrderId = #orderId) AND (cte.ItemId = #ItemId)
UNION ALL
SELECT od.*
FROM cte
CROSS APPLY (SELECT * FROM #OrderDetail WHERE Id = cte.nextId) od
WHERE (cte.OrderId = #orderId) AND (cte.ItemId = #ItemId)
Output:
Id OrderId ItemId OrderDate Lookup
2 10 2 11/06/2018 00:00:00 BE
4 2 2 04/06/2018 00:00:00 D
5 3 2 14/06/2018 00:00:00 DD
Update to given this data set: I see where you are going with this. Note that in SOME cases there IS no row before the given one - so it only returns 2 not 3. Here I updated the CTE version. Un-comment the OTHER row to see 3 not 2 as there is then one before the selected row with that Itemid.
Added a variable to demonstrate how this is better allowing you to get 1 before and after or 2 before/after if you change that number (i.e. pass a parameter) - and if less rows, or none are before or after it gets as many as it can within that constraint.
Data setup for all versions:
Declare #OderDetail table
(
Id int primary key,
OrderId int,
ItemId int,
OrderDate DateTime2,
Lookup varchar(15)
)
INSERT INTO #OderDetail
VALUES
(1, 10, 1, '2018-06-11', 'A'),
(2, 10, 2, '2018-06-11', 'BE'), --this
(3, 2, 1, '2018-06-04', 'DR'),
(4, 2, 2, '2018-06-04', 'D'), --this
(5, 3, 2, '2018-06-14', 'DD'), --this
(9, 4, 2, '2018-06-14', 'DD'),
(6, 4, 2, '2018-06-14', 'R'),
--(10, 10, 2, '2018-06-02', 'BE'), -- un-comment to see one before
(23, 4, 2, '2018-06-14', 'R');
DECLARE
#ItemId int = 2,
#orderid int = 2;
CTE updated version:
DECLARE #rowsBeforeAndAfter INT = 1;
;WITH cte AS (
SELECT
Id,
OrderId,
ItemId,
OrderDate,
[Lookup],
ROW_NUMBER() OVER (ORDER BY OrderDate,Id) AS RowNumber
FROM #OderDetail
WHERE
ItemId = #itemId -- all matches of this
),
myrow AS (
SELECT TOP 1
Id,
OrderId,
ItemId,
OrderDate,
[Lookup],
RowNumber
FROM cte
WHERE
ItemId = #itemId
AND OrderId = #orderid
)
SELECT
cte.Id,
cte.OrderId,
cte.ItemId,
cte.OrderDate,
cte.[Lookup],
cte.RowNumber
FROM ctE
INNER JOIN myrow
ON ABS(cte.RowNumber - myrow.RowNumber) <= #rowsBeforeAndAfter
ORDER BY OrderDate, OrderId;
You probably want the CTE method (See an original at the end of this) however:
Just to point out, this gets the proper results but is probably not what you are striving for since it is dependent on the row order and the item id not the actual row with those two values:
SELECT TOP 3
a.Id,
a.OrderId,
a.ItemId,
a.Lookup
FROM #OderDetail AS a
WHERE
a.ItemId = #ItemId
To fix that, you can use an ORDER BY and TOP 1 with a UNION, kind of ugly. (UPDATED with date sort and != on the id.)
SELECT
u.Id,
u.OrderId,
u.OrderDate,
u.ItemId,
u.Lookup
FROM (
SELECT
a.Id,
a.OrderId,
a.OrderDate,
a.ItemId,
a.Lookup
FROM #OderDetail AS a
WHERE
a.ItemId = #ItemId
AND a.OrderId = #orderid
UNION
SELECT top 1
b.Id,
b.OrderId,
b.OrderDate,
b.ItemId,
b.Lookup
FROM #OderDetail AS b
WHERE
b.ItemId = #ItemId
AND b.OrderId != #orderid
ORDER BY b.OrderDate desc, b.OrderId
UNION
SELECT top 1
b.Id,
b.OrderId,
b.OrderDate,
b.ItemId,
b.Lookup
FROM #OderDetail AS b
WHERE
b.ItemId = #ItemId
AND b.OrderId != #orderid
ORDER BY b.OrderDate asc, b.OrderId
) AS u
ORDER BY u.OrderDate asc, u.OrderId
I think its simple, you can check with min(Id) and Max(id) with left outer join or outer apply
like
Declare #ItemID int = 2
Select * From #OderDetail A
Outer Apply (
Select MIN(A2.Id) minID, MAX(A2.Id) maxID From #OderDetail A2
Where A2.ItemId =#ItemID
) I05
Outer Apply(
Select * From #OderDetail Where Id=minID-1
Union All
Select * From #OderDetail Where Id=maxID+1
) I052
Where A.ItemId =#ItemID Order By A.Id
Let me know if this helps you or you face any problem with it...
Regards,

SQL Server - How would I insert a RANK function to rows that are already sorted in ranked order?

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

Resources