SQL to reconcile two tables - sql

I have two tables ItemSold and ItemInventory, and am trying to create a query that will check if the ItemSold SUM(quantity of an item) is balanced against ItemInventory.
ItemSold
sale_id | item_id | quantity | conversion |
-------- ------- --------- ----------
1099 1 1.00 1.00
1099 1 1.00 0.50
1099 2 1.00 1.00
I'm using the conversion column to signify a retail/smaller unit of an item
ItemInventory
id | description | quantity
----- ------------ --------
1 Item1 100.00
2 Item2 100.00
Below is a query that I created to check if there are items that if you SUM up is greater than the quantity on the ItemInventory.
SELECT ItemSold.item_id, ItemSold.quantity, ItemSold.conversion
FROM ItemSold
INNER JOIN ItemInventory
ON ItemSold.item_id = ItemInventory.id
WHERE Itemsold.sale_id = #saleid
GROUP BY ItemSold.item_id, ItemSold.quantity, ItemSold.conversion
HAVING SUM(ItemSold.quantity * ItemSold.conversion) > SUM(ItemInventory.quantity)
It works but it compares the quantity of EACH ROW on the ItemSold rather than the SUM(quantity group by item_id).
My goal is to not process the Sale transaction unless there is enough inventory regardless what unit the item is sold.

The strategy of this query is to aggregate, for each item, the total quantity promised to be sold in the ItemSold table, and then compare this number against the inventory number which is available. Items not having sufficient quantity are displayed in the result set.
SELECT t1.id,
t1.description
FROM ItemInventory t1
LEFT JOIN
(
SELECT item_id, SUM(quantity*conversion) AS quantity_sold
FROM ItemSold
GROUP BY item_id
) t2
ON t1.id = t2.item_id
WHERE t1.quantity < COALESCE(t2.quantity_sold, 0)

This will check that all the items are in the inventory for the sale_id of 1099:
SELECT sale_id,
MIN( CASE WHEN s.total_quantity <= i.quantity THEN 1 ELSE 0 END )
AS can_process_order
FROM ( SELECT sale_id,
item_id,
SUM( quantity ) AS total_quantity
FROM itemsold
WHERE sale_id = 1099
GROUP BY sale_id, item_id
) s
LEFT OUTER JOIN
iteminventory i
ON ( s.item_id = i.id )
GROUP BY sale_id;
It will output 1 if there are sufficient items and 0 if any one item does not have enough stock.
To perform the update you can do:
MERGE INTO iteminventory dst
USING (
SELECT i.ROWID AS rid,
total_sale,
MIN( CASE WHEN total_sale <= quantity THEN 1 ELSE 0 END )
OVER ( PARTITION BY sale_id ) AS can_process_sale
FROM (
SELECT sale_id,
item_id,
SUM( quantity ) AS total_sale
FROM itemsold
WHERE sale_id = 1099
GROUP BY sale_id, item_id
) s
LEFT OUTER JOIN iteminventory i
ON ( i.id = s.item_id )
) src
ON ( src.rid = dst.ROWID AND src.can_process_sale = 1 )
WHEN MATCHED THEN
UPDATE SET quantity = quantity - total_sale;

Related

is there a function in sql to find second minimum in table, which then used in case

For example, there are two tables, and in one - prices with articles, and another table - checks, some articles in checks, and quantity of article
TABLE checks
checks
art
quantity
1check
1toy
2
1check
1toy
5
1check
1toy
1
1check
2toy
1
1check
4toy
3
2check
2toy
1
2check
1toy
2
TABLE articles
art
price
1toy
2.00
2toy
2.50
3toy
1.50
4toy
6.00
1toy
2.50
1toy
3.00
and i need to count the sum of sales of 1check,where i need to take the second minimum of price if the articles repeat.
for 1toy in 1check the price have to be 2.5.- for sum (quantity*price)
i try to write a code - but i finally confused.
help please
SELECT
a.check,
Sump
FROM
(
SELECT
price2,
Case
WHEN COUNT( a.art ) > 1 THEN
SUM( a.quantity * a.price )
ELSE
SUM( a.quantity * price2 )
END AS sump,
a.art,
a.check
FROM
checks AS a
INNER JOIN
(
SELECT
art,
price,
LEAD( price, 1 ) OVER (
PARTITION BY art
ORDER BY price ASC
) AS price2
FROM
prices
) AS b on a.art = b.art
WHERE
a.quantity > 0
GROUP BY
a.checks,
a.art,
price2
)
WHERE
a.checks = '1check'
You may use the ROW_NUMBER() and COUNT window functions as the following:
SELECT T.checks, T.art, SUM(T.quantity * D.price) sump
FROM
checks T JOIN
(
SELECT art, price,
COUNT(*) OVER (PARTITION BY art) cn,
ROW_NUMBER() OVER (PARTITION BY art ORDER BY price) rn
FROM articles
) D
ON T.art = D.art
WHERE (D.cn = 1 OR D.rn = 2) AND T.checks = '1check'
GROUP BY T.checks, T.art
ORDER BY T.checks, T.art
The WHERE (D.cn = 1 OR D.rn = 2) ensures that the returned price is the second minimum (rn=2) or it's only the existed price (cn=1).
The output according to your provided data:
CHECKS
ART
SUMP
1check
1toy
20
1check
2toy
2.5
1check
4toy
18
See a demo on Oracle 11g.
We use dense_rank() to find the second lowest price in the case count(*) > 1. Then we merge the tables, group by art and total the sales.
with a as (
select art
,price
from
(
select a.*
,dense_rank() over(partition by art order by price) as dns_rnk
,count(*) over(partition by art) as cnt
from articles a
) a
where cnt > 1 and dns_rnk = 2
or cnt = 1
)
select art
,sum(quantity)*price as total
from a left join checks c using(art)
group by art, price, checks
having checks = '1check'
order by art
ART
TOTAL
1toy
20
2toy
2.5
4toy
18
Fiddle

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

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

How can I merge a result set from a matrix table?

Say I have a price matrix that looks like this:
ID SalePrice CostPrice
1 200 0
1 0 75
1 0 100
2 150 0
2 0 50
2 500 0
I then have a table with 75k ID´s that I wish to join with the price matrix and return one row per ID with the lowest value for SalePrice and CostPrice. Like so:
ID SalePrice CostPrice
200 75
150 50
Here is the sql I currently use:
declare #priceMatrix TABLE(
prodNo varchar(50) NOT NULL,
salePrice decimal,
costPrice decimal)
INSERT INTO #priceMatrix
SELECT pm.ID, pm.SalePr, pm.CstPr
FROM PriceMatrix pm
GROUP BY pm.ID, pm.SalePrice, pm.CostPrice
ORDER BY pm.ID ASC
SELECT ProductTable.ProdNo,
(SELECT TOP 1 salePrice FROM #priceMatrix WHERE ProductTable.ID = ID AND
costPrice = 0 ORDER BY salePrice ASC) AS SalePrice ,
(SELECT TOP 1 costPrice FROM #priceMatrix WHERE ProductTable.ID = ID AND
salePrice = 0 ORDER BY costPrice ASC) AS CostPrice ,
FROM ProductTable
GROUP BY ProductTable.ID
But with 75000 ID´s the performance is just terrible using sub queries. Not to say completely useless.
Is there an effective way to achieve what I´m looking for?
SELECT ProductTable.ProdNo,
sub1.MIN_SALE_PRICE as SalePrice ,
sub2.MIN_COST_PRICE as CostPrice
FROM ProductTable pt
LEFT JOIN (SELECT MIN(salePrice) MIN_SALE_PRICE, ID
FROM #priceMatrix
WHERE costPrice = 0
GROUP BY ID) sub1 ON pt.ID=sub1.ID
LEFT JOIN (SELECT MIN(costPrice ) MIN_COST_PRICE, ID
WHERE salePrice = 0
FROM #priceMatrix
GROUP BY ID) sub2 ON pt.ID=sub2.ID
GROUP BY ProductTable.ID
Move the sub selects with ORDER BY to JOINS to evaluate them once for all products
This actually turned out to execute faster by 1 sek:
SELECT ProductTable.ProdNo,
(SELECT MIN(salePrice)
FROM PriceMatrix
WHERE PriceMatrix.ID = ProductTable.ID
AND PriceMatrix.CostPrice = 0
AND PriceMatrix.SalePrice > 0) AS SalePrice,
(SELECT MIN(costPrice)
FROM PriceMatrix
WHERE PriceMatrix.ID = ProductTable.ID
AND PriceMatrix.CostPrice > 0
AND PriceMatrix.SalePrice = 0) AS CostPrice
FROM ProductTable

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

Group By by hiding a column - TSQL

I have a table structure
Table1
ID Hours Qty ProductID
1 2 1 100
1 3 5 200
2 6 6 100
2 2 2 200
If productid is (1,2,3) then i need sum ( Qty * Hours),If productid in (200,300,400,500) then i need sum(qty).
I have written a code like this
select ID,case when productid in (1,2,3) then
SUM( qty * hrs)
when productid in (100,200,300) then SUM( qty ) end result1
from Prod group by id ,productid
but i don't want to group by productid,i would like to pass it in "IN clause".How to achieve it.
Move the SUM() outside of the CASE WHEN statement.
SELECT
ID,
SUM(case when productid in (1,2,3) then qty * hrs
when productid in (100,200,300) then qty
end) result1
FROM
Prod
GROUP BY
ID
Assuming you want all columns plus the result of your query, you can do this:
select p.*, aux.result
from Prod p
inner join (select ID,case when productid in (1,2,3) then SUM( qty * hrs)
when productid in (100,200,300) then SUM( qty )
end as result
from Prod group by id ,productid) aux on aux.id = p.id