I have a query that I can get right. So here is my scenario:
I have 1 parent table called 'Couriers', then 1 courier may have 1 or many 'Messengers' then Couriers has a 2nd table called 'Control' where I control how many messengers can a courier have.
The structures goes like this:
'Courier'
idCourier(pk)
181
162
101
'Messenger'
idMessenger (pk), idCourier (fk)
1 101
2 181
3 101
4 101
5 162
'Control'
idCourier(pk, fk), maxNumberOfMessengers, idSupervisor
181 20 146
181 20 149
162 10 129
162 10 130
162 10 138
162 10 139
101 0 83
101 0 86
So with the next query:
SELECT COUNT(G.idCourier) AS NUM_COURIER, G.idMessenger, SUM(T.maxNumberOfMessengers) AS TOTAL
FROM Courier M
LEFT JOIN Messenger G ON M.idCourier = G.idCourier
LEFT JOIN Control T ON T.idCourier = G.idCourier AND T.idCourier = M.idCourier
GROUP BY G.idMessenger
I get:
NUM_COURIER G.idMessenger TOTAL
2 181 40
4 162 40
6 101 0
And I was expecting:
NUM_COURIER G.idMessenger TOTAL
1 181 40
3 162 40
1 101 0
What am I missing or doing wrong in my query???
Thanks in advance!!:)
How many messengers do we have per courier?
SELECT idCourier, COUNT(*) CourierCount FROM Messenger GROUP BY idCourier
Now join this and compare to control:
SELECT CO.idcourier,
CO.maxnumberofmessengers,
CC.couriercount
FROM control CO
INNER JOIN (SELECT idcourier,
Count(*) CourierCount
FROM messenger
GROUP BY idcourier) CC
ON CC.idcourier = CO.idcourier
I am not familiar with Oracle so I hope this is helpful as this is how you would do this with MS Sql.
SELECT COUNT(G.idCourier) AS NUM_COURIER,
m.idcourier,
(
SELECT sum(maxnumberofmessengers)
FROM CONTROL con
WHERE con.idcourier = m.idcourier
GROUP BY con.idcourier
) AS TOTAL
FROM Courier M
JOIN Messenger G ON M.idCourier = G.idCourier
GROUP BY m.idcourier
My previous query incorrectly assumed that we needed to count the number of courier entries in the Control table, while also summing the number of messengers.
SELECT COUNT(T.idCourier) AS NUM_COURIER, T.idCourier, SUM(T.maxNumberOfMessengers) AS TOTAL
FROM Control T
GROUP BY T.idCourier
I see now that we need to get the number of Messengers. So a sub query is required:
SELECT Couriers.CourierCount as NUM_COURIER, Couriers.idCourier, MaxMessengers.TOTAL
FROM
(SELECT M.idCourier, COUNT(*) CourierCount FROM Messenger M GROUP BY M.idCourier) Couriers
JOIN(SELECT C.idCourier, SUM(C.maxNumberOfMessengers) AS TOTAL From Control C GROUP BY C.idCourier) MaxMessengers
ON MaxMessengers.idCourier = Couriers.idCourier
courier table and control table is connected by idcourier and courier table and messenger table is also connected by idcourier but messenger table and control table are not connected at all. Another thing is idcourier alone can not be pk in control table as per its data. it has duplicate data.
select t1.idcourier,cnt,sm
from (
SELECT COURIER.IDCOURIER,
COUNT(MESSENGER.IDMESSENGER) as cnt
FROM COURIER
left JOIN MESSENGER
ON COURIER.IDCOURIER = MESSENGER.IDCOURIER
GROUP BY COURIER.IDCOURIER ) t1
inner join (
SELECT COURIER.IDCOURIER,
SUM(CONTRAL.MAXNUMBEROFMESSENGERS) as sm
FROM COURIER
left JOIN CONTRAL
ON COURIER.IDCOURIER = CONTRAL.IDCOURIER
GROUP BY COURIER.IDCOURIER )t2 on t1.idcourier = t2.idcourier;
Related
So the Situation is that I have 3 Tables, 2 of which are "helper" tables while the other one is the main table I'm trying to get distinct IDs out of:
Main Table dbo.recipes has columns ID, Name and some others such as:
ID NAME
5 Veggie Cassola
6 Mozzarella Penne
7 Wiener Schnitzel with Fries
8 Grilled Salmon with Rice
9 Greek style Salad
The helpers are dbo.stock:
ID_USER ID_INGREDIENT
1 225
1 585
1 607
1 643
1 763
1 874
1 937
1 959
1 960
2 225
2 246
2 331
2 363
2 511
2 585
and dbo.content:
ID_INGREDIENT ID_RECIPE
98 5
196 5
333 5
607 5
608 5
613 5
627 5
643 5
763 5
874 5
951 5
956 5
225 6
585 6
607 6
Basically the dbo.stock is the inventory of ingredients that the user has hence user id and ingredient id.
The dbo.content is the ingredients needed to make a certain dish.
What I want to query is ONLY recipes that the user actually has the ingredients for, so that means that all the recipes which have ALL their ingredients matched (to a certain user) should be returned. The code that I have at the moment for my procedure is as follows:
SELECT * FROM [dbo].[recipe]
WHERE [recipe].[id] NOT IN
(SELECT DISTINCT [content].[id_recipe] FROM [dbo].[content]
WHERE [content].[id_ingredient] NOT IN
(SELECT [stock].[id_ingredient] FROM [dbo].[stock]
WHERE [stock].[id_user] = #userID))
which works, but I doubt this is the best way to achieve this. Is there a better way to reach the same?
MS SQL Server Express 2019
Basically, you want to find all the recipes where there isn't an ingredient in content that is not in stock. It's not the way you think about it in English, but it leads to this if you think about it that way in SQL:
DECLARE #userID int = 1;
SELECT ID, NAME
FROM dbo.recipe AS r
WHERE NOT EXISTS
(
SELECT id_ingredient FROM dbo.content WHERE id_recipe = r.ID
EXCEPT
SELECT id_ingredient FROM dbo.stock WHERE id_user = #userID
);
However, this query is more along the lines of yours, just without the expensive DISTINCTs found in both of the above plans (EXCEPT is sneaky like that), so is probably the best option:
DECLARE #userID int = 1;
SELECT ID, NAME
FROM dbo.recipe AS r
WHERE NOT EXISTS
(
SELECT 1 FROM dbo.content AS c
WHERE id_recipe = r.ID AND NOT EXISTS
(
SELECT 1 FROM dbo.stock
WHERE id_ingredient = c.id_ingredient
AND id_user = #userID
)
);
Example db<>fiddle
This is a classic Relational Division With Remainder question.
#AaronBertrand has given you a couple good solutions. Here is another one that is often used.
DECLARE #userID int = 1;
SELECT
r.Id,
r.Name
FROM dbo.recipe AS r
JOIN dbo.content AS c ON c.id_recipe = r.ID
LEFT JOIN dbo.stock AS s ON s.id_ingredient = c.id_ingredient
AND s.id_user = #userID
GROUP BY
r.Id,
r.Name
HAVING COUNT(*) = COUNT(s.id_ingredient);
This will join everything together (left-joining the stock), group by the recipe and return only those groupings that have the same number of rows as there are non-null stock rows. In other words, every content must match, and there must be at least one content.
There is a semantic difference: if you wanted also all recipes which have no `content, you can change it slightly.
DECLARE #userID int = 1;
SELECT
r.Id,
r.Name
FROM dbo.recipe AS r
LEFT JOIN dbo.content AS c ON c.id_recipe = r.ID
LEFT JOIN dbo.stock AS s ON s.id_ingredient = c.id_ingredient
AND s.id_user = #userID
GROUP BY
r.Id,
r.Name
HAVING COUNT(c.id_recipe) = COUNT(s.id_ingredient);
db<>fiddle
I'm trying to reduce storage with packages included in sold products.
Products can have different amounts of same packages and invoice can have different amounts of products in separate lines. Action should be performed against certain invoiceID.
Simplified tables (summary is not column, just added to ease up calculation):
Inv_rows
InvID ProdID Qty *Summary*
999 100 2 *100 = 10*
999 101 2 *101 = 2*
999 102 2 *102 = 2*
999 103 2 *103 = 10*
999 100 8
999 103 8
Pack_to_prod
ProdID PackID Qty
100 A 2 *A = 20*
100 B 1 *B = 10*
101 A 1 *A = 2*
101 B 1 *B = 2*
102 A 3 *A = 6*
103 B 2 *B = 20*
Storage
ItemID Qty
A 100 *A = 28*
B 100 *B = 32*
**Desired Result**
Storage
ItemID Qty
A 72 *(100-28)*
B 68 *(100-32)*
What I have tried is:
UPDATE Storage
SET Storage.Qty = Storage.Qty -
(SELECT SUM (Inv_rows.Qty * Pack_to_prod.Qty) FROM Inv_rows
WHERE Inv_rows.ProdID IN (SELECT ProdID FROM Pack_to_prod
WHERE Pack_to_prod.PackID=Storage.ItemID) AND Inv_rows.InvId = 999
)
FROM Inv_rows, Storage, Pack_to_prod
WHERE Inv_rows.ProdID = Pack_to_prod.ProdID
AND Pack_to_prod.PackID = Storage.ItemID
AND Inv_rows.InvID = 999
But the problem is the 'IN' since it doesn't match the rows but I can't use '=' as 'where' results more than one line.
Then I tried to simplify it for myself (as I'm not a professional) with a select clause to understand what exactly needs to be done..
SELECT
Pack_to_prod.PackID AS PackageID,
Inv_rows.ProdID AS ProductID,
Inv_rows.Qty AS ProductQty,
Pack_to_prod.Qty AS PackageQtyPerProduct,
Inv_rows.Qty*Pack_to_prod.Qty AS PackageTotal,
Storage.Qty AS StorageQty,
Storage.Qty - (Inv_rows.Qty * Pack_to_prod.Qty) AS NewStorageQty
FROM Pack_to_prod,Inv_rows,Storage
WHERE Inv_rows.ProdID = Pack_to_prod.ProdID
AND Pack_to_prod.PackID = Storage.ItemID
AND Inv_rows.InvID = 999
GROUP BY Inv_rows.ProdID, Pack_to_prod.PackID, Pack_to_prod.Qty, Storage.Qty, Inv_rows.Qty
Which results as simple list showing the problem why they can't be just summed up:
PackageID ProductID ProductQty PackageQtyPerProduct PackageTotal StorageQty NewStorageQty
A 100 2 2 4 100 96
A 100 8 2 16 100 84
B 100 2 1 2 100 98
B 100 8 1 8 100 92
A 101 2 1 2 100 98
B 101 2 1 2 100 98
A 102 2 3 6 100 94
B 103 2 2 4 100 96
B 103 8 2 16 100 84
But can't figure out how to make it run 'line by line' so that each 'Pack_to_prod.Qty' would be handled separately.
Any help in right direction appreciated.
This seems like some aggregations and joins. To get the changes in values, then:
select pp.packid, sum(p.qty * pp.qty) as new_qty
from (select ir.prodid, sum(qty) as qty
from inv_rows ir
group by ir.prodid
) p join
Pack_to_prod pp
on pp.prodid = p.prodid
group by pp.packid;
You can then use this in an update:
update s
set s.qty = s.qty - pp.new_qty
from storage s join
(select pp.packid, sum(p.qty * pp.qty) as new_qty
from (select ir.prodid, sum(qty) as qty
from inv_rows ir
group by ir.prodid
) p join
Pack_to_prod pp
on pp.prodid = p.prodid
group by pp.packid
) pp
on pp.packid = s.itemid;
This was explained very well. The SQL code is user older coding conventions (such as JOIN conditions in the WHERE clause, instead of FROM) and isn't organized into subqueries properly. All the pieces are present though. I think something like this is what you're looking for.
with
inv_cte(ProdID, sum_qty) as (
select ProdID, sum(Qty)
from Inv_rows
group by ProdID),
pack_cte(PackID, prod_qty) as (
select pp.PackID, sum(pp.Qty*ic.sum_qty)
from Pack_to_prod pp
join inv_cte ic on pp.ProdID=ic.ProdID
group by pp.PackID)
select s.ItemID, s.Qty-p.prod_qty as ResultQty
from Storage s
join pack_cte p on s.ItemID=p.PackID;
Update statement
with
inv_cte(ProdID, sum_qty) as (
select ProdID, sum(Qty)
from Inv_rows
group by ProdID),
pack_cte(PackID, prod_qty) as (
select pp.PackID, sum(pp.Qty*ic.sum_qty)
from Pack_to_prod pp
join inv_cte ic on pp.ProdID=ic.ProdID
group by pp.PackID)
update s
set Qty=Qty-p.prod_qty
from Storage s
join pack_cte p on s.ItemID=p.PackID;
I have the following tables
tblMainequipment
asset_id rev equipment_name
123 0 box
123 1 box
124 0 box
125 0 bottle
tblmainswablocation
asset_id rev swab_location
123 0 cover
123 0 base
123 1 cover
123 1 base
123 1 lock
124 0 cover
124 0 base
125 0 tube
125 0 cover
I did like to get the total count of swablocations for the maximum rev for a particular asset_id. For example, the total number of swab location for asset_id 123 rev 0 is 2 but for rev 1 its 3. I have been trying to figure out for the past few hours but cant seem to find a solution. I am pretty bad with joins. The following shows the what i am trying to get,
query
asset_id maxrev #swablocation equipment_name
123 1 3 box
124 0 2 cover
125 0 2 bottle
I use the following sql
SELECT MEQ.*
FROM tblMainEquipment AS MEQ
INNER JOIN (Select asset_id, max(rev) as maxrev
From tblmainequipment GROUP By asset_id) AS groupmeq ON
(MEQ.asset_id = groupmeq.asset_id) AND (MEQ.rev = groupmeq.maxrev)
I am not sure how i can add the #swablocation into my query.
Here is a method that uses correlated subqueries:
select me.*,
(select count(*)
from tblmainswablocation as sl
where sl.asset_id = me.asset_id
) as numSwabs
from tblMainEquipment as me
where me.rev = (select max(me2.rev) from tblMainEquipment as me2 where me2.asset_id = me.asset_id);
The advantage is that the outer query does not require aggregation.
You need a count and a group by
SELECT MEQ.asset_id maxrev, MEQ.maxrev,
count(blmainswablocations.swab_location), MEQ.equipment_name
FROM tblMainEquipment AS MEQ
INNER JOIN (Select asset_id, max(rev) as maxrev
From tblmainequipment GROUP By asset_id) AS groupmeq ON
(MEQ.asset_id = groupmeq.asset_id) AND (MEQ.rev = groupmeq.maxrev)
Inner join blmainswablocations on MEQ.asset_id = blmainswablocations.asset_id
Group by MEQ.asset_id maxrev
I have two tables in a SQL Server 2008 database, one listing groups and another mapping those groups to records for objects. One group may have many objects.
I've got the following query
SELECT GroupNumber
From dbo.Groups G
LEFT OUTER JOIN dbo.Mapping_ObjectToGroup m ON G.GroupID = m.GroupID
Which results in a table like
GroupNumber
700
700
700
701
701
702
703
703
How can I get a second column that would give me an indexing number for each group like this:
GroupNumber | Ctr
700 | 1
700 | 2
700 | 3
701 | 1
701 | 2
702 | 1
703 | 1
703 | 2
Essentially I'd like to be able to have an integer represent each item within a group. Is there a name for what I'm doing? Can anyone help me do this?
You can use row_number() for this. Do you have a field to establish an order by? If not, you can randomly select with null. Here's an example:
SELECT GroupNumber,
row_number() over (partition by GroupNumber order by (select null)) ctr
From dbo.Groups G
LEFT OUTER JOIN dbo.Mapping_ObjectToGroup m ON G.GroupID = m.GroupID
I have two tables in SQL Server that looks as follows,
Table 1:
Store Type
100 A
101 B
102 B
103 B
104 C
105 C
106 A
107 A
108 D
109 D
110 C
111 B
112 D
and table 2:
Store Units
100 5
101 3
102 10
103 6
104 6
105 8
I want to combine the two tables in a query with a relationship link between the Store column of the two tables. The problem i have is that if i create a query between the two then the stores from 106 to 112 does not appear in the table. The query looks as follows,
SELECT dbo.Table1.Store,
dbo.Table1.Type,
dbo.Table2.Units
FROM Table1 INNER JOIN Table2
ON dbo.Table1.Store=dbo.Table2.Store
What should i do to include the stores that does not appear in Table2 (106-112) but in Table1 and make there Units = 0, such that the query table look as follows,
Store Type Units
100 A 5
101 B 3
102 B 10
103 B 6
104 C 6
105 C 8
106 A 0
107 A 0
108 D 0
109 D 0
110 C 0
111 B 0
112 D 0
You want a left outer join instead of an inner join:
SELECT t1.Store, t1.Type, COALESCE(t1.Units, 0) as Units
FROM Table1 t1 LEFT JOIN
Table2 t2
ON t1.Store = t2.Store;
I also introduced table aliases for your query to make it more readable. Note the use of coalesce() so you get 0 for Units instead of NULL.
SELECT dbo.Table1.Store, dbo.Table1.Type, coalesce(dbo.Table2.Units, 0) As Units
FROM Table1
LEFT JOIN Table2 ON dbo.Table1.Store=dbo.Table2.Store