I have a program using SQL Server 2005 Express and I need some help looping through 2 tables to calculate inventory.
Table 1: stores all products with the inventory total upon setup
Table 2: stores the transactions against all products from table 1
How can I loop through all items in table 2 and subtract that amount from table 1 counts?
If I have my query like this then I get data for each product
SELECT
ii.ItemNum, ii.ItemName, ii.OzOnHand
FROM
dbo.InventoryItems ii
INNER JOIN
dbo.InventoryLog il ON ii.ItemNum = il.InvItemNum
WHERE
ii.active = 1
I need each occurrence from table 2 to be subtracted from table 1's total amount
This is an example of a join to an aggregated table (I think that is the best way to understand it):
SELECT ii.ItemNum, ii.ItemName, ii.OzOnHand, ii.OzOnHand - coalesce(il.cnt, 0)
FROM dbo.InventoryItems ii LEFT JOIN
(select il.InvItemNum, sum(OzRemoved) as cnt
from dbo.InventoryLog il
group by il.InvItemNum
) il
ON ii.ItemNum = il.InvItemNum
WHERE ii.active = 1;
The subquery groups everything in the log and counts the number of entries. If each entry could affect more than one item, then you would use something like sum(cnt) as cnt instead of count(*).
Then, the query uses a left outer join. This type of join ensures that all inventory items remain, even those with nothing in the log. Finally, the count is subtracted from what is available in set up. The coalesce() is to handle the situation where there are no matches in the log table. To avoid getting NULL, the NULL is turned into a 0.
Related
My task is to produce a report that shows the on time delivery of products to consumers. In essence I have achieved this. However, as you will see only some of the data is accurate.
Here is our test case: we have a sales order number '12312.' This sales order has had 5 partial shipments made (200 pieces each). The result is shown below from our DUE_DTS table.
Due Dates table data
The following code gives me the information I need (excluding due date information) to show the packing details of the 5 shipments:
DECLARE #t AS TABLE (
CUSTNAME char(35),
SONO char(10),
INVDATE date,
PACKLISTNO char(10),
PART_NO char(25),
SOBALANCE numeric(9,2)
)
INSERT INTO #t
SELECT DISTINCT c.CUSTNAME, s.SONO, p.INVDATE, p.PACKLISTNO, i.PART_NO, q.SOBALANCE
FROM [manex].[dbo].[SODETAIL]
INNER JOIN [manex].[dbo].[SOMAIN] s ON s.SONO = SODETAIL.SONO
INNER JOIN [manex].[dbo].[CUSTOMER] c ON c.CUSTNO = s.CUSTNO
INNER JOIN [manex].[dbo].[INVENTOR] i ON i.UNIQ_KEY = SODETAIL.UNIQ_KEY
INNER JOIN [manex].[dbo].[DUE_DTS] d ON d.SONO = s.SONO
INNER JOIN [manex].[dbo].[PLMAIN] p ON p.SONO = s.SONO
INNER JOIN [manex].[dbo].[PLDETAIL] q ON q.PACKLISTNO = p.PACKLISTNO
WHERE s.SONO LIKE '%12312'
SELECT * FROM #t
Here is a screenshot of the results from running this query:
Query Result
Now is when it should be time to join my due dates table (adding in the appropriate column(s) to my table definition and select statement) and make DATEDIFF comparisons to determine if shipments were on time or late. However, once I reference the due dates table, each of the 5 shipments is compared to all 5 dates in the due dates table, resulting in 25 rows. The only linking column DUE_DTS has is the SONO column. I've tried using DISTINCT and variations of the group by clause without success.
I've put enough together myself to figure joining the DUE_DTS table on SONO must be causing this to happen, as there are 5 instances of that value in the table (making it not unique) and a join should be based on a unique column. Is there a workaround for something like this?
You will need to use additional fields to join the records and reduce the results. You may need to link SONO to SODETAIL to DUE_DTS because the dates are tied to the items, not to the SONO.
Let me explain a little bit about what I am trying to do because I dont even know the vocab to use to ask. I have an Access 2016 database that records staff QA data. When a staff member misses a QA we assign a job aid that explains the process and they can optionally send back a worksheet showing they learned about what was missed. If they do all of these ina 3 month period they get a credit on their QA score. So I have a series of records all of whom have a date we assigned the work(RA1) and MAY have a work returned date(RC1).
In the below image "lavalleer" has earned the credit because both of her sheets got returned. "maduncn" Did not earn the credit because he didn't do one.
I want to create a query that returns to me only the people that are like "lavalleer". I tried hitting google and searched here and access.programmers.co.uk but I'm only coming up with instructions to use Not null statements. That wouldn't work for me because if I did a IS Not Null on "maduncn" I would get the 4 records but it would exclude the null.
What I need to do is build a query where I can see staff that have dates in ALL of their RC1 fields. If any of their RC1 fields are blank I dont want them to return.
Consider:
SELECT * FROM tablename WHERE NOT UserLogin IN (SELECT UserLogin FROM tablename WHERE RCI IS NULL);
You could use a not exists clause with a correlated subquery, e.g.
select t.* from YourTable t where not exists
(select 1 from YourTable u where t.userlogin = u.userlogin and u.rc1 is null)
Here, select 1 is used purely for optimisation - we don't care what the query returns, just that it has records (or doesn't have records).
Or, you could use a left join to exclude those users for which there is a null rc1 record, e.g.:
select t.* from YourTable t left join
(select u.userlogin from YourTable u where u.rc1 is null) v on t.userlogin = v.userlogin
where v.userlogin is null
In all of the above, change all occurrences of YourTable to the name of your table.
I have three tables: Clinic, Stock and StockLog.
I need to get all rows where Stock.stock < 5. I need to also show if an order has been placed and what amount it is; which is found in the table Stocklog.
The issue is that a user can set his stock level in the Stock table without placing an order which would go to Stocklog.
I need a query that can : return the rows in the Stock table and get the related order amounts in the Stocklog table. If no order has been placed in StockLog, then set amount to order amount to zero.
I have tried :
SELECT
Clinic.Name,
Stock.NameOfMedication, Stock.Stock,
StockLog.OrderAmount
FROM
Clinic
JOIN
Stock ON Stock.ClinicID = Clinic.ClinicID
JOIN
StockLog ON StockLog.StockID = Stock.StockID
WHERE
Stock.Stock <= 5
The issue with my query is that I lose rows which are not found in StockLog.
Any help on how to write this.
Thank you.
I am thinking the query should look like this:
SELECT c.Name, s.NameOfMedication, s.Stock,
COALESCE(sl.OrderAmount, 0) as OrderAmount
FROM Stock s LEFT JOIN
Clinic c
ON s.ClinicID = c.ClinicID LEFT JOIN
StockLog sl
ON sl.StockID = s.StockID
WHERE s.Stock <= 5 ;
You want to keep all rows in Stock (subject to the WHERE condition). So think: "make Stock the first table in the FROM and use LEFT JOIN for all the other tables."
If you want to keep all the rows that result from joining Clinic and Stock, then use a LEFT OUTER JOIN with StockLog. I don't know which SQL you're using (SQL Server, MySQL, PostgreSQL, Oracle), so I can't give you a precise example, but searching for "left outer join" in the relevant documentation should work.
See this Stack Overflow post for an explanation of the various kinds of joins.
I am working with our inventory database and I created a query (access 2002 format citrix run server) that counts all items of a type meeting a serviceability status and it runs as I had expected but when I try to display the item name Equipment.Model in the other table which is already left joined via the ID I get the error Tried to execute a query that does not include the specified expression 'model' as part of an aggregate function. I think I would expect to get this if I tried to list something like an asset ID but this is related to the items I am counting. The trouble piece is quoted. The tables are quite large but they are basically the below
Equipment
|EquipmentID|CompanyID|Model|Description|TypeID|...
Inventory
|InventoryID|EquipmentID|Serial|Status|...
SELECT Inventory.EquipmentID, "Equipment.Model", COUNT(*) AS Count
FROM (Inventory LEFT JOIN Equipment ON Inventory.EquipmentID=Equipment.EquipmentID)
WHERE Equipment.TypeID = 14
AND Inventory.Status NOT IN (4,5,6,8)
GROUP BY Inventory.EquipmentID;
SELECT Inventory.EquipmentID, Equipment.Model, COUNT(*) AS Count
FROM Inventory
LEFT JOIN Equipment ON Inventory.EquipmentID=Equipment.EquipmentID
WHERE Equipment.TypeID = 14
AND Inventory.Status NOT IN (4,5,6,8)
GROUP BY Inventory.EquipmentID, Equipment.Model;
You have to use all non aggregate columns in the GROUP BY clause.
You need to include equipment.Model in your group by clause
I am using the following query to obtain some sales figures. The problem is that it is returning the wrong data.
I am joining together three tables tbl_orders tbl_orderitems tbl_payment. The tbl_orders table holds summary information, the tbl_orderitems holds the items ordered and the tbl_payment table holds payment information regarding the order. Multiple payments can be placed against each order.
I am trying to get the sum of the items sum(mon_orditems_pprice), and also the amount of items sold count(uid_orderitems).
When I run the following query against a specific order number, which I know has 1 order item. It returns a count of 2 and the sum of two items.
Item ProdTotal ProdCount
Westvale Climbing Frame 1198 2
This order has two payment records held in the tbl_payment table, which is causing the double count. If I remove the payment table join it reports the correct figures, or if I select an order which has a single payment it works as well. Am I missing something, I am tired!!??
SELECT
txt_orditems_pname,
SUM(mon_orditems_pprice) AS prodTotal,
COUNT(uid_orderitems) AS prodCount
FROM dbo.tbl_orders
INNER JOIN dbo.tbl_orderitems ON (dbo.tbl_orders.uid_orders = dbo.tbl_orderitems.uid_orditems_orderid)
INNER JOIN dbo.tbl_payment ON (dbo.tbl_orders.uid_orders = dbo.tbl_payment.uid_pay_orderid)
WHERE
uid_orditems_orderid = 61571
GROUP BY
dbo.tbl_orderitems.txt_orditems_pname
ORDER BY
dbo.tbl_orderitems.txt_orditems_pname
Any suggestions?
Thank you.
Drill down Table columns
dbo.tbl_payment.bit_pay_paid (1/0) Has this payment been paid, yes no
dbo.tbl_orders.bit_order_archive (1/0) Is this order archived, yes no
dbo.tbl_orders.uid_order_webid (integer) Web Shop's ID
dbo.tbl_orders.bit_order_preorder (1/0) Is this a pre-order, yes no
YEAR(dbo.tbl_orders.dte_order_stamp) (2012) Sales year
dbo.tbl_orders.txt_order_status (varchar) Is the order dispatched, awaiting delivery
dbo.tbl_orderitems.uid_orditems_pcatid (integer) Product category ID
It's a normal behavior, if you remove grouping clause you'll see that there really are 2 rows after joining and they both have 599 as a mon_orditems_pprice hence the SUM is correct. When there is a multiple match in any joined table the entire output row becomes multiple and the data that is being summed (or counted or aggregated in any other way) also gets summed multiple times. Try this:
SELECT txt_orditems_pname,
SUM(mon_orditems_pprice) AS prodTotal,
COUNT(uid_orderitems) AS prodCount
FROM dbo.tbl_orders
INNER JOIN dbo.tbl_orderitems ON (dbo.tbl_orders.uid_orders = dbo.tbl_orderitems.uid_orditems_orderid)
INNER JOIN
(
SELECT x.uid_pay_orderid
FROM dbo.tbl_payment x
GROUP BY x.uid_pay_orderid
) AS payments ON (dbo.tbl_orders.uid_orders = payments.uid_pay_orderid)
WHERE
uid_orditems_orderid = 61571
GROUP BY
dbo.tbl_orderitems.txt_orditems_pname
ORDER BY
dbo.tbl_orderitems.txt_orditems_pname
I don't know what data from tbl_payment you are using, are any of the columns from the SELECT list actually from tbl_payment? Why is tbl_payment being joined?