SQL Subtract from different tables - mysql

I'm subtracting values from diferent tables based on ID.
Its like an inventory where I've the tables for Itens In and Itens Out.
What I want to do is to substract the Itens In - Itens Out based on ID of the iten.
I manage to do that, but if an Iten only as an In movement, the query just shows an empty row, when it should show the In movement - Out moment that even if it doesnt exists should be considered as 0, showing in this case only the value of the IN movment.
Can someone help?
Each row in each table represents one item.
TABLE - in_used
id_item_____qnt
1 _________500
2 _________1000
TABLE - out_used
id_item_____qnt
1 _________200
OUTPUT EXPECTED
used_stock
id_item____qnt
1 ________300
2 ________1000 (there's no out movement so it should show only the IN one)
Select
in_used.qnt - out_used.qnt As used_Stock
From
in_used Inner Join
out_used On in_used.id_item = out_used.id_item

If id_item is unique in in_used and there is at most 1 row in out_used then all you need is a LEFT join and COALESCE():
select i.id_item,
i.qnt - coalesce(o.qnt, 0) used_Stock
from in_used i left join out_used o
on i.id_item = o.id_item
If there are more rows use also aggregation:
select i.id_item,
sum(i.qnt) - coalesce(sum(o.qnt), 0) used_Stock
from in_used i left join out_used o
on i.id_item = o.id_item
group by i.id_item

It sounds like you want something like this:
select item, sum(inc)
from ((select item, in_used as inc
from items_in
) union all
(select item, - out_used as inc
from items_out
)
) ii
group by item;
I'm not sure if each row in the two tables represents one item or if there is a counter. If there is a counter, then that should be used instead of 1/-1.

In this case we know that we have id_item in all cases for join results and none of them would be null:
SELECT I.id_item,
SUM(I.qnt) - SUM(O.qnt) AS used_Stock
FROM in_used AS I
JOIN out_used AS O ON I.id_item = O.id_item
GROUP BY id_item
But for general result, you can try below query:
SELECT (CASE
WHEN I.id_item IS NOT NULL THEN I.id_item
ELSE O.id_item
END) AS id_item,
SUM(I.qnt) - SUM(O.qnt) AS used_Stock
FROM in_used AS I
JOIN out_used AS O ON I.id_item = O.id_item
GROUP BY id_item
Hope this works for you!

If you have more then one record per id you can use sum() and must use FULL JOIN if there a no records on both tables or otherwise you can use LEFT JOIN
Select
id_item , coalesce(sum(in_used.qnt),0) - coalesce(sum(out_used.qnt),0) As used_Stock
From
in_used FULL Join
out_used On in_used.id_item = out_used.id_item
GROUP BY id_item

Related

mysql query group by totals

I am using the following query to retrieve the number of events per state from 2 tables that are linked by a userID.
SELECT state,COUNT(*) AS num
FROM tableUserInfo
WHERE userID IN (SELECT userID
FROM tableEvents
WHERE conditionOne = 1
AND conditionTwo = 2)
GROUP BY state
This query works correctly. My problem is that not all states have user entries, and I need the query to return 0 for those. I was wondering if there was a method such as joining or using an in clause, that would included a set of all states, making the query return 0 for any that didn't have entries in tableEvents?
Do you have a list of states? If not then this would give a list of all the states your database knows about:
SELECT DISTINCT state FROM tableUserInfo
....and enclosing this in brackets it can be dropped in place in the query below:
SELECT s.state, IFNULL(cnt, 0) AS num
FROM list_of_states s
LEFT JOIN (
SELECT state,COUNT(*) AS cnt
FROM tableUserInfo ui
INNER JOIN tableEvents te
ON ui.userId=te.userId
WHERE conditionOne = 1
AND conditionTwo = 2
GROUP BY state
) u
ON s.state=u.state;
Although in the absence of "list_of_states" it would be more efficient to do this:
SELECT ui.state, SUM(IF(te.userId IS NULL, 0, 1)) AS cnt
FROM tableUserInfo ui
LEFT JOIN tableEvents te
ON ui.userId=te.userId
AND te.conditionOne = 1
AND te.conditionTwo = 2
GROUP BY state;
As #raymond-nijland suggested you can use Left Join to include all states.
SELECT tableUserInfo.state,COUNT(tableUserInfo.*) AS num
FROM tableUserInfo Left Join tableEvents on tableUserInfo.userID = tableEvents.userID
WHERE tableEvents.conditionOne = 1 AND tableEvents.conditionTwo = 2
GROUP BY state

Count field and group by regarding another field on same table MySQL

I am trying to find count of field by another field in same table on MySQL.My Table Like This:
Id DrgId CodeType IsPrincipal Code
182250051 48261836 I 1 T151
182250055 48261836 I 2 U739
182250059 48261836 I 3 Y929
182250061 48261836 I 4 W444
182250062 48261836 A 2 3006104
So I want to find used helper codes for T151 which is IsPrincipal equals 1.
Output should like this:
Code Helper_I_Count Helper_A_Count
T151 3 1
So I tried Like this:
SELECT t.`Code`,COUNT(v1.`Code`) AS EkTaniSay,COUNT(v2.`Code`) AS IslemSay
FROM TIGPatientCode t,
(
SELECT DRGPatientId,`Code`
FROM TIGPatientCode
WHERE IsPrincipal<>'1' AND CodeType='I'
) v1,
(
SELECT DRGPatientId,`Code`
FROM TIGPatientCode
WHERE IsPrincipal<>'1' AND CodeType='A'
) v2
WHERE t.IsPrincipal='1' AND t.DRGPatientId=v1.DRGPatientId AND t.DRGPatientId=v2.DRGPatientId
GROUP BY t.`Code`
But it wont get actual count.
How can I Do this?
Thanks
SELECT t2.Code,
SUM(t1.CodeType = 'I') AS EkTaniSay,
SUM(t1.CodeType = 'A') AS IslemSay
FROM TIGPatientCode AS t1
RIGHT JOIN TIGPatientCode AS t2 ON t1.DrgPatientId = t2.DrgPatientId
WHERE t1.isPrincipal != 1 AND t2.isPrincipal = 1
GROUP BY t1.DrgPatientId;
The first part of the query is based on multiple query same table but in different columns mysql. Then I join this with the table again to get the code for the principal row.
The problem with your query is that joining the two subqueries creates a cross-product of all the rows, which causes the counts to be multiplied. Also, if there's any group that doesn't have one of the codes, that subquery will return no rows, so the join will be empty for that code. You could fix the first problem by doing the counts in the subqueries rather than the main query. The second problem can be fixed by using LEFT JOIN. So the fixed version of your query would look like:
SELECT t.Code, v1.EkTaniSay, v2.IslemSay
FROM TIGPatientCode t
LEFT JOIN (
SELECT DRGPatientId, COUNT(*) AS EkTaniSay
FROM TIGPatientCode
WHERE IsPrincipal<>'1' AND CodeType='I'
GROUP BY DRGPatientId
) AS v2 ON t.DRGPatientId = v2.DRGPatientId
LEFT JOIN (
SELECT DRGPatientId, COUNT(*) AS IslemSay
FROM TIGPatientCode
WHERE IsPrincipal<>'1' AND CodeType='A'
GROUP BY DRGPatientId
) AS v1 ON t.DRGPatientId = v1.DRGPatientId
WHERE t.IsPrincipal = 1
DEMO

Query to join 2 tables and keep only 1 row from one of the tables

I have 2 tables (item and item_historic) and i'm looking for a query that keep the last item_historic line (max date) and join it to the item table.
item :
id_item state_item
5560 complete
5570 removed
item_historic :
id_historic id_item state_historic date
2002 5560 declared 2011-01-13 13:32:15
2198 5560 complete 2011-03-14 11:44:40
1780 5570 declared 2011-03-15 15:26:55
2208 5570 removed 2011-04-15 08:17:59
result :
id_item id_historic state_item date state_historic
5560 2198 complete 2011-03-14 11:44:40 complete
5570 2208 removed 2011-04-15 08:17:59 removed
I want one id_item only.
I hope that make sense and thanks in advance.
EDIT : wrong result and my question is what should the query look like ?
Tried :
select ah.id_item, ah.id_historic, at.state, date, ah.id_type, ah.state_item from item at left join item_historic ah on ah.id_item = at.id_item group by ah.id_item order by max(date) ;
In MySQL, the not exists approach is often the most efficient:
select ah.id_attestation, ah.id_historic, at.state, date, ah.id_type, ah.state_aft
from `via-energica_plateforme`.attestation at left join
`via-energica_plateforme`.attestation_historic ah
on ah.id_attestation = at.id_attestation
where not exists (select 1
from `via-energica_plateforme`.attestation_historic ah2
where ah2.id_item = ah.id_item and
ah2.date > ah.date
);
This query will work best with an index on attestation_historic(id_item, date).
The not exists clause takes some getting used to in this context. It is saying "choose the row from ah where there is no more recent row from the table" -- which is that same as "get the maximum date". The advantage in MySQL is that no aggregation is required, and aggregation can be an expensive operation. But, for performance, you really need to compare the two approaches.
The following is a way of doing it with a sub-query:
SELECT i.id_item, ih.id_historic, i.state_item, sq.max_date as `date`, ih.state_historic
FROM attestation i
INNER JOIN (SELECT id_item, MAX(`date`) AS max_date FROM attestation_historic GROUP BY id_item) AS sq ON i.id_item = sq.id_item
INNER JOIN attestation_historic ih ON i.id_item = ih.id_item AND sq.max_date = ih.`date`;
Maybe there's another way, but this would work:
SELECT t.id_item, t.id_historic, i.state_item, t.date, t.state_historic FROM item i
LEFT JOIN
(SELECT * FROM (SELECT *
FROM id_historic ORDER BY DATE DESC) t GROUP BY t.`id_item`
) t ON t.id_item = i.id_item ORDER BY t.date;

JOIN query not behaving as expected

I'm working on a query that doesn't behave as expected, and would appreciate any help on pointing me in the right direction.
TABLES
I have three central tables consists of the following.
table_categories
- id (int)
- name (varchar)
table_events
- id (int)
- name (varchar)
- created (date time)
table_taxonomy
- cat_id (id from table_categories)
- event_id (id from table_events)
- type (varchar in this example always 'category')
GOAL
Count events created after a certain date in each category. The result should be something like:
COUNT NAME ID
3 Rock 1
2 Punk 3
QUERY
This is the query that I've come up with. The problem is that the result doesn't care about the created date, and grabs all events regardles of when they where created.
SELECT COUNT(*) AS count,
            table_categories.name,
            table_categories.id
            FROM table_taxonomy
            INNER JOIN table_categories
            ON table_categories.id = table_taxonomy.cat_id
            LEFT JOIN table_events
            ON table_events.id = table_taxonomy.events_id
            WHERE table_taxonomy.type = 'category'
            AND table_taxonomy.cat_id IN(2,3)
            AND table_events.created > '2012-10-07 05:30:00'
            GROUP BY (table_categories.name)
try this one, just small change while comparing date :
SELECT COUNT(*) AS count,
table_categories.name,
table_categories.id
FROM table_taxonomy
INNER JOIN table_categories
ON table_categories.id = table_taxonomy.cat_id
LEFT JOIN table_events
ON table_events.id = table_taxonomy.events_id
WHERE table_taxonomy.type = 'category'
AND table_taxonomy.cat_id IN(2,3)
AND Date(table_events.created) > '2012-10-07 05:30:00'
GROUP BY (table_categories.name)
Try this one,
SELECT c.ID, c.Name, COUNT(*)
FROM table_categories a
INNER JOIN table_taxonomy b
ON a.id = b.cat_id
INNER JOIN table_events c
ON b.event_ID = c.ID
WHERE c.created > 'dateHere'
-- AND ..... -- other conditions here
GROUP BY c.ID, c.Name

SQL Query to Calculate Values based on Conditions

I have three relevant tables:
Evaluations:
EvalID
WorktypeID
PointsPossible
Items:
EvalID
ItemID
Value
QuestionID
Question_Worktype:
WorkTypeID
QuestionID
Points
There are multiple Items for each Evaluation, and each item has a QuestionID/WorkTypeID pair that relates to an entry in Question_Worktype, where the score lies.
Items.Value is whether an Item was correct or not.
I would like a query that returns the total Points of all Items for an Evaluation, Points only are awarded if the Value of the Item is 1.
My goal would be to get:
Eval_ID, Total Points (calculated), Points Possible
I can't seem to wrap my brain around it.
Edit: I forgot an important part:
I have an additional table:
qa_edits:
EditID
ItemID
NewValue
Date
If someone edits the value, it's stored here, and I really need it to calculate based on the newest edit for the item if there is one, or the item value if there isn't.
Start with your edit table, to get the latest edit for each item:
SELECT
M.ItemId,
COALESCE(Q.Value, I.Value) as Value,
I.QuestionId
FROM (
SELECT
I.ItemId,
MAX(Date) as LastEditDate
FROM Items as I
LEFT OUTER JOIN QA_Edits as E ON
I.ItemId = E.ItemId
GROUP BY
I.ItemId
) as M
LEFT OUTER JOIN QA_Edits as Q ON
M.ItemId = Q.ItemId
AND M.LastEditDate = Q.Date
LEFT OUTER JOIN Items as I ON
M.ItemId = Q.ItemId
AND M.LastEditDate IS NULL
I'd suggest a view for that - let's call it LatestItems.
Then, back to Blorgbeard's:
select
e.Eval_ID,
sum(qw.Points) as TotalPoints,
e.PointsPossible
from Evaluations e
join LatestItems i on
e.Eval_ID = i.Eval_ID
and i.Value = 1
join Questions_WorkType qw on
qw.QuestionID = i.QuestionID
group by
e.Eval_ID, e.PointsPossible
I think this should work:
select e.Eval_ID, sum(qw.Points) as TotalPoints, e.PointsPossible
from Evaluations e
left join Items i on e.Eval_ID = i.Eval_ID and i.Value = 1
join Questions_WorkType qw on qw.QuestionID = i.QuestionID
group by e.Eval_ID, e.PointsPossible