MySQL JOIN table problem and aggregation
I have three tables. This request will log the correct answer (x-lines for btv.id_user with corresponding btv.cas and race.id_zavod
SELECT `btv.id_user`, `btv.id_zavod`,`btv.cas`
FROM `btv`
JOIN `btu` ON `btv.id_user` = `btu.id_user`
JOIN `race` ON 'btv.id_zavod' = `race.id_zavod`
WHERE `race.type` = '8' AND `btv.id_user` = '607'
Result:
| 607 | 512 | 03:15:58 |
| 607 | 730 | 03:01:18 |
| 607 | 164 | 03:07:26 |
| 607 | 767 | 02:58:31 |
| 607 | 1147 | 03:06:47 |
| 607 | 1149 | 03:09:41 |
| 607 | 1178 | 03:24:20 |
But when I try to concatenate it into one line using id_user it returns the correct min btv.cas value but enters incorrectly into the incorrect combination race.id_zavod
SELECT `btv.id_user`, `btv.id_zavod`, MIN( `btv.cas` )
FROM `btv`
JOIN `btu` ON `btv.id_user` = `btu.id_user`
JOIN `race` ON 'btv.id_zavod' = `race.id_zavod`
WHERE `race.type` = '8' AND `btv.id_user` = '607'
GROUP BY `btv.id_user`
Result:
| 607 | 512 | 02:58:31 |
source to share
When you use GROUP BY, the columns in the select list must satisfy one of the following:
- Column named in GROUP BY (like
btv.id_user
in your example) - The column is inside an aggregate function (for example
MIN( btv.cas )
) - Column is the functional dependency of the column (s) that you named in GROUP BY.
This is a mistake in your request: btv.id_zavod
has many meanings for each value btv.id_user
. This does not satisfy functional dependency. For each value, there id_user
must be only one value in id_zavod
for it to be a functional dependency.
On some brands of databases, this query will actually give you an error. MySQL is more flexible by trusting you to only specify columns in the select list, which are functional dependencies of the columns (s) that you named in the GROUP BY.
Here's a query that returns what you want, the MIN value btv.cas
for id_user
, with the corresponding value btv.id_zavod
:
SELECT b1.id_user, b1.id_zavod, b1.cas
FROM btv AS b1
JOIN btu ON (b1.id_user = btu.id_user)
JOIN race ON (b1.id_zavod = race.id_zavod)
LEFT OUTER JOIN btv AS b2 ON (b1.id_user = bt2.id_user AND
(b1.cas > b2.cas OR (b1.cas = b2.cas AND b1.primarykey > b2.primarykey))
WHERE race.type = '8' AND b1.id_user = '607'
AND b2.id_user IS NULL;
In other words, you need to make your join as before, but then re-join it to btv
see if there is another row with the same value id_user
and less value in cas
. Use the BUILT-IN CONNECTION because you are looking for a case where there is no match. You can check this with b2.id_user IS NULL
, because OUTER JOIN makes all columns NULL when there is no match.
Note that there may be relationships, so we add an additional term using the primary key as a tiebreaker.
You don't need to use GROUP BY in this query. This is implicitly taken care of, because there will only be one row that satisfies the OUTER JOIN condition.
source to share
The request you wrote:
SELECT `btv.id_user`, `btv.id_zavod`, MIN( `btv.cas` )
FROM `btv`
JOIN `btu` ON `btv.id_user` = `btu.id_user`
JOIN `race` ON 'btv.id_zavod' = `race.id_zavod`
WHERE `race.type` = '8' AND `btv.id_user` = '607'
GROUP BY `btv.id_user`
will not work. You need the id_zavod group or something. Can you tell us which query you are actually using? And what result do you expect?
source to share
I'm not sure what you are trying to accomplish, but you might be looking for GROUP_CONCAT to function? ?
source to share
Are you just trying to get the lowest btv.cas and other relevant columns? If so, you can simply order btv.cas and limit to 1 hit:
SELECT `btv.id_user`, `btv.id_zavod`,`btv.cas`
FROM `btv`
JOIN `btu` ON `btv.id_user` = `btu.id_user`
JOIN `race` ON 'btv.id_zavod' = `race.id_zavod`
WHERE `race.type` = '8' AND `btv.id_user` = '607'
ORDER BY `btv.cas` LIMIT 1
source to share