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

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

Related

SQL retrieve rows based on column condition

Say I have below tables
Order Table
OrderNo CategoryID CountryID ServiceTypeID
100 1 3 1
200 2 5 2
300 3 4 4
400 1 2 9
1 service Type might belong to many category type
Category Table
ID Name ServiceTypeID
1 x 1
2 x 2
3 x 1
ServiceType table
ID Name
1 xx
2 xx
3 xx
Tracking Table
OrderNo CountryID TrackingTypeID
100 2 3
200 1 4
100 3 2
400 5 1
200 2 6
Reviewd Table
OrderNo
300
100
200
I want to write a query with below requirements
Order must belong to serviceTypeID = 1 or 2
And If the orderNo has a categoryID = 1
I want that record to be retrieved only if
there's a record in tracking table for that
orderNo with same countryID as in Order table
and if that orderNo doesn't have tracking type of id (0,7,1) in tracking table
Else for all other orders with any other category excluding orders which are not belong
to serviceTypeID = (1,2)
I want that record to be retrived only if
there's an existing record for that orderNo in
Reviewed table
and if that orderNo doesn't have tracking type of id (0,7,1) in tracking table
So basically based on above requirements the result should look like
OrderNo CategoryID StationID
100 1 3
200 2 5
select DISTINCT top 10000 o.orderNo , o.categoryID , o.serviceTypeid
,o.countrtId , Tracking.countryId
from Order o
join Tracking on o.orderNo = Tracking.orderNo
where
(o.CategoryID in (1 ) and o.countryId = Tracking.countryId
and
exists (select 1
from tracking t
where t.orderNo = o.orderNo and t.countryId =
o.countryId
)
)
OR
(o.categoryID in (select id from Category where ServiceTypeid in (7,8) and
ID not in (56 , 65)
) and
exists (select 1
from Reviewed r
where r.orderNo = o.orderNo
)
)
AND not exists
( select 1
from tracking t
WHERE T.orderNo = o.orderNo
and t.TrackingTypeID in (0 , 7 ,25))
That query seems to return only orders with ID 1 and even if it's have a trackingTypeID = 0,7,25
You could use exists and boolean logic:
select o.*
from orders o
where
(
category_id = 1
and exists (select 1 from tracking t where t.order_no = o.order_no and t.country_id = o.country_id)
)
or (
category_id = 2
and exists (select 1 from reviewed r where r.order_no = o.order_no)
)
Based on your conditions, you can use boolean logic with exists:
select o.*
from orders o
where (o.categoryid = 1 and
exists (select 1
from tracking t
where t.orderno = o.orderno and t.CountryID = o.CountryID
)
) or
(o.categoryid = 2 and
exists (select 1
from reviewed r
where r.orderno = o.orderno
)
) ;
According to your conditions this query will give results accordingly:
Select o.OrderNo,o.CategoryID,o.CountryID as StationID
from Order o
inner join Tracking t on t.OrderNo = o.OrderNo and t.CountryID = o.CountryID
where o.CategoryID = 1 or (o.CategoryID = 2
and exists (Select * from Reviewed where OrderNo = o.OrderNo))

Merge or group rows corresponding to particular column postgresql

I want to group by the resultset further corresponding to the price_type column,
if the data for a product with both price_type variant and parent_product is present, then it must show only the variant one
For example, this data
Product Name PPID QTY PRID PRICE PRICE_TYPE
Shorts 1 10 21 200 variant
Shorts 1 10 21 100 parent_product
Night Suit 1 10 22 200 variant
Night Suit 1 10 22 100 parent_product
Denim 1 10 23 400 parent_product
should come like
Product Name PPID QTY PRID PRICE PRICE_TYPE
Shorts 1 10 21 200 variant
Night Suit 1 10 22 200 variant
Denim 1 10 23 400 parent_product
It seems you want row_number() with conditional ordering:
select *
from (select *, row_number() over (partition by ProductName
order by (case when Price_type = 'variant'
then 0 else 1
end)
) as seq
from table
) t
where seq = 1;
Below is the simple query to get desired result.
select
distinct on(product_name),
t.*
from tab t
order by price_type='variant' desc
You can use a window function:
SELECT * FROM
(
SELECT * ,
RANK() OVER (PARTITION BY product_name ORDER BY priority ) AS rank
FROM (
SELECT *,
CASE
WHEN price_type='variant' THEN 1
ELSE 2
END AS priority
FROM yourtable
) AS X
) AS Y
WHERE rank=1

How to use group by without using an aggregation function

I am trying to find the row number of the price while grouping them with respect to their ProductId. For ProductId = 1, rank of price (where price = 1000) should be smt. For productId=3, rank of price (= 1000) should be sth.
How can I find row number of price for different productId in the same table?
How can I achieve this using group by without aggregation for Row_number.
ProductId Price
-----------------
1 2000
1 1600
1 1000
2 2200
2 1000
2 3250
3 1000
3 2500
3 1750
So result should be
ProductId Price PriceRank
------------------------------
1 1000 3
2 1000 2
3 1000 1
This is my code:
SELECT
ProductId,
ROW_NUMBER() OVER (ORDER BY price ASC) AS PriceRank
FROM
product
WHERE
price = 1000
GROUP BY
ProductId
This will give you correct result:
select * from
(SELECT ProductId,price,ROW_NUMBER()
OVER (partition by productid order by productid ) AS PriceRank
FROM products) a
WHERE price =1000
Not sure if this is the best way to do it.. but it certainly is ONE way to do it
;WITH mycte AS (
SELECT
1 as ProductId , 2000 as Price
UNION ALL SELECT
1 , 1600
UNION ALL SELECT
1 , 1000
UNION ALL SELECT
2 , 2200
UNION ALL SELECT
2 , 1000
UNION ALL SELECT
2 , 3250
UNION ALL SELECT
3 , 1000
UNION ALL SELECT
3 , 2500
UNION ALL SELECT
3 , 1750
)
,my_rank as (
Select
ProductId
, Price
,ROW_NUMBER() OVER (ORDER BY (SELECT 1)) rownumber
from mycte
)
,ranking AS (
SELECT
ProductId
, Price
, rownumber
, ROW_NUMBER() OVER (PARTITION BY ProductId ORDER BY rownumber) pricerank
FROM my_rank
)
SELECT
ProductId
, Price
,pricerank
FROM ranking
WHERE Price = 1000
Try this:
declare #result table(ProductID smallint, Price int, PriceRank smallint)
declare #product table(ProductID smallint, Price int) --replicate your table data
declare #id smallint
--feed table with your data
insert into #product
select ProductId, Price from product
while(exists(select * from #product))
begin
select top 1 #id = ProductId from #product
insert into #result
SELECT
ProductId, price,
ROW_NUMBER() OVER (ORDER BY ProductID) as CountPerGroup
FROM #product
WHERE ProductID = #id
delete from #product where ProductID = #id
end
select * from #result where Price = 1000

SQL to reconcile two tables

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;

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