Duplicated records from SELECT DISTINCT COUNT statement - sql

I have 2 tables with the following structure:
------------------------------------
| dbo.Katigories | dbo.Products |
|-----------------|------------------|
| product_id | product_id |
| Cat_Main_ID | F_material |
| Cat_Sub_ID | |
What I am trying to accomplish is the following:
I want to COUNT how many unique products (from the Products table) have as Cat_Main_ID=111, have as Cat_Sub_ID=222, and have as F_material=10
I have tried the following SELECT COUNT statement:
SELECT DISTINCT COUNT(*) AS METR_AN_EXEI_001
FROM dbo.Products P
INNER JOIN dbo.Katigories K
ON P.product_id = K.product_id
AND
K.Cat_Main_ID = 111
AND
P.F_material = 10
AND
K.Cat_Sub_ID = 222
The above statement is working, and counts the correct products, but is giving me duplicated records.
For example: When a product belongs to only 1 category the result of my count is correct and I am getting the number 1 as a result. But when a product belongs to more than one category then the result of the count is incorrect, depending on how many categories the product belongs. I know that the reason for the duplicates is that some of my products belong simultaneously to more than one category or sub category, so I am getting incorrect count results.
It will be truly appreciated if someone could help me with the syntax of my COUNT statement, in order to get the correct number of products (without duplicates) from my Products table.
!!!!!!!!!!!!!-----------------------------------------!!!!!!!!!!
Dear all.
Please forgive me for the inconvenience!
I'm so stupid, that I was placing your code in the wrong point in my page. Eventually almost all of the suggestions were correct.
Now I have a problem and I do not know which of the answers I choose to be right. All of them are correct!
A very big thank to all of you and I apologize again to all of you.

I suppose grouping should be approporate compared to distinct in this case
SELECT product_id, COUNT(product_id)
METR_AN_EXEI_001
FROM dbo.Products P
INNER JOIN dbo.Katigories K
ON P.product_id = K.product_id
AND
K.Cat_Main_ID = 111
AND
P.F_material = 10
AND
K.Cat_Sub_ID = 222
GROUP BY P.product_id

Can u try it using subselect to count, like this:
DECLARE #Katigories TABLE ( product_id int , Cat_Main_ID int , Cat_Sub_ID int )
DECLARE #Products TABLE( product_id int , F_material int )
INSERT INTO #Products (product_id,F_material) VALUES (1, 10)
INSERT INTO #Products (product_id,F_material) VALUES (2, 10)
INSERT INTO #Products (product_id,F_material) VALUES (3, 15)
INSERT INTO #Katigories ( product_id ,Cat_Main_ID ,Cat_Sub_ID ) VALUES ( 1,111, 222 )
INSERT INTO #Katigories ( product_id ,Cat_Main_ID ,Cat_Sub_ID ) VALUES ( 1,123, 223 )
INSERT INTO #Katigories ( product_id ,Cat_Main_ID ,Cat_Sub_ID ) VALUES ( 1,444, 222 )
INSERT INTO #Katigories ( product_id ,Cat_Main_ID ,Cat_Sub_ID ) VALUES ( 2,133, 223 )
SELECT
P.product_id,
(SELECT COUNT(*) FROM #Katigories WHERE product_id = P.product_id AND Cat_Main_ID=111 AND Cat_Sub_ID=222 AND P.F_material=10) AS METR_AN_EXEI_001
FROM #Products P

If you need to count distinct products use count(distinct ...). Something like this.
SELECT COUNT(DISTINCT P.product_id) AS METR_AN_EXEI_001
FROM dbo.Products P
INNER JOIN dbo.Katigories K
ON P.product_id = K.product_id
AND
K.Cat_Main_ID = 111
AND
P.F_material = 10
AND
K.Cat_Sub_ID = 222

Related

Sum Quantity and Filter Results

I have the following table with order id's and quantities. I need to be able to sum the quantity and retrieve the id's that that equal less than the provided number.
| id | quantity |
|------|----------|
| 100 | 1 |
| 200 | 25 |
| 300 | 15 |
For example, I need the id's where the sum of quantity equals less than 25.
When I try the following it only provides me the first id (100).
Select *
from (
select *,
SUM (Quantity) OVER (ORDER BY Id) AS SumQuantity
from dbo.Orders
) as A
where SumQuantity <= 25
Is it possible to adjust this query where it will provide me id 100 and 300, since the sum total of those orders is less than 25?
I know I can use a where clause on for quantity less than 25, but the important thing here is I need to be able to sum the quantity and pull id's that give me less than the provided number.
Thank you in advance!
Perhaps you want to order by the quantity instead of id?
Select o.*
from (select o.*, SUM (Quantity) OVER (ORDER BY quantity) AS SumQuantity
from dbo.Orders
) o
where SumQuantity <= 25;
This chooses the smallest values so you will get the most rows.
Group by Id and set the condition in the HAVING clause:
select Id, SUM(Quantity) AS SumQuantity
from Orders
group by Id
having SUM(Quantity) <= 25
See the demo.
Results:
Id | SumQuantity
100 | 1
200 | 25
300 | 15
If you want to include all the columns you can modify your query to not ORDER BY id but PARTITION BY id:
select *
from (
select *,
SUM (Quantity) OVER (PARTITION BY Id) AS SumQuantity
from Orders
) as A
where SumQuantity <= 25
For this dataset:
CREATE TABLE Orders([id] varchar(6), [quantity] int);
INSERT INTO Orders([id], [quantity])VALUES
('100', '1'), ('100', '2'),
('200', '25'), ('200', '3'),
('300', '15'), ('300', '5');
Results:
id | quantity | SumQuantity
100 | 1 | 3
100 | 2 | 3
300 | 15 | 20
300 | 5 | 20
See the demo.
Setup:
Your threshold can vary, so let's make it into a variable:
declare #threshold int = 25;
But I also imagine that your table values can vary, like if we add another row only having a quantity of 2:
declare #orders table (id int, quantity int)
insert #orders values (100,1), (200,25), (300,15), (400, 2);
Solution:
For this, we'll need a recursive kind of cross joining:
with
traverse as (
select ids = convert(nvarchar(255), id),
id,
quantity
from #orders
where quantity < #threshold
union all
select ids =
convert(nvarchar(255), tv.ids + ',' +
convert(nvarchar(255), o.id)),
o.id,
quantity = tv.quantity + o.quantity
from traverse tv
cross join #orders o
where tv.id < o.id
and tv.quantity + o.quantity < #threshold
)
select t.ids, t.quantity
from traverse t;
which will produce:
Explanation:
The above code is an algorithm that builds a tree. It starts with your base id's and quantities as nodes (the anchor part of the CTE). It trims anything not meeting the threshold.
It then adds edges by cross joining with orders table again (the recursive part of the CTE), but it only includes the following:
Id's that are greater than the last id considered in the current node (this is so that we avoid duplicate considerations, such as ids = '300,400' and ids = '400,300').
Ids where the sum of quantities is less than the threshold.
Warnings:
But beware, the type of problem you're considering will have computational complexity considerations. But because of the trimming conditions, it will be more efficient than doing all the cross joins first and then filtering the result set at the end.
Also, keep in mind that you may get rows in your table where there is no single set of numbers that will sum up to less than 25. Rather, you can get different paths to that sum. The way I produce the results here will help you identify such a situation.
cross join is perfect for this task, try:
declare #tbl table (id int, quantity int);
insert into #tbl values
(100, 1), (200, 25), (300, 15), (400, 10);
select distinct case when t1.id > t2.id then t1.id else t2.id end,
case when t1.id < t2.id then t1.id else t2.id end
from #tbl t1
cross join #tbl t2
where t1.id <> t2.id
and t1.quantity + t2.quantity < 25

How would l write SQL to label quantities until they run out?

I would like to label quantities (in the quantity table) using the labels assigned (see label assignment table) until the quantity goes to 0. Then I know that I am done labeling that particular ID.
label assignment table is as follows:
ID | Label | Quantity
1 aaa 10
1 bbb 20
2 ccc 20
And my quantity table:
ID | Total Quantity
1 60
2 20
And I would like to get the following result:
ID | Label | Quantity
1 aaa 10 (read from reference table, remaining 50)
1 bbb 20 (read from reference table, remaining 30)
1 [NULL] 30 (no label in reference table, remaining 0)
2 ccc 20 (read from reference table, remaining 0)
You can do it with a simple JOIN and UNION operation so as to include 'not covered' quantities:
SELECT la.ID, la.Label, la.Quantity
FROM label_assignment AS la
INNER JOIN quantity AS q ON la.ID = q.ID
UNION
SELECT q.ID, NULL AS Label, q.TotalQuantity - la.TotalQuantity
FROM quantity AS q
INNER JOIN (
SELECT ID, SUM(Quantity) AS TotalQuantity
FROM label_assignment
GROUP BY ID
) AS la ON q.ID = la.ID AND q.TotalQuantity > la.TotalQuantity
Demo here
DECLARE #PerLabelQuantity TABLE(Id int, Label varchar(10), Quantity int);
INSERT INTO #PerLabelQuantity
VALUES (1, 'aaa', 10), (1, 'bbb', 20), (2, 'ccc', 20);
DECLARE #QuantityRequired TABLE(Id int, TotalQuantity int);
INSERT INTO #QuantityRequired
VALUES (1, 60), (2, 20);
SELECT t.Id,
CASE WHEN o.Overflowed = 1 THEN NULL ELSE t.Label END AS Label,
CASE WHEN o.Overflowed = 1 THEN t.QuantityStillNeeded
WHEN t.QuantityStillNeeded < 0 THEN t.Quantity + t.QuantityStillNeeded
ELSE t.Quantity END AS Quantity
FROM (
SELECT p.Id, p.Label, p.Quantity,
MAX(p.Label) OVER (PARTITION BY p.Id) AS LastLabel,
r.TotalQuantity - SUM(p.Quantity)
OVER (PARTITION BY p.Id
ORDER BY Label
ROWS UNBOUNDED PRECEDING) AS QuantityStillNeeded
FROM #PerLabelQuantity p
INNER JOIN #QuantityRequired r ON p.Id = r.Id) t
INNER JOIN (VALUES (0), (1)) o(Overflowed)
ON t.LastLabel = t.Label AND t.QuantityStillNeeded > 0 OR Overflowed = 0
WHERE t.QuantityStillNeeded > -t.Quantity; -- Remove this if you want labels with
-- 0 quantity used, but you'll need to tweak
-- the CASE expression for Quantity
The subquery calculates a set of used up labels and how many items remain afterward. If there is any quantity remaining after the last label, then we need to insert a row in the result set. To do this, I join on a two-element table but the join condition is only true when we are at the last label and there is quantity remaining. This is probably a confusing way to do this, and we could combine the UNION from George's answer with the subquery from mine to avoid this Overflow table.
Here's the changed (and probably preferable) query:
SELECT Id,
Label,
CASE WHEN QuantityStillNeeded < 0 THEN Quantity + QuantityStillNeeded
ELSE Quantity END AS Quantity
FROM (SELECT p.Id, p.Label, p.Quantity,
r.TotalQuantity - SUM(p.Quantity)
OVER (PARTITION BY p.Id
ORDER BY Label
ROWS UNBOUNDED PRECEDING) AS QuantityStillNeeded
FROM #PerLabelQuantity p
INNER JOIN #QuantityRequired r ON p.Id = r.Id) t
WHERE t.QuantityStillNeeded > -t.Quantity
UNION ALL
SELECT q.Id, NULL AS Label, q.TotalQuantity - la.TotalQuantity AS Quantity
FROM #QuantityRequired AS q
INNER JOIN (
SELECT Id, SUM(Quantity) AS TotalQuantity
FROM #PerLabelQuantity
GROUP BY Id) la ON q.ID = la.ID
WHERE q.TotalQuantity > la.TotalQuantity
Simplest answer I think, after getting ideas from the other answers: Just create a "FAKE" label for the missing amount:
DECLARE #PerLabelQuantity TABLE(Id int, Label varchar(10), Quantity int);
INSERT INTO #PerLabelQuantity
VALUES (1, 'aaa', 10), (1, 'bbb', 20), (2, 'ccc', 20);
SELECT *
FROM #PerLabelQuantity
DECLARE #QuantityRequired TABLE(Id int, TotalQuantity int);
INSERT INTO #QuantityRequired
VALUES (1, 60), (2, 20);
SELECT *
FROM #QuantityRequired
-- MAKE A FAKE LABEL LET'S CALL IT [NULL] WITH THE AMOUNT THAT IS NOT LABELED
-- i.e. WITH THE REMAINING AMOUNT
-- Probably should be done by copying the original data and the following
-- into a temp table but this is just for proof of concept
INSERT INTO #PerLabelQuantity( Id, Label, Quantity )
SELECT q.ID,
NULL,
ISNULL(q.TotalQuantity - p.TotalQuantityLabeled, q.TotalQuantity)
FROM #QuantityRequired q
LEFT JOIN (SELECT p.ID, SUM(Quantity) AS TotalQuantityLabeled
FROM #PerLabelQuantity p
GROUP BY p.Id) p ON
p.ID = q.ID
AND q.TotalQuantity - p.TotalQuantityLabeled > 0
SELECT *
FROM #PerLabelQuantity p

SQL query help on joining 2 tables

I have a table of items and another table with item groups (not all items are in a group).
I want to get all from the items table and if it exists in the groups table, I only want to get one item per group please show me SQL query for this.
I am defining a sample items and itemGroups table.
items
itemID, itemName, ItemDescription
itemGroups
itemGroupId, itemGroupname, ItemGroupDescription, itemID
Now to get all items from the item table if it exists in the itemsgroups table you have to compare the itemID which should your primary key in items table and compare it to the foreigh key for items which should be stored in the item groups table. So the SQL query could look like this
select * from items
where itemID in (select itemID from itemGroups)
Your coloumns names might be different but this is the general concept
If you have a groupid stored with each item row, try this:
select *
from items i
where groupid is null or itemid = (
select min(itemid) from items i2
where i2.groupid = i.groupid
)
If you didn't establish the linkage that way you might have to take this approach:
select *
from items i
where itemid = (
select coalesce(min(ig.itemid), i.itemid)
from itemgroups ig
where ig.groupid = (select groupid from itemgroups ig2 where ig2.itemid = i.itemid)
)
Assuming you have a table structure similar to this:
create table items (itemid int, itemname varchar2(50));
insert all
into items (itemid, itemname) values (1,'Apples')
into items (itemid, itemname) values (2,'Bananas')
into items (itemid, itemname) values (3,'Ford')
into items (itemid, itemname) values (4,'Honda')
into items (itemid, itemname) values (5,'Football')
select * from dual;
create table itemgroups (itemgroupid int, itemid int, itemgroupname varchar2(50));
insert all
into itemgroups (itemgroupid, itemid, itemgroupname) values (1,1,'Fruits')
into itemgroups (itemgroupid, itemid, itemgroupname) values (1,2,'Fruits')
into itemgroups (itemgroupid, itemid, itemgroupname) values (2,3,'Cars')
into itemgroups (itemgroupid, itemid, itemgroupname) values (2,4,'Cars')
select * from dual;
You could do a left join to get all the records from your items table (but only get one item per group). Then, do some kind of aggregation like MAX or MIN to only get one value per group like this:
select min(i.itemid) as itemid,
min(i.itemname) as itemname,
ig.itemgroupid,
ig.itemgroupname
from items i
left join itemgroups ig on i.itemid = ig.itemid
group by ig.itemgroupid,
ig.itemgroupname
order by min(i.itemid);
Result:
+--------+----------+-------------+---------------+
| itemid | itemname | itemgroupid | itemgroupname |
+--------+----------+-------------+---------------+
| 1 | Apples | 1 | Fruits |
| 3 | Ford | 2 | Cars |
| 5 | Football | (null) | (null) |
+--------+----------+-------------+---------------+
SQL Fiddle Demo

SQL ALL IN clause

I have been searching for this, but didn't find anything special.
Is it possible to have an SQL query which will act like ALL IN? To better explain, Here is a table structure.
Orders table
OrderItem table (having several columns, but mainly ProductID, OrderID)
ProductGroup table (several columns, but mainly GroupID and ProductID)
I want to write a query which will select all those order which belongs to a specific ProductGroup. So if I have a group named "XYZ" with ID = 10. It has One ProductID in it. Say ProductID01
An order came in with two order items. ProductID01 and ProductID02. To find all orders in the specific Product Group I can use a simple SQL like
SELECT bvc_OrderItem.ProductID, bvc_OrderItem.OrderID
From bvc_OrderItem
INNER JOIN bvc_Product_Group_Product with (nolock) ON bvc_OrderItem.ProductID = bvc_Product_Group_Product.ProductID
WHERE bvc_Product_Group_Product.GroupID = 10
Or I can write using an IN Clause
SELECT bvc_OrderItem.ProductID, bvc_OrderItem.OrderID
From bvc_OrderItem
WHERE ProductID IN (
SELECT ProductID FROM bvc_Product_Group_Product WHERE GroupID=10
)
However, This will return all orders where one or more ProductIDs are part of the product group. I need to return the order row ONLY if ALL of the order items are part of the Product Group
So basically, I need an IN Clause which will considered matched if ALL of the values inside IN Clause matches the rows in bvc_OrderItem.
Or if we are using the Join, then the Join should only succeed if ALL rows on the left have values in the corresponding right table.
If I could write it more simply, I would write it like this
Select ID FROM Table WHERE ID IN (1, 2, 3, 4)
and if the table contains all rows with ids 1,2,3,4; it should return success. If any of these IN values is missing, it should return false and nothing should be selected.
Do you think it is possible? Or there is a workaround to do that?
You can get the list of orders in a variety of ways, such as:
SELECT oi.OrderID
FROM bvc_OrderItem oi JOIN
bvc_Product_Group_Product pgp
ON oi.ProductID = pgp.ProductId AND
pgp.GroupID = 10
GROUP BY oi.OrderID
HAVING COUNT(DISTINCT oi.ProductID) = (SELECT COUNT(*)
FROM bvc_Product_Group_Product
WHERE GroupID = 10
);
Getting the specific products requires an additional join. In most cases, the list of orders is more useful.
The problem with your ALL IN syntax is that it doesn't do what you want. You want to select orders. The syntax:
SELECT bvc_OrderItem.ProductID, bvc_OrderItem.OrderID
From bvc_OrderItem
WHERE ProductID ALL IN (SELECT ProductID
FROM bvc_Product_Group_Product
WHERE GroupID = 10
)
This doesn't specify that you intend for the grouping to be by OrderId, as opposed to some other level.
More fundamentally, though, the SQL language is inspired by relational algebra. The constructs of SELECT, JOIN, WHERE, and GROUP BY directly relate to relational algebra fundamental constructs. The notion of ALL IN -- although sometimes useful -- can be expressed using the more basic building blocks.
You can do it by this tricky statement:
DECLARE #Items TABLE
(
OrderID INT ,
ProductID INT
)
DECLARE #Groups TABLE
(
ProductID INT ,
GroupID INT
)
INSERT INTO #Items
VALUES ( 1, 1 ),
( 1, 2 ),
( 2, 1 ),
( 3, 3 ),
( 3, 4 )
INSERT INTO #Groups
VALUES ( 1, 10 ),
( 2, 10 ),
( 3, 10 ),
( 4, 15 )
SELECT OrderID
FROM #Items i
GROUP BY OrderID
HAVING ( CASE WHEN 10 = ALL ( SELECT gg.GroupID
FROM #Items ii
JOIN #Groups gg ON gg.ProductID = ii.ProductID
WHERE ii.OrderID = i.OrderID ) THEN 1
ELSE 0
END ) = 1
Output:
OrderID
1
2
Also(this is better):
SELECT OrderID
FROM #Items i
JOIN #Groups g ON g.ProductID = i.ProductID
GROUP BY OrderID
HAVING MIN(g.GroupID) = 10
AND MAX(g.GroupID) = 10

Creating a SQL query that performs math with variables from multiple tables

Here's an example of what I've attempted thus far:
A mockup of what the tables look like:
Inventory
ID | lowrange | highrange
-------------------------------
1 | 15 | 20
2 | 21 | 30
Audit (not used in this query asides from the join)
MissingOrVoid
ID | Item | Missing | Void
---------------------------------
1 | 17 | 1 | 0
1 | 19 | 1 | 0
The most recent query I've attempted to use:
SELECT I.*,
SUM(
(I.HIGHRANGE - I.LOWRANGE + 1)
- (Count(M.Missing) from M where M.ID = I.ID)
- (Count(M.Void) from M where M.ID = I.ID)) AS Item_Quantity
FROM Inventory I
JOIN Audit A
ON A.ID = I.ID
JOIN MissingOrVoid M
ON M.ID = I.ID
The result should be:
ID | lowrange | highrange | Item_Quantity
-----------------------------------------------
1 | 15 | 20 | 4
2 | 21 | 30 | 10
I can't remember exactly where I've made changes, but in a previous attempt the error message received prior was "Cannot perform an aggregate function on an expression containing an aggregate or a subquery." Currently the error is incorrect syntax near "from" (the one beside M.Missing but in my minimal knowledge of SQL, it appears that these syntax issues cause an outright failure and there may be underlying issues with the query that aren't visible until all of the syntax problems are fixed).
The part where I'm really bombing on is obviously the SUM() section. I am far from a database architect so could someone explain how to correctly perform this and possibly point me in the direction of a resource to learn about this type of function?
Thanks
You almost had it right. I am guessing missing/void are BIT types, which you cannot SUM directly.
SELECT I.*,
(I.HIGHRANGE - I.LOWRANGE + 1)
- (select Count(nullif(M.Missing,0)) from MissingOrVoid M where M.ID = I.ID)
- (select Count(nullif(M.Void,0)) from MissingOrVoid M where M.ID = I.ID)
AS Item_Quantity
FROM Inventory I
If an item cannot both be missing and void, then
SELECT I.*,
I.HIGHRANGE - I.LOWRANGE + 1
- (select Count(case when M.Missing=1 or M.Void=1 then 1 end)
from MissingOrVoid M where M.ID = I.ID)
AS Item_Quantity
FROM Inventory I
In fact, if it is only present in MissingOrVoid when it is missing or void, then the CASE in the above query will always be true, so this simplifies to
SELECT I.*,
I.HIGHRANGE - I.LOWRANGE + 1
- (select Count(*) from MissingOrVoid M where M.ID = I.ID)
AS Item_Quantity
FROM Inventory I
Initially I have a question as to whether you need to sum those values. If your inventory table has one row per item, that shouldn't be necessary. I'll assume that your table can have multiple rows for a given item, though, and proceed from there.
I think the issue is just a problem with the construction of the subquery. I haven't tested this, but I think it should look more like:
select I.ID,
I.Item,
SUM(I.HighRange - I.LowRange + 1)
- (
select SUM(M.Missing + M.Void)
from dbo.Audit A
where A.ID = I.ID
)
from Inventory I
group by I.ID, I.Item
Is this what you're trying to do? I'm not sure what the numbers in the missing and void columns are unless they're just flags...
SELECT I.*,
((I.highrange - I.lowrange + 1)
- SUM(M.Missing)
- SUM(M.Void)) AS Item_Quantity
FROM Inventory I
JOIN MissingOrVoid M
ON M.ID = I.ID
The following query works. This assumes there is only one highrange and lowrange for each id.
CREATE TABLE #Inventory (ID INT,Lowrange INT,highrange INT)
CREATE TABLE #MissingOrVoid (Id INT,item INT, missing INT, void INT)
INSERT #Inventory
( ID, Lowrange, highrange )
VALUES ( 1, -- ID - int
15, -- Lowrange - int
20 -- highrange - int
)
INSERT #Inventory
( ID, Lowrange, highrange )
VALUES ( 2, -- ID - int
21, -- Lowrange - int
30 -- highrange - int
)
INSERT #MissingOrVoid
( Id, item, missing, void )
VALUES ( 1, -- Id - int
17, -- item - int
1, -- missing - int
0 -- void - int
)
INSERT #MissingOrVoid
( Id, item, missing, void )
VALUES ( 1, -- Id - int
19, -- item - int
1, -- missing - int
0 -- void - int
)
SELECT #Inventory.ID,
#Inventory.highrange,
#Inventory.Lowrange,
highrange-Lowrange+1
-SUM(ISNULL(missing,0))
-SUM(ISNULL(void,0)) AS ITEM_QUANTITY
FROM #Inventory
left JOIN #MissingOrVoid ON #Inventory.ID = #MissingOrVoid.Id
GROUP BY #Inventory.ID,#Inventory.highrange,#Inventory.Lowrange
DROP TABLE #Inventory
DROP TABLE #MissingOrVoid
I'd say this would work :
SELECT I.ID,I.Lowrange as Lowrange,
I.highrange as Highrange,
Highrange-Lowrange+1-COUNT(J.missing)-COUNT(J.void) AS ITEM_QUANTITY
FROM Inventory I
left JOIN ( select missing as missing, void as void, id from MissingOrVoid
) J
ON I.ID = J.Id
JOIN Audit A
ON A.ID = I.ID
GROUP BY I.ID,Highrange,Lowrange
But it looks like what RemoteSojourner suggested a lot (and his one is also more esthetic).
I'm going to give the derived table approach as it could be faster than a correlated subquery (which run row by row)
SELECT I.*,
I.HIGHRANGE - I.LOWRANGE + 1 - MissingVoidCount AS Item_Quantity
FROM Inventory I
JOIN
(SELECT ID,Count(*) AS MissingVoidCount FROM MissingOrVoid GROUP BY ID) M
on M.ID = I.ID
Of course in real life, I would never use select *. You also could use a CTE approach.
;WITH MissingVoid(ID, MissingVoidCount) AS
(
SELECT ID, Count(*) FROM MissingOrVoid GROUP BY ID
)
SELECT
I.*,
I.HIGHRANGE - I.LOWRANGE + 1 - MissingVoidCount AS Item_Quantity
FROM Inventory I
JOIN MissingVoid M
on M.ID = I.ID