How to show the count of all items in cross joined table in SQL Server - sql

I have a table that has all Items in the inventory, table called CI
CI has 2 columns (ProdID and Price), and it looks like this
ProdID Price
-------------
A8373 700
G8745 900
J7363 300
K7222 800
Y6311 350
I have another table for documents called Docs with columns DocID, CustID and InvoiceID.
DocID, CustID, InvoiceID
------------------------
1 1001 751
2 1001 752
3 1001 753
4 1002 831
5 1002 832
6 1003 901
7 1003 902
Another table for purchases called Purchase with DocID, ProdID, ProdSize.
In the same invoice, ProdID can be repeated as it can be in different sizes
DocID, ProdID, ProdSize
------------------------
1 A8373 41
1 A8373 42
1 A8373 43
1 G8745 35
1 G8745 36
2 A8373 44
2 A8373 45
Now I want to get the quantity of of products for all customer and invoice, but for highest priced products
So it should be like this
CustID, InvoiceID, ProdID, Quantity
-----------------------------------
1001 751 A8373 3
1001 751 G8745 2
1001 751 K7222 0
1001 752 A8373 2
1001 752 G8745 0
1001 752 K7222 0
and to show 0 for the products that do not exist in that invoice
I wrote this query, but it is extremely slow. I wonder if there is an easier fast way to get this results
DECLARE #Features AS TABLE
(
CustID varchar(100),
InvoiceID varchar(100)
INDEX IX3 CLUSTERED(CustID, InvoiceID),
ProdID varchar(100),
Quantity bigint
)
INSERT INTO #Features (CustID, InvoiceID, ProdID, Quantity)
SELECT
R.CustID, R.InvoiceID, T.ProdID, COUNT(*) AS Quantity
FROM
Docs R
CROSS JOIN
(SELECT TOP 1000 * FROM CIs ORDER BY Price DESC) C
INNER JOIN
Purchase T ON T.DocID = R.DocID
GROUP BY
R.CustID, R.InvoiceID, T.ProdID
SELECT TOP 100 *
FROM #Features
ORDER BY CustID, InvoiceID, ProdID
SELECT COUNT(*) FROM #Features
UPDATE F
SET Quantity = Cnt
FROM #Features F
INNER JOIN
(SELECT R.CustID, R.InvoiceID, COUNT(*) Cnt
FROM Purchase T
INNER JOIN Docs R ON T.DocID = R.DocID
GROUP BY R.CustID, R.InvoiceID ) X ON F.CustID = X.CustID
AND F.InvoiceID = X.InvoiceID
SELECT * FROM #Features

here is a way to do this. I filter out the 1000 products first and then perform the join as follows..
Also there isn't a need for update query, all could be obtained in the SQL itself.
Filter early join late
with top_product
as (select prodid,price, rownumber() over(order by price desc) as rnk
from ci
)
,invoice_product
as(select d.docid,d.custid,d.invoiceid,p.prodid
from top_product
join docs d
on 1=1
and rnk<=1000
)
select a.CustID, a.InvoiceID, a.ProdID,count(b.prodid) as qty
from invoice_product a
left join purchase b
on a.DocID=b.docid
and a.ProdID=b.prodid
group by a.CustID, a.InvoiceID, a.ProdID

You can use the DENSE_RANK as follows:
select CustID, InvoiceID, ProdID, sum(qty) as qty
from (select d.CustID, d.InvoiceID, ci.ProdID, p.prodid as qty,
dense_rank() over (order by ci.price desc) as rn
from ci cross join docs d
left join purchase p on d.docid = p.docid and ci.prodid = p.prodid) t
where rn <= 1000
group by CustID, InvoiceID, ProdID

Can you please try following SQL Select statement where I used Common Table Expression SQL CTEs
with topproducts as (
select top 3 ProdID from CI order by Price desc
), sales as (
select
CustID,
InvoiceID,
ProdId,
count(ProdId) as cnt
from (
select
d.CustID,
d.InvoiceID,
p.ProdId
from Docs d
inner join Purchase p
on p.DocID = d.DocID
where p.ProdId in (select ProdId from topproducts)
) t1
group by
CustID,
InvoiceID,
ProdId
)
select
t.*, isnull(ss.cnt,0) as Qty
from (
select
distinct s.CustID, s.InvoiceID, p.ProdId
from sales s, topproducts p
) t
left join sales ss on ss.InvoiceID = t.InvoiceID and ss.ProdId = t.ProdId

Related

How to get the most sold Product in PostgreSQL?

Given a table products
pid
name
123
Milk
456
Tea
789
Cake
...
...
and a table sales
stamp
pid
units
14:54
123
3
15:02
123
9
15:09
456
1
15:14
456
1
15:39
456
2
15:48
789
12
...
...
...
How would I be able to get the product(s) with the most sold units?
My goal is to run a SELECT statement that results in, for this example,
pid
name
123
Milk
789
Cake
because the sum of sold units of both those products is 12, the maximum value (greater than 4 for Tea, despite there being more sales for Tea).
I have the following query:
SELECT DISTINCT products.pid, products.name
FROM sales
INNER JOIN products ON sale.pid = products.pid
INNER JOIN (
SELECT pid, SUM(units) as sum_units
FROM sales
GROUP BY pid
) AS total_units ON total_units.pid = sales.pid
WHERE total_units.sum_units IN (
SELECT MAX(sum_units) as max_units
FROM (
SELECT pid, SUM(units) as sum_units
FROM sales
GROUP BY pid
) AS total_units
);
However, this seems very long, confusing, and inefficient, even repeating the sub-query to obtain total_units, so I was wondering if there was a better way to accomplish this.
How can I simplify this? Note that I can't use ORDER BY SUM(units) LIMIT 1 in case there are multiple (i.e., >1) products with the most units sold.
Thank you in advance.
Since Postgres 13 it has supported with ties so your query can be simply this:
select p.pId, p.name
from sales s
join products p on p.pid = s.pid
group by p.pId, p.name
order by Sum(units) desc
fetch first 1 rows with ties;
See demo Fiddle
Solution for your problem:
WITH cte1 AS
(
SELECT s.pid, p.name,
SUM(units) as total_units
FROM sales s
INNER JOIN products p
ON s.pid = p.pid
GROUP BY s.pid, p.name
),
cte2 AS
(
SELECT *,
DENSE_RANK() OVER(ORDER BY total_units DESC) as rn
FROM cte1
)
SELECT pid,name
FROM cte2
WHERE rn = 1
ORDER BY pid;
Working example: db_fiddle link

Display average quantity of product in warehouses using record type pl/sql

I need to display a list of product names where the ID is between 100-105, with the average quantity in all warehouses, and the name of the warehouse that has the most product quantity (for each product). The only problem is that the average (f_avg) is not working, instead, f_avg is displaying the greatest quantity of the product.
The output example is:
Kingston HyperX Predator average product: 173 mostly found in San Francisco
Intel Xeon E5-2687W V4 average product: 90 mostly found in Toronto
EVGA 12G-P4-3992-KR average product: 178 mostly found in San Francisco
This is my code, anyone know what's wrong with the average and i also got an error:
ORA-00979: not a GROUP BY expression ORA-06512: at line 3
ORA-06512: at line 14
ORA-06512: at "SYS.DBMS_SQL", line 1721
It will be much better if you don't change the structure of the code, but the most important thing is, i need to use record type
DECLARE
CURSOR cur_emp_detail IS
SELECT pproduct_name, avg_quantity, (SELECT w.warehouse_name FROM warehouses w WHERE w.warehouse_id = (SELECT i.warehouse_id FROM inventories i join products p on p.product_id = i.product_id HAVING i.quantity = MAX (i.quantity))) as ware
FROM (SELECT p.product_name as pproduct_name, AVG (i.quantity) AS avg_quantity
FROM products p JOIN inventories i ON p.product_id = i.product_id
WHERE (p.product_id BETWEEN 100 AND 105)
GROUP BY p.product_name);
TYPE type_record_type IS RECORD (
emp_f_name products.product_name%TYPE,
emp_avg inventories.quantity%type,
emp_wh warehouses.warehouse_name%type);
emp_rec_type type_record_type;
BEGIN
OPEN cur_emp_detail;
LOOP
FETCH cur_emp_detail INTO emp_rec_type;
EXIT WHEN cur_emp_detail%NOTFOUND;
dbms_output.Put_line(emp_rec_type.emp_f_name||' average product: '||emp_rec_type.emp_avg||' mostly found in '||emp_rec_type.emp_wh);
END LOOP;
CLOSE cur_emp_detail;
END;
/
I'd suggest a structured approach using subqueries as follow
1) subquery to produce detail data: product, warehouse and quantity
Note that I assume the table inventories has a primary key product_id, warehouse_id, if not you must additionaly aggregate the quantity with grouping by product_id, warehouse_id
SELECT p.product_name, w.warehouse_name, i.quantity
FROM products p
JOIN inventories i ON p.product_id = i.product_id
LEFT OUTER JOIN warehouses w ON w.warehouse_id = i.warehouse_id
WHERE (p.product_id BETWEEN 100 AND 105);
PRODUCT_ WAREHOUSE_NAM QUANTITY
-------- ------------- ----------
Kingston San Francisco 173
EVGA Toronto 178
Intel Toronto 70
Intel Toronto 90
Kingston Toronto 173
2) calculate the AVG quantity and the warehouse rank - using analytic functions
with prd as (
SELECT p.product_name, w.warehouse_name, i.quantity
FROM products p
JOIN inventories i ON p.product_id = i.product_id
LEFT OUTER JOIN warehouses w ON w.warehouse_id = i.warehouse_id
WHERE (p.product_id BETWEEN 100 AND 105)
),
prd2 as (
select
PRODUCT_NAME, WAREHOUSE_NAME, QUANTITY,
avg(QUANTITY) over (partition by PRODUCT_NAME) avg_quantity,
row_number() over (partition by PRODUCT_NAME order by QUANTITY desc, WAREHOUSE_NAME) warehouse_order
from prd)
select * from prd2;
PRODUCT_ WAREHOUSE_NAM QUANTITY AVG_QUANTITY WAREHOUSE_ORDER
-------- ------------- ---------- ------------ ---------------
EVGA Toronto 178 178 1
Intel Toronto 90 80 1
Intel Toronto 70 80 2
Kingston San Francisco 173 173 1
Kingston Toronto 173 173 2
3) get per product only the record with the top warehouse
with prd as (
SELECT p.product_name, w.warehouse_name, i.quantity
FROM products p
JOIN inventories i ON p.product_id = i.product_id
LEFT OUTER JOIN warehouses w ON w.warehouse_id = i.warehouse_id
WHERE (p.product_id BETWEEN 100 AND 105)
),
prd2 as (
select
PRODUCT_NAME, WAREHOUSE_NAME, QUANTITY,
avg(QUANTITY) over (partition by PRODUCT_NAME) avg_quantity,
row_number() over (partition by PRODUCT_NAME order by QUANTITY desc, WAREHOUSE_NAME) warehouse_order
from prd)
select PRODUCT_NAME, WAREHOUSE_NAME, AVG_QUANTITY
from prd2
where warehouse_order = 1;
PRODUCT_ WAREHOUSE_NAM AVG_QUANTITY
-------- ------------- ------------
EVGA Toronto 178
Intel Toronto 80
Kingston San Francisco 173
If you insist use the CURSOR and your record_type - the core logic is the the query above.
Two notes:
Using MAX(quantity) to get the top record is a naive approach, that badly fails if you have ties, i.e. the MAX equals to more than one record!
See my implementation where in case of ties is shown the warehouse with the lower name
order by QUANTITY desc, WAREHOUSE_NAME
Grouping on NAMEs is possible, but fails if you have more products with the same name - typically you group by IDs
Sample data
create table products as
select 100 product_id, 'Kingston' product_name from dual union all
select 101 product_id, 'Intel ' product_name from dual union all
select 102 product_id, 'EVGA ' product_name from dual;
create table inventories as
select 100 product_id, 173 quantity, 1 warehouse_id from dual union all
select 100 product_id, 173 quantity, 2 warehouse_id from dual union all
select 101 product_id, 90 quantity, 2 warehouse_id from dual union all
select 101 product_id, 70 quantity, 2 warehouse_id from dual union all
select 102 product_id, 178 quantity, 2 warehouse_id from dual;
create table warehouses as
select 1 warehouse_id, 'San Francisco' warehouse_name from dual union all
select 2 warehouse_id, 'Toronto' warehouse_name from dual;
Using a BULK COLLECT is much more efficient then looping through a cursor. This reduces the amount of context switches and still allows you to reference each row as a record type.
DECLARE
TYPE product_rec IS RECORD
(
emp_f_name products.product_name%TYPE,
emp_avg inventories.quantity%TYPE,
emp_wh warehouses.warehouse_name%TYPE
);
TYPE product_t IS TABLE OF product_rec;
l_products product_t;
BEGIN
SELECT product_name, avg_quantity, warehouse_name
BULK COLLECT INTO l_products
FROM (SELECT p.product_name,
AVG (i.quantity) OVER (PARTITION BY p.product_id) AS avg_quantity,
w.warehouse_name,
ROW_NUMBER ()
OVER (PARTITION BY PRODUCT_NAME ORDER BY QUANTITY DESC, WAREHOUSE_NAME) warehouse_order
FROM products p
JOIN inventories i ON p.product_id = i.product_id
JOIN warehouses w ON i.warehouse_id = w.warehouse_id
WHERE (p.product_id BETWEEN 100 AND 105))
WHERE warehouse_order = 1;
FOR i IN 1 .. l_products.COUNT
LOOP
DBMS_OUTPUT.Put_line (
l_products (i).emp_f_name
|| ' average product: '
|| l_products (i).emp_avg
|| ' mostly found in '
|| l_products (i).emp_wh);
END LOOP;
END;
/

SQL for highest sold Products

Order Sample data:
ORDER_DAY ORDER_ID PRODUCT_ID QUANTITY PRICE
---------- --------- ----------- ---------- ---------
01-JUL-11 O1 P1 5 5
01-JUL-11 O2 P2 2 10
01-JUL-11 O3 P3 10 25
01-JUL-11 O4 P1 20 5
02-JUL-11 O5 P3 5 25
02-JUL-11 O6 P4 6 20
02-JUL-11 O7 P1 2 5
02-JUL-11 O8 P5 1 50
02-JUL-11 O9 P6 2 50
02-JUL-11 O10 P2 4 10
Q: Get me highest sold Products (Qty* Price) on both days
Desired output :
DATE PRODUCT_ID SOLD_AMOUNT
01-JUL-11 P3 250
02-JUL-11 P3 125
Try following query:
select order_day, product_id, totalsale
from (select order_day,
product_id,
nvl(QUANTITY, 0) * PRICE as totalsale,
dense_rank() over(partition by ORDER_DAY order by(nvl(QUANTITY, 0) * PRICE) desc) as maxsum
from orders )
where maxsum = 1;
You would start with getting the sold amount per day and product. With this data you first select the maximum profit per day and then select those entries that match this:
WITH PRODUCT_PER_DAY AS
(
SELECT ORDER_DAY, PRODUCT_ID, SUM(QUANTITY * PRICE) AS SOLD_AMOUNT
FROM MYTABLE
GROUP BY ORDER_DAY, PRODUCT_ID
)
SELECT ORDER_DAY, PRODUCT_ID, SOLD_AMOUNT
FROM PRODUCT_PER_DAY
WHERE (ORDER_DAY, SOLD_AMOUNT) IN
(
SELECT ORDER_DAY, MAX(SOLD_AMOUNT)
FROM PRODUCT_PER_DAY
GROUP BY ORDER_DAY
)
ORDER BY ORDER_DAY, PRODUCT_ID;
select b.product_id,a.order_day,a.total_price from(select order_day, max(quantity*price)as total_price from order group by order_day)a
join (select product_id , quantity * price as total_amount from order)b on
a.total_price= b.total_price
Try this out (modify the table and column names as per your needs):
Select a.order_day, b.product_id, a.sales
from
(select order_day, max(quantity*price) as sales
from ordr
group by order_day) a
inner join
(select order_day, product_id, quantity*price as sales
from ordr) b
on a.order_day = b.order_day and a.sales = b.sales;
with temp as
(select order_day,product_id,sum(quantity*price) qp
from orders_amazn
group by order_day,product_id
)
select * from (select order_day,product_id,qp,
dense_rank() over (partition by order_day order by qp desc) rnk
from temp)
where rnk=1;
select date,product_id,max(product_id*price) as sold_amount form order
group by order_day
order by order_day
select t.ORDER_DAY as date1 ,t.PRODUCT_ID,max(t.quantity * t.price)
as sold_amount from table t group by t.ORDER_DAY,t.PRODUCT_ID
Kindly use this query you will be the desired output.I have tried in MySQL.
SELECT
order_date,
Product_id,
sold_amount
FROM
(
SELECT
date_format(order_day,'%d-%b-%y') AS order_date,
Product_id,
SUM(price*quantity) AS sold_amount
FROM
ecommerce
GROUP BY
date_format(order_day,'%d-%b-%y'),
product_id) a
WHERE
(
order_date,sold_amount) IN
(
SELECT
DATE,
MAX(sold_amt) AS sold_amount
FROM
(
SELECT
date_format(order_day,'%d-%b-%y') AS DATE,
product_id,
SUM(price*quantity) AS sold_amt
FROM
ecommerce
GROUP BY
date_format(order_day,'%d-%b-%y'),
product_id)a
GROUP BY
DATE)
select order_day as orderdate,product_id,max(quantity * price) as sold_amount
from order
group by order_day
order by product_id

select max value from a table looking for description in another table

i have 3 tables
Buyer
buyer_id | name
50 |Joe
60 |Astor
70 |Cloe
Item
item_id | description
1 | iphone
2 | ipod
3 | imac
Item_Sold
buyer_id | item_id
50 | 1
50 | 2
60 | 1
60 | 3
70 | 1
70 | 2
70 | 3
I want to find out the description of the best-selling item, in this case:
Best-Selling
iphone
SELECT description AS Best_Selling
FROM item
WHERE item_id = (SELECT item_id FROM( SELECT item_id ,COUNT(*) as num_items
FROM Item_Sold
GROUP BY item_id
ORDER BY num_items DESC
LIMIT 1
)z
)
See SQL FIDDLE
This answer is not totally correct . If two items have same sale amount then it will return only one of them.
This query will give all item id decription whose sale is maximum i.e. when two or more item id have equal amount of sale....
;WITH CTE1(Id,Counts) as
(
SelectItem_Id,COUNT(buyer_id ) AS C FROM T GROUP BY ID
)
Select Item.Description from CTE1 A inner join
(Select MAX(Counts) AS MaxCount FROM CTE1 ) b on a.Counts=b.MaxCount
inner join
Item on Item.Item_Id=a.Item_Id
If Common table Expression Not Work you Can Try Like this....
Select Item.Description from (Select Item_Id,COUNT(buyer_id ) AS Counts FROM item_sold GROUP BY Item_Id) A inner join
(Select MAX(Counts) AS MaxCount FROM
(
Select Item_Id,COUNT(buyer_id) AS Counts
FROM item_sold GROUP BY Item_Id) v
) b
on a.Counts=b.MaxCount
inner join
Item on Item.Item_Id=a.Item_Id
SQL Fiddle Demo
Here Is the Liknk of Fiddle the case i m talknig about....it give all description who have maximun sale....
Case Sql Fiddle Demo
select description as "Best-Selling"
from (select a.item_id, b.description, count(*) count
from Item_Sold a,Items b
where a.item_id = b.item_id
group by a.item_id ) temp
where count = (select max(count)
from (select a.item_id, count(*) count
from Item_Sold a,Items b
where a.item_id = b.item_id
group by a.item_id ) temp1)
pl-sql:
select description as "Best-Selling"
from item
where item_id in (
select item_id from (
select item_id, count(item_id) as item_count
from item_sold
group by item_id)
where item_count = (
select max(item_count) from (
select item_id, count(item_id) as item_count
from item_sold
group by item_id)
)
)

Select 3 lastest order for each customer

Here is my table CusOrder that collect customer order
OrderID Cus_ID Product_ID NumberOrder OrderDate
1 0000000001 9 1 6/5/2553 0:00:00
2 0000000001 10 1 6/5/2553 0:00:00
3 0000000004 9 2 13/4/2553 0:00:00
4 0000000004 9 1 17/3/2553 0:00:00
5 0000000002 9 1 22/1/2553 0:00:00
7 0000000005 9 1 16/12/2552 0:00:00
8 0000000003 9 3 13/12/2552 0:00:00
10 0000000001 9 2 19/11/2552 0:00:00
11 0000000003 9 2 10/11/2552 0:00:00
12 0000000002 9 1 23/11/2552 0:00:00
I need to select 3 lastest order for each customer and I need all customer
so it will show each customer and his/her 3 lastest order
how can I do it
sorry for my bad english
This CTE should work for you:
;with cteTop3PerGroup as
(
select row_number() over(partition by Cus_ID order by OrderDate DESC) as RN
, *
from CusOrder
)
select * from cteTop3PerGroup
where RN <= 3
WITH Temp AS
(
SELECT *, (ROW_NUMBER() OVER (PARTITION BY Cus_ID ORDER BY OrderDate DESC)) AS Number
FROM CusOrder
)
SELECT * FROM Temp WHERE Number <= 3
Should work. Not tested with this exact database structure, but something similar.
"Pure" SQL solution.
With Customers As (
Select Cus_ID From test Group By Cus_ID
),
TopLastOrders as (
Select o.Cus_ID, Max(OrderDate) as OrderDate
From test o Inner Join Customers c on c.Cus_ID = o.Cus_ID
Group By o.Cus_ID
),
TopSecondOrders as (
Select o.Cus_ID, Max(OrderDate) as OrderDate
From test o Inner Join Customers c on c.Cus_ID = o.Cus_ID
Where Not Exists (Select 1 From TopLastOrders Where Cus_ID=o.Cus_ID And OrderDate=o.OrderDate)
Group By o.Cus_ID
),
TopThirdOrders as (
Select o.Cus_ID, Max(OrderDate) as OrderDate
From test o Inner Join Customers c on c.Cus_ID = o.Cus_ID
Where Not Exists (Select 1 From TopLastOrders Where Cus_ID=o.Cus_ID And OrderDate=o.OrderDate)
And Not Exists (Select 1 From TopSecondOrders Where Cus_ID=o.Cus_ID And OrderDate=o.OrderDate)
Group By o.Cus_ID
)
Select
c.Cus_ID,
t1.OrderDate,
t2.OrderDate,
t3.OrderDate
From
Customers c
Left Outer Join TopLastOrders t1 on t1.Cus_ID = c.Cus_ID
Left Outer Join TopLastOrders t2 on t2.Cus_ID = c.Cus_ID
Left Outer Join TopLastOrders t3 on t3.Cus_ID = c.Cus_ID
Order By
c.Cus_ID
I'm not sure what output you need but this should not be hard.