MySQL SUM outputs wrong value in a query consisting multiple joins - mysql

I'm getting information on these tables using the following query, however defenderhit and defenderdamage SUM values are getting multiplied with the row count of first join's row number.
Table 'battles':
battle_id city_id attacker defender battle_time
1 07 6 0 1342918014
Table 'battlehits':
battle_id family_id user_id hits damage
1 0 0 1000 50000
1 6 15 108 3816
1 6 2 81 2046
1 6 1 852 1344
MySQL Query:
SELECT b.battle_id, b.city_id, b.attacker, b.defender, b.battle_time,
SUM(COALESCE(bh1.damage,0)) AS attackerdamage, SUM(COALESCE(bh2.damage,0)) AS defenderdamage,
SUM(COALESCE(bh1.hits,0)) AS attackerhit, SUM(COALESCE(bh2.hits,0)) AS defenderhit
FROM battles AS b
LEFT JOIN battlehits AS bh1 ON b.attacker = bh1.family_id
LEFT JOIN battlehits AS bh2 ON b.defender = bh2.family_id
WHERE b.battle_id=1
GROUP BY b.battle_id LIMIT 1
Result of this query is as following:
battle_id city_id attacker defender battle_time attackerdamage defenderdamage attackerhit defenderhit
1 07 6 0 1342918014 7206 150000 1041 3000
As you can see in the table data, defenderhit and defenderdamage SUM values are supposed to be 1000 and 50000, but they're multiplied by 3.
What am I doing in here? What's the problem?
Thanks in advance.

You are getting three rows, before the group by/sum. You have one row for each of the three attacker rows from battlehits. Each of these is paired with the same defender row from battlehits, causing the defender data to be tripled. To see this, remove the group by and limit clauses and take out the sum()s. You are effectively creating the cross product of all defenders X all attackers, and then summing.
This shows the three rows with duplicated defender data. This is a consequence of doing a join on a one to many to many relationship, instead of a one to one to one.
SELECT b.battle_id, b.city_id, b.attacker, b.defender, b.battle_time,
COALESCE(bh1.damage,0) AS attackerdamage, COALESCE(bh2.damage,0) AS defenderdamage,
COALESCE(bh1.hits,0) AS attackerhit, COALESCE(bh2.hits,0) AS defenderhit
FROM battles AS b
LEFT JOIN battlehits AS bh1 ON b.attacker = bh1.family_id
LEFT JOIN battlehits AS bh2 ON b.defender = bh2.family_id
WHERE b.battle_id=1;
Output:
battle_id city_id attacker defender battle_time attackerdamage defenderdamage attackerhit defenderhit
1 7 6 0 1342918014 3816 50000 108 1000
1 7 6 0 1342918014 2046 50000 81 1000
1 7 6 0 1342918014 1344 50000 852 1000
You need to split this into separate queries. One for the attacker sums and another for the defender sums.

You can emulate a SUM of distinct rows by using
(SUM(t.field_to_sum) / COUNT(t.primary_key) * COUNT(DISTINCT t.primary_key))

Related

Delete duplicate rows with lowest values in another column in mysql

I'm newbie, and I'm trying to delete duplicate rows with highest value in another column,
This is My database and result of below query
SELECT file_id,src,dst,durations,date_time, COUNT(*), MAX(durations) as
Max_durations
FROM C2
WHERE durations in (
SELECT max(durations)
FROM C2
GROUP BY src, dst
HAVING COUNT(*) >1)
GROUP BY src,dst
HAVING COUNT(*)>1
Now I want to remove Duplicate rows but keep rows that have maximum duration or equal Max_duration column and if have same duration delete one of them.
How can i do it...?
src
dst
duration
COUNT
Max_duration
12014504264
9726341011
464
20
684
12014504264
9726645434
320
8
875
12014556435
9726876431
765
4
900
12014576565
9726347656
43
7
600
12014508754
9726344537
233
2
233
12014764532
9726877654
655
2
54
12014587875
9726645443
1345
5
982
12014654536
9726766433
73
2
84
Assuming you are trying to actually delete rows from the table, not just have a query that omits certain rows:
To do this, just self-join with a row that would be preferred to the current one, so only rows where that is found are deleted:
delete C2
from C2
join C2 preferred on
preferred.src=C2.src and
preferred.dst=C2.dst and
preferred.durations >= C2.durations and
(preferred.durations > C2.durations or preferred.file_id < C2.file_id)
But to do this, you need some unique column to establish a precedence between multiple rows with the maximum duration; here I am using file_id and keeping the row with the lowest file_id.

MySQL Select statement with several joins returns duplicated records

I have 4 tables with the following structure:
Table Groups
Groupid
groupname
groupadmin
Table GroupMembership
devid
groupid
Table GroupLocator
devid
name
pass
color
sampling
connected
forget
trace
Table GroupTracker
devid
groupid
latitude
longitude
timestamp
There is only one groupid='1' with groupname="FBorges"
Table GroupLocator has 2 records where devid points to grouid='1' on GroupMembership
GroupTracker has two records where groupid='1'
When I run the following SELECT:
SELECT GroupLocator.name, GroupLocator.color, GroupLocator.sampling,
GroupLocator.forget, GroupLocator.connected, GroupLocator.trace,
Groups.groupname, GroupTracker.latitude, GroupTracker.longitude,
GroupTracker.timestamp
FROM GroupMembership
JOIN GroupLocator ON GroupLocator.devid=GroupMembership.devid
JOIN Groups ON Groups.groupid=GroupMembership.groupid
JOIN GroupTracker ON GroupTracker.groupid=GroupMembership.groupid
WHERE GroupMembership.groupid=1;
I get the result:
name color sampling forget connected trace groupname latitude longitude timestamp
PCBorges 2 1 45 0 1 FBorges -22.883639 -42.822542 2020-01-08 20:29:24
Test 3 2 45 1 0 FBorges -22.883639 -42.822542 2020-01-08 20:29:24
PCBorges 2 1 45 0 1 FBorges -22.873639 -42.322542 2020-01-11 16:56:30
Test 3 2 45 1 0 FBorges -22.873639 -42.322542 2020-01-11 16:56:30
What I hope to get is:
name color sampling forget connected trace groupname latitude longitude timestamp
PCBorges 2 1 45 0 1 FBorges -22.883639 -42.822542 2020-01-08 20:29:24
Test 3 2 45 1 0 FBorges -22.883639 -42.822542 2020-01-08 20:29:24
EDIT: Removed my previous speculation after structure and data was provided and wrote a new answer:
I believe that you want to JOIN GroupTracker on devid instead of on groupid. Groupid 1 matches both rows in the GroupTracker table, so it will provide two results for each 1 row in GroupMemebership. Devid only matches one row. A correct JOIN is more efficient than your current GROUP BY solution (in comments) and may also produce more consistent results as your database grows.
SELECT gl.name, gl.color, gl.sampling,
gl.forget, gl.connected, gl.trace,
g.groupname, gt.latitude, gt.longitude,
gt.timestamp
FROM GroupMembership AS gm
JOIN GroupLocator AS gl ON gm.devid = gl.devid
JOIN Groups AS g ON gm.groupid = g.groupid
JOIN GroupTracker AS gt ON gm.devid = gt.devid
WHERE gm.groupid=1
;
I aliased all your tables so the query is much shorter and hence faster to write. I also swapped positions of all your JOIN clauses. I prefer to have the left table on the left side and the right table on the right side. Makes it easier to read. These two changes are not important. It's only style. The query will work perfectly without them.

msql count of items in each category into 2 columns by status with join

I’m a mysql newbie, recently installed mariadb to work on a project.
I have one table of many ITEMS, which are in various categories (catnum), and another table, STATUS, showing items (by id#) and their current status, either A or B.
I need to write a query that lists all of the categories (by catnum) and the total of all A’s and B’s in each category, something like this:
Desired result:
catnum statA statB
1001 22 15
1002 0 12
1003 14 8
1004 3 37
1005 24 0
1006 0 1
1007 47 5
etc
The ITEMS table looks like this:
itemid catnum
1 1205
2 1008
3 1010
4 1150
5 1782
6 1553
7 1004
etc
The STATUS table looks like this:
itemid stat
60 A
302 A
95 B
122 B
8 B
6 A
46 B
etc
The itemid in ITEMS is auto_increment, in case that matters.
I know (or think I know) that I need to use the following in some combination:
count(status.stat) or count(status.stat = A)
where items.itemid = status.itemid
where stat = A (then B)
group by catnum.
In some combinations I got error saying “Unknown column 'status.itemid' in 'having clause'” or other clause, despite that it exists. Why is that?
The closest I have gotten is to show each category and both status columns properly labeled but the number of B status items was incorrect, just a repeat of number of A status items.
SELECT
items.catnum,
count(status.stat=1) AS statA,
count(status.stat=2) AS statB
FROM
status
INNER JOIN
items
WHERE
items.itemid = status.itemid
GROUP BY
catnum;
(ALSO tried with ON instead of WHERE, same result, statB totals were wrong.)
I have explored self joins, inner joins, left/right joins, unions, subquery, and other techniques but I can’t seem to get to what I want. It seems like this must be a really common general query, but I can’t seem to find the right search terms to find it online. Any guidance would be appreciated.
Your query as it currently stands will simply return a COUNT of all the items in STATUS which have a given catnum. This is why the values for statA and statB are the same. What you need to do is SUM the occurrences of each status value. I've made a small SQLFiddle demo that shows this query in action:
SELECT
items.catnum,
SUM(status.stat='A') AS statA,
SUM(status.stat='B') AS statB
FROM items
JOIN status
ON items.itemid = status.itemid
GROUP BY items.catnum
Output (for the demo data):
catnum statA statB
1004 1 1
1008 2 1
1010 0 2
Note that in MySQL a boolean expression (e.g. status.stat='A') evaluates to 1 if true, 0 if false, so it can be summed directly.

How to get the lowest price from a particular group of users MYSQL

I have been at this for a few days without much luck and I am looking for some guidance on how to get the lowest estimate from a particular group of sullpiers and then place it into another table.
I have 4 supplier estimate on every piece of work and all new estimates go into a single table, i am trying to find the lowest 'mid' price from the 4 newsest entries in the 'RECENT QUOTE TABLE' with a group id of '1' and then place that into the 'LOWEST QUOTE TABLE' as seen below.
RECENT QUOTE TABLE:
suppid group min mid high
1 1 200 400 600
2 1 300 500 700
3 1 100 300 500
[4] [1] 50 [150] 300
5 2 1000 3000 5000
6 2 3000 5000 8000
7 2 2000 4000 6000
8 2 1250 3125 5578
LOWEST QUOTE TABLE:
suppid group min mid high
4 1 50 150 300
Any help on how to structure this would be great as i have been loking for a few days and have not been able to find anything to get me moving again, im using MYSQL and the app is made in Python im open to all suggestions.
Thanks in advance.
If you really want to select only row with group 1, you can do something like
INSERT INTO lowest_quote_table
SELECT * FROM recent_quote_table
WHERE `group` = 1
ORDER BY `mid` ASC
LIMIT 1.
If you want a row with the lowest mid from every group, you can do something like
INSERT INTO lowest_quote_table
SELECT rq.* FROM recent_quote_table AS rq
JOIN (
SELECT `group`, MIN(`mid`) AS min_mid FROM recent_quote_table
GROUP BY `group`
) MQ ON rq.`group` = MQ.`group` AND rq.`mid` = MQ.min_mid

Create a query to return number of produced products based on date in Microsoft Access

I want to create a query to get the total number of produced products for each day in Microsoft Access.
Here are the few rows of my table as a sample:the table's name is Orders
ordernumber number of products Date
100 2 11-May-16
101 1 11-May-16
121 2 24-May-16
122 3 24-May-16
131 1 25-May-16
105 3 11-May-16
127 1 24-May-16
135 2 25-May-16
The desired output is :
TotalNoProducts Date
6 11-May-16
6 24-May-16
3 25-May-16
This is one of the more basic aggregate queries:
SELECT SUM([number of products]) As TotalNoProducts, CDate(Int([Date])) As TheDate
FROM Orders
GROUP BY CDate(Int([Date]))
Note that you can also build this query through the query builder, which is usually easier for beginners than using SQL