I am struggling to get my head around this sql.
I have a function that returns a list of items associated with a Bill of Materials BOM.
The result of the sql select
SELECT
BOM,
ITEMID,
QTY
FROM boms
WHERE
bom='A'
is
BOM | ITEMID | QTY
A | ITEMB | 1
A | ITEMC | 2
Now using that result set I am looking to query my salestable to find sales where ITEMB and ITEMC were sold in enough quantity.
The format of the salestable is as follows
SELECT
salesid,
itemid,
sum(qtyordered) 'ordered'
FROM salesline
WHERE
itemid='ITEMB'
or itemid='ITEMC'
GROUP BY salesid, itemid
This would give me something like
salesid | itemid | ordered
SO-10000 | ITEMB | 1
SO-10001 | ITEMB | 1
SO-10001 | ITEMC | 1
SO-10002 | ITEMB | 1
SO-10002 | ITEMC | 2
ideally I would like to return only SO-10002 as this is the only sale where all necessary units were sold.
Any suggestions would be appreciated. Ideally one query would be ideal but I am not sure if that is possible. Performance is not a must as this would be run once a week in the early hours of the morning.
EDIT
with the always excellent help, the code is now complete. I have wrapped it all up into a UDF which simply returns the sales for a specified BOM over a specified period of time.
Function is
CREATE FUNCTION [dbo].[BOMSALES] (#bom varchar(20),#startdate datetime, #enddate datetime)
RETURNS TABLE
AS
RETURN(
select count(q.SALESID) SOLD FROM (SELECT s.SALESID
FROM
(
SELECT s.SALESID, ITEMID, SUM(qtyordered) AS SOLD
FROM salesline s inner join SALESTABLE st on st.salesid=s.SALESID
where st.createddate>=#startdate and st.CREATEDDATE<=#enddate and st.salestype=3
GROUP BY s.SALESID, ITEMID
) AS s
JOIN dbo.BOM1 AS b ON b.ITEMID = s.ITEMID AND b.QTY <= s.SOLD
where b.BOM=#bom
GROUP BY s.SALESID
HAVING COUNT(*) = (SELECT COUNT(*) FROM dbo.BOM1 WHERE BOM = #bom)) q
)
This should return all sales with an exact match, i.e. same itemid and same quantity:
SELECT s.salesid
FROM
(
SELECT salesid, itemid, SUM(qtyordered) AS ordered
FROM salesline AS s
GROUP BY salesid, itemid
) AS s
JOIN
boms AS b
ON b.itemid = s.itemid
AND b.QTY = s.ordered
WHERE b.BOM='A'
GROUP BY s.salesid
HAVING COUNT(*) = (SELECT COUNT(*) FROM boms WHERE BOM='A');
If you want to return a sale where the quantity is greater than boms.qty youhave to change the join accordingly:
JOIN
boms AS b
ON b.itemid = s.itemid
AND b.QTY <= s.ordered
Untested...
You can do this aggregation and a having clause:
select salesid
from salesline sl
group by salesid
having sum(case when itemid = 'ITEMB' then 1 else 0 end) > 0 and
sum(case when itemid = 'ITEMA' then 1 else 0 end) > 0;
Each condition in the having clause is counting the number of rows with each item.
I think this may get you the results you need. You'll have to replace #BOM with your bom:
SELECT
DISTINCT salesid
FROM
salesline sl
INNER JOIN boms b ON
b.bom = #BOM
AND b.itemid = sl.itemid
GROUP BY salesid, itemid
HAVING SUM(qtyordered) >= b.qty
From what I gather, the first query is used to get the thresholds for returning qualifying sales? Based on your example data rows I assumed that there will only be one line per salesid + itemid (basically acting as a dual field primary key) in the salesline table? If that is true I don't think there is a need to do a SUM as you have in your second example query. Let me know if I'm mistaken in any of my assumptions and I'll adjust my answer.
Related
I'm trying to get all products based on two conditions on another table. See my example db data.
Product
--------------
ProductID ProductName
1 P1
2 P2
3 P3
AuditLog
------------
Event Date ProductId
ApproveProduct 2016-01-27 16:00 1
ViewProduct 2016-01-27 17:00 1
ViewProduct 2016-01-27 15:00 2
ApproveProduct 2016-01-27 17:00 2
Based on the example db data, I would like to get all products that have ViewProduct auditlog entry AFTER ApproveProduct entry based on the Date. I would like to return first product since second product has the AuditLog entries in wrong order:
ProductID ProductName
1 P1
I'm trying to achieve something like below, but obviously that code is not valid:
SELECT p.* FROM Product p
WHERE EXISTS (SELECT TOP 1 Date
FROM AuditLog WHERE Event = 'ApproveProduct' AND ProductId = p.ProductID) ap
AND EXISTS (SELECT 1
FROM AuditLog WHERE Event = 'ViewProduct' AND Date > ap.Date AND ProductId = p.ProductID)
You can use conditional aggregation to get the ProductIDs that you want:
SELECT ProductId
FROM AuditLog
GROUP BY ProductId
HAVING MAX(CASE WHEN Event = 'ViewProduct' THEN Date END) >
MAX(CASE WHEN Event = 'ApproveProduct' THEN Date END)
and also the operator IN to get the product details:
SELECT *
FROM Product
WHERE ProductId IN (
SELECT ProductId
FROM AuditLog
GROUP BY ProductId
HAVING MAX(CASE WHEN Event = 'ViewProduct' THEN Date END) >
MAX(CASE WHEN Event = 'ApproveProduct' THEN Date END)
)
See the demo.
Results:
> ProductID | ProductName
> --------: | :----------
> 1 | P1
The below query should do it
select ProductId
from
(
select
ProductId,
Event,
Lag(Event, 1) over (partition by ProductId order by Date) as PrevEvent
from AuditLog
where Event in ('ApproveProduct', 'ViewProduct')
)
where PrevEvent = 'ApproveProduct' and Event = 'ViewProduct'
I didn't add a join with the Product table to make the SQL clearer but it should be trivial.
I need to write a SELECT query in SQL Server which uses a JOIN or UNION that selects distinct ItmNo or Code rows from 3 tables OnHand, Sale and Purchase.
Here are the details of the tables I have and what I need. ItmNo and/or Code columns can be used as foreign keys to join the tables.
These are my input tables-
Table OnHand
ID ItmNo Code Qty
----------------------------------
1 I001 001 100
2 I001 001 50
3 I003 003 300
Table Sale
ID ItmNo Code Qty
----------------------------------
1 I001 001 100
2 I004 004
3 I003 003 120
Table Purchase
ID ItmNo Code Qty
----------------------------------
1 I005 005 10
2 I003 003 200
3 I003 003 300
And this is what I need as output. Only DISTINCT ItmNo and Code should be displayed here:
ID ItmNo Code SumQtyOnHand SumQtyOnSale SumQtyOnPurchase
------------------------------------------------------------------------------
1 I001 001 150 100
2 I003 003 300 120 500
3 I005 005 10
Here is the SELECT query that I have tried is below but I cannot get the output I want-
SELECT
A.ItmNo, A.Code,
A2.TOTAL SumQtyOnHand,
B.TOTAL SumQtyOnSale,
C.TOTAL SumQtyOnPurchase
FROM
dbo.OnHand A
LEFT JOIN
(SELECT ItmNo, Code, SUM(Qty) TOTAL
FROM dbo.OnHand
GROUP BY ItmNo, Code) A2 ON A.ItmNo = A2.ItmNo
LEFT JOIN
(SELECT ItmNo, Code, SUM(Qty) TOTAL
FROM dbo.Sale
GROUP BY ItmNo, Code) B ON A.ItmNo = A2.ItmNo
LEFT JOIN
(SELECT ItmNo, Code, SUM(Qty) TOTAL
FROM dbo.Purchase
GROUP BY ItmNo, Code) C ON A.ItmNo = A2.ItmNo
Please suggest the correction in the SELECT query to achieve the above output.
Thanks in advance!
I think you are on the right track with the prea-ggregation subqueries. Then, you can full join. The syntax is a bit cumbersome in SQL Server, that does not support the using() clause:
select
coalesce(o.itmno, s.itemno, p.itemno) as itemno,
coalesce(o.code, s.code, p.code) as code,
o.SumQtyOnHand,
s.SumQtyOnSale,
p.SumQtyOnPurchase
from (
select itmno, code, sum(qty) SumQtyOnHand
from dbo.onhand
group by itmno, code
) o
full join (
select itmno, code, sum(qty) SumQtyOnSale
from dbo.sale
group by itmno, code
) s on s.itmno = o.itmno and s.code = o.code
full join (
select itmno, code, sum(qty) SumQtyOnPurchase
from dbo.purchase
group by itmno, code
) p on p.itemno = coalesce(s.itemno, o.itemno) and p.code = coalesce(s.code, o.code)
It might be simpler expressed with union all and aggregation:
select itemno, code,
sum(qtyOnHand) as SumQtyOnHand,
sum(qtyOnSale) as SumQtyOnSale,
sum(qtyOnPurchase) as SumQtyOnPurchase
from (
select itemno, code, qty as qtyOnHand, null as qtyOnSale, null as qtyOnPurchase from dbo.onhand
union all select itemno, code, null, qty, null from dbo.sale
union all select itemno, code, null, null, qty from dbo.purchase
) t
group by itemno, code
i have 1 table inventory_movement here is data in table
product_id | staff_name | status | sum | reference_number
--------------------------------------------------
1 zes cp 1 000122
2 shan cp 4 000133
i have another table inventory_orderproduct where i have cost date
orderdate product_id cost
--------------------------------
01/11/2018 1 3200
01/11/2018 2 100
02/11/2018 1 4000
02/11/2018 1 500
03/11/2018 2 2000
i want this result
product_id| staff_name | status | sum reference_number | cost
--------------------------------------------------------------
1 zes cp 1 000122 4000
2 shan cp 4 000133 2000
here is my query
select ipm.product_id,
case when ipm.order_by_id is not null then
(select au.first_name from users_staffuser us inner join auth_user au on us.user_id= au.id
where us.id = ipm.order_by_id) else '0' end as "Staff_name"
,ipm.status,
Sum(ipm.quantity), ip.reference_number
from inventory_productmovement ipm
inner join inventory_product ip on ipm.product_id = ip.id
inner join users_staffuser us on ip.branch_id = us.branch_id
inner join auth_user au on us.user_id = au.id
AND ipm.status = 'CP'
group by ipm.product_id, au.first_name, ipm.status,
ip.reference_number, ip.product_name
order by 1
Here is the solution of your question.its working fine.if you like the answer please vote!
SELECT i.product_id,i.staff_name,i.status,i.sum reference_number ,s.Cost
FROM (SELECT product_id,MAX(cost) AS Cost
FROM inventory_orderproduct
GROUP BY product_id ) s
JOIN inventory_movement i ON i.product_id =s.product_id
In the given situation, this should work fine:
Select table1.product_id, table2.staff_name, table2.status, table2.reference_number,
MAX(table1.cost)
FROM table2
LEFT JOIN table1 ON table1.product_id = table2.product_id
GROUP BY table2.product_id, table2.staff_name, table2.status, table2.reference_number
You can use the below query to get MAX cost for products
SELECT i.product_id,i.staff_name,i.status,i.sum reference_number ,s.MAXCost
FROM (SELECT product_id,MAX(cost) AS MAXCost
FROM inventory_orderproduct
GROUP BY product_id ) s
JOIN inventory_movement i ON i.product_id =s.product_id
For Retrieving the cost using the latest date use the below query
WITH cte as (
SELECT product_id,cost
,ROW_NUMBER() OVER (PARTITION BY product_id ORDER BY orderdate DESC) AS Rno
FROM inventory_orderproduct )
SELECT i.product_id,i.staff_name,i.status,i.sum reference_number ,s.Cost
FROM cte s
JOIN inventory_movement i ON i.product_id =s.product_id
WHERE s.Rno=1
You can use below query it will pick the data according to the latest date
WITH result as (
SELECT product_id,cost
,ROW_NUMBER() OVER (PARTITION BY product_id ORDER BY date DESC)
FROM inventory_orderproduct )
SELECT i.product_id,i.staff_name,i.status,i.sum reference_number ,s.Cost
FROM result s
JOIN inventory_movement i ON i.product_id =s.product_id
TABLE : ITEMS
---------------------------------------
Item_ID Description
---------------------------------------
1 Vivel Satin Soap
2 Flake
3 Maggie
4 Mango Juice
---------------------------------------
TABLE : SALES
------------------------------------------------
Sale_ID Sale_Date Item_ID Quantity
------------------------------------------------
1 15-Feb-14 1 2
2 16-Feb-14 1 1
3 16-Feb-14 2 1
4 17-Feb-14 3 1
5 18-Feb-14 1 1
6 18-Feb-14 2 2
------------------------------------------------
I'm having trouble in constructing SQL query as the way i wanted.... Here, i have two
tables in the database as shown above. The "ITEMS" table is for Items' Description Look-up
and "SALES" table for Items' Sale Record Look-up. Now, my requirement is, i want to select a records
from both the tables to generate report (as shown in the following). Report should contain
Items Description and its corresponding Sum of Quantity.
REPORT
------------------------------------------------------
Item_ID Description Total_Quantity
------------------------------------------------------
1 Vivel Satin Soap 4
2 Flake 3
3 Maggie 1
4 Mango Juice (SHOULD BE NULL HERE)
------------------------------------------------------
I tried following SQL query and some more to generate the report but had a logical error....
so, help me to construct better!
1) SELECT I.Item_ID, I.Description, Sum(S.Quantity)
FROM ITEMS I
INNER JOIN SALES S ON I.Item_ID = S.Item_ID
ORDER BY I.Item_ID;
2) Select I.Item_ID, I.Description, Sum(S.Quantity)
From ITEMS I, SALES S
Where S.Item_ID IN (Select Item_ID from ITEMS)
Order by I.Item_ID;
3) etc..........
try this,
SELECT I.Description, Sum(S.Quantity)
FROM ITEMS I
LEFT JOIN SALES S ON I.Item_ID = S.Item_ID
GROUP BY I.Description
Instead of Inner just try with Left Join...
And you need to use Group by Clause
SELECT I.Item_ID, I.Description, Sum(S.Quantity) -- It will return Null for Mango Juice
FROM ITEMS I
LEFT JOIN SALES S ON I.Item_ID = S.Item_ID
Group By I.Item_ID, I.Description
ORDER BY I.Item_ID;
OR:
SELECT I.Item_ID, I.Description, CASE WHEN Sum(S.Quantity) IS NULL THEN 0 ELSE Sum(S.Quantity) END Quantity -- It will return 0 for Mango Juice
FROM ITEMS I
LEFT JOIN SALES S ON I.Item_ID = S.Item_ID
Group By I.Item_ID, I.Description
ORDER BY I.Item_ID;
Hi I wonder if you can help with the following query , I am going around in circles trying to get the syntax correct.
I have two Tables Orders
OrderID | Product ID | LineTotal
1 ABC 2
2 CDE 3
2 DEF 1
and Products Table Containing the Weight and Cost
ProductID | Weight | Cost
ABC 1 1
CDE 2 2
DEF 1 0.5
So for each order ID I need to SUM the LineTotal the Weight and the Cost.
Thanks for some pointers on how to go about this as I am getting errors with joins and silly results
Thanks
It should be very simple if I got the task right:
SELECT o.OrderID, o.ProductID, sum = (o.LineTotal + p.Weight + p.Cost)
FROM ORDERS o
INNER JOIN PRODUCTS p on o.ProductID = p.ProductID
Try this.
Select t3.OrderID , SUM(t3.SUM1) As TotalSum
From (Select t1.*,t2.Weight,t2.Cost,t1.LineTotal+t2.Weight+t2.Cost AS Sum1
from Orders t1
INNER JOIN Products t2
ON t1.ProductID=t2.ProductID ) t3
Group BY t3.OrderID