Selecting row with minimal value in one column, in a join query - ms-access

I have two tables: tblBusinesses, tblBusinessImages that are matched on tblBusinesses.fldID = tblBusinessImages.fldBusinessID like this:
tblBusinesses
=============
fldID | fldName | fldTitle | fldBody
-------------------------------------
1 | b1 | title1 | body1
2 | b2 | title2 | body2
3 | b3 | title3 | body3
4 | b4 | title4 | body4
tblBusinessImages
=============
fldID | fldFileName | fldTitle | fldBusinessID | fldOrder
-----------------------------------------------------------
1 | img1.jpg | img1title | 1 | 3
2 | img2.jpg | img2title | 1 | 1
3 | img3.jpg | img3title | 1 | 2
I want to write a query that gets for every business in tblBusinesses, the image in tblBusinessImages with minimal fldOrder. In other words, in the example I wrote above I want to get business b1, title1, body1 along with img2.jpg from tblBusinessImages (because it has minimal fldOrder in tblBusinessImages ).
Any help would be appreciated!

If fldOrder alwyas starts at one for each fldBusinessID the query becomes
SELECT b.fldName, b.fldTitle, b.fldBody, i.fldFileName
FROM
tblBusinesses b
LEFT JOIN tblBusinessImages i
ON (b.fldID = i.fldBusinessID AND i.fldOrder = 1)
If not, you will have to use subqueries.
SELECT b.fldName, b.fldTitle, b.fldBody, firstImage.fldFileName
FROM
tblBusinesses b
LEFT JOIN (
SELECT i.fldBusinessID, i.fldFileName
FROM
tblBusinessImages i
WHERE
i.fldOrder = (SELECT MIN(x.fldOrder)
FROM tblBusinessImages x
WHERE x.fldBusinessID = i.fldBusinessID)
) firstImage
ON b.fldID = firstImage.fldBusinessID
Note that the left join makes the query return also businesses having no image at all.
If you need only entries having images where the smallest order id can have any value:
SELECT b.fldName, b.fldTitle, b.fldBody, i.fldFileName
FROM
tblBusinesses b
INNER JOIN tblBusinessImages i
ON b.fldID = i.fldBusinessID
WHERE
i.fldOrder = (SELECT MIN(x.fldOrder)
FROM tblBusinessImages x
WHERE x.fldBusinessID = i.fldBusinessID)

Do you only want to get the ONE entry with the smallest fldOrder?
Didn't test this query:
SELECT b.fldName,b.fldTitle,b.fldBody,bi.fldFileName,bi.fldOrder
FROM tblBuisnesses b
INNER JOIN tblBuisnessImages bi ON bi.fldBusinessID=b.fldID ORDER BY b.fldOrder DESC LIMIT 1;

Related

Select all items and count in related table by criteria

I have tables Match and Reaction as following:
REACTION
+----------+----------+----------+----------+
| user_id | game_id | item_id | reaction |
+----------+----------+----------+----------+
| 1 | 1 | 1 | 1 |
| 1 | 1 | 2 | 1 |
| 2 | 1 | 1 | 1 |
| 2 | 1 | 2 | 0 |
+----------+----------+----------+----------+
MATCH:
+----------+----------+
| game_id | item_id |
+----------+----------+
| 1 | 1 |
| 1 | 2 |
+----------+----------+
Now I want (if possible without subqueries) to select ALL item_ids from MATCH table AND count of rows where field reaction in table Reaction is equal to 1 for user with id = 2. For example, for defined tables I want to get following results:
+----------+----------+
| item_id | count |
+----------+----------+
| 1 |  1 |
| 2 | 0 |
+----------+----------+
I've tried something like
SELECT match.item_id, COUNT(reaction.user_id) as c
FROM match
LEFT JOIN reaction ON reaction.item_id = match.item_id
WHERE reaction.reaction = 1 AND match.game_id = 2
GROUP BY match.item_id
HAVING c > 0
but it didn't work as expected. I cannot get count for particular user.
I think you are close. I think you just need to move conditions on the second table to the ON clause:
SELECT m.item_id, COUNT(r.user_id) as c
FROM match m LEFT JOIN
reaction r
ON r.item_id = m.item_id AND
r.reaction = 1 AND
r.user_id = 2
WHERE m.game_id = 2
GROUP BY m.item_id;
I'm not sure what the HAVING clause is for, because you seem to want counts of 0.
Note that this also introduces table aliases so the query is easier to write and to read.
SELECT match.item_id, COUNT(reaction.user_id) as c
FROM match JOIN reaction ON (reaction.item_id = match.item_id and reaction.reaction = 1 AND match.game_id = 2)
GROUP BY match.item_id
HAVING COUNT(reaction.user_id)
I think you need to filter 'before' join -> so use the 'on' clause.
Filters in where are applied after the join is made while filter applied on on clause are applied before the join is made
You have not game_id = 2 so this should return no value
and you should not use left joined table columns in where condition otherwise these wprk as inner join ... in these cases you shou move the related condition in ON clause
SELECT match.item_id, COUNT(reaction.user_id) as c
FROM match
LEFT JOIN reaction ON reaction.item_id = match.item_id
AND reaction.reaction = 1
WHERE match.game_id = 2
GROUP BY match.item_id
HAVING c > 0
but try also
SELECT match.item_id, COUNT(reaction.user_id) as c
FROM match
LEFT JOIN reaction ON reaction.item_id = match.item_id
AND reaction.reaction = 1
GROUP BY match.item_id

MySQL left join that shows NULL for missing rows

I have 2 tables
Table:
recip
recipid | recipname
1 | Recip1
2 | Recip2
And table:
recipuser
recipid | userid
1 | 1
2 | 1
1 | 2
So userid 2 has 1 recip
The result I'm trying to achieve is to show all "recip" rows with matching or null for given user id, EG:
SELECT r.recipid, r.recipname, ru.userid
FROM recip r
left JOIN recipuser ru ON r.recipid = ru.recipid
WHERE ru.userid = 2 OR ru.userid IS NULL
Results in:
recipid | recipname | userid
1 | Recip1 | 2
I want to get:
recipid | recipname | userid
1 | Recip1 | 2
2 | Recip2 | NULL
How do I show all rows from recip with the userid or NULL for every row given a user id??
Thanks for your help.
Move the WHERE logic to the ON clause:
SELECT r.recipid, r.recipname, ru.userid
FROM recip r
LEFT JOIN recipuser ru
ON r.recipid = ru.recipid AND ru.userid = 2;
The problem with your current query is that the WHERE clause is filtering off the non matching record which you want to appear.

Query displays different data each time I run it in Mysql

I have the following query:
SELECT pr_production_units_details.id AS idProductionUnit,
pr_production_units_details.production_units_detail AS productionUnit,
IF(pr_varieties.variety IS NULL, 'SIN SEMBRAR O ERR', pr_varieties.variety)
AS variety
FROM pr_production_units_details
LEFT JOIN
(SELECT MAX(sw_sowing.id) AS ids, sw_sowing.id_production_unit_detail,
sw_sowing.id_variety
FROM sw_sowing
WHERE sw_sowing.status != 0
AND sw_sowing.id_tenant = 1
AND YEARWEEK(sw_sowing.date) <= 201741
GROUP BY sw_sowing.id_production_unit_detail, id_variety
ORDER BY ids DESC
) AS sw
ON pr_production_units_details.id = sw.id_production_unit_detail
INNER JOIN pr_varieties ON sw.id_variety = pr_varieties.id
WHERE pr_production_units_details.id_grouper_detail = 1
AND pr_production_units_details.status = 100
AND pr_production_units_details.id_tenant = 1
GROUP BY pr_production_units_details.id
which brings me the following result:
------------------------------------------------
idProductionUnit | productionUnit | variety
------------------------------------------------
1 | 1 | YELLOW
------------------------------------------------
2 | 2 | YELLOW
------------------------------------------------
3 | 3 | YELLOW
------------------------------------------------
The above result is fine, but every time I run the query, the variety column changes its values, that is:
------------------------------------------------
idProductionUnit | productionUnit | variety
------------------------------------------------
1 | 1 | YELLOW
------------------------------------------------
2 | 2 | BLUE
------------------------------------------------
3 | 3 | YELLOW
------------------------------------------------
also it gives me the following result:
------------------------------------------------
idProductionUnit | productionUnit | variety
------------------------------------------------
1 | 1 | BLUE
------------------------------------------------
2 | 2 | YELLOW
------------------------------------------------
3 | 3 | YELLOW
------------------------------------------------
I do not know if it has to be seen by the ORDER BY or the GROUP BY but I have not understood because it shows me different data.
I hope that you can help me!
Thanks!
I can solve my problem, I change the query like this:
SELECT pr_production_units_details.id AS idProductionUnit, pr_production_units_details.production_units_detail AS productionUnit, pr_varieties.variety
FROM (SELECT MAX(sw_sowing.id) AS id
FROM sw_sowing
WHERE sw_sowing.status != 0
AND sw_sowing.id_tenant = 1
AND YEARWEEK(sw_sowing.date) <= 201741
GROUP BY sw_sowing.id_production_unit_detail
)AS sw
INNER JOIN sw_sowing ON sw_sowing.id = sw.id
INNER JOIN pr_production_units_details ON pr_production_units_details.id = sw_sowing.id_production_unit_detail
INNER JOIN pr_varieties ON pr_varieties.id = sw_sowing.id_variety
WHERE sw_sowing.status != 0
AND sw_sowing.id_tenant =1
AND pr_production_units_details.id_grouper_detail = 13
GROUP BY pr_production_units_details.id, variety
I follow the instructions about the comments and my error only shows me depending the Mysql versions
Thanks for the help!

Mysql select rows with same id's (3 tables)

I have the following tables:
'blog_content'
'blog_media'
'blog_media_content'
| blog_id | media_id |
========================
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
| 3 | 4 |
I want to select all blog_media.uri's where blog_media.media_id equals blog_media_content.blog_id.
Please help me to achieve my aim.
An inner join between blog_media and blog_media_content tables would suffice.
SELECT
bm.uri
FROM blog_media bm
INNER JOIN blog_media_content bmc ON bm.media_id = bmc.media_id
WHERE bmc.blog_id =3;
Note:
If you need any additional information from blog table then you need an additional inner join like below:
...INNER JOIN blog_table b ON bmc.blog_id = b.blog_id...
EDIT:
In order to get records for all blog_ids :
SELECT
bm.uri
FROM blog_media bm
INNER JOIN blog_media_content bmc ON bm.media_id = bmc.media_id
ORDER BY bmc.blog_id;

Mysql search on multiple join results

I've a table "products" and a table where are store some attributes of a product:
zd_products
----------
|ID|title|
----------
| 1| Test|
| 2| Prod|
| 3| Colr|
zd_product_attached_attributes
------------------
|attrid|pid|value|
------------------
|1 | 1 | A |
|2 | 1 | 10 |
|3 | 1 | AB |
|1 | 2 | B |
|2 | 2 | 22 |
|3 | 2 | BB |
|1 | 3 | A |
|2 | 3 | 10 |
|3 | 3 | CC |
I want to search in zd_products only the products that have some attributes values, for exam place
Get the product when the attribute 1 is A and the attribute 3 is AB
Get the product when the attribute 2 is 10 and the attribute 3 is CC
etc
How can i do this using a join ?
Oh, the Joys of the EAV model!
One way is to use a separate JOIN operation for each attribute value. For example:
SELECT p.id
, p.title
FROM zd_products p
JOIN zd_product_attached_attributes a1
ON a1.pid = p.id
AND a1.attrid = 1
AND a1.value = 'A'
JOIN zd_product_attached_attributes a3
ON a3.pid = p.id
AND a3.attrid = 3
AND a3.value = 'AB'
With appropriate indexes, that's likely going to be the most efficient approach. This isn't the only query that will return the specified result, but this one does make use of JOIN operations.
Another, less intuitive approach
If id is unique in the zd_products table, and we have guarantee that the (attrid,pid,value) tuple is unique in the zd_product_attached_attributes table, then this:
SELECT p.id
, p.title
FROM zd_products p
JOIN zd_product_attached_attributes a
ON a.pid = p.id
AND ( (a.attrid = 1 AND a.value = 'A')
OR (a.attrid = 3 AND a.value = 'AB')
)
GROUP
BY p.id
, p.title
HAVING COUNT(1) > 1
will return an equivalent result. The latter query is of a form that is particularly suitable for matching two criteria out of three, where we don't need a match on ALL of the attributes, but just some of them. For example, finding a product that matches any two of:
color = 'yellow'
size = 'bigger'
special = 'on fire'
And of course there are other approaches that don't make use of a JOIN.
FOLLOWUP
Q: And if I want to the same but using OR operator? I mean get ONLY if the attribute 1 is A or the attribute 2 is AB otherwise don't select the record.
A: A query of the form like the second one in my answer (above) is more conducive to the OR condition.
If you want XOR (exclusive OR), where one of the attributes has a matching value but the other one doesn't, just change the HAVING COUNT(1) > 1 to HAVING COUNT(1) = 1. Only rows from products that find one "matching" row in the attributes table will be returned. To match exactly 2 (out of several), HAVING COUNT(1) = 2, etc.
A query like the first one in my answer can be modified to use OUTER joins, to find matches, and then do a conditional test in the WHERE clause, to determine if a match was found.
SELECT p.id
, p.title
FROM zd_products p
LEFT
JOIN zd_product_attached_attributes a1
ON a1.pid = p.id
AND a1.attrid = 1
AND a1.value = 'A'
LEFT
JOIN zd_product_attached_attributes a3
ON a3.pid = p.id
AND a3.attrid = 3
AND a3.value = 'AB'
WHERE a1.pid IS NOT NULL
OR a3.pid IS NOT NULL
I've just added the LEFT keyword, to specify an outer join; rows from products will be returned with matching rows from a1 and a3, along with rows from products that don't have any matching rows found in a1 or a3.
The WHERE clause tests a column from a1 and a3 to see whether a matching row was returned. If a matching row was found in a1, we are guaranteed that the pid column from a1 will be non-NULL. That column will be returned as NULL only if a matching row was not found.
If we replaced the OR with an AND, we'd be negating the "outerness" of both joins, making it essentially equivalent to the first query above.
To get an XOR type operation (exclusive OR) where we find one matching attribute but not the other, we could change the WHERE clause to read:
WHERE (a1.pid IS NOT NULL AND a3.pid IS NULL)
OR (a3.pid IS NOT NULL AND a1.pid IS NULL)
Use a pivot
You can do this type of query using a pivot. As far as I know, MySQL doesn't have a native, built in pivot, but you can achieve this by transposing the rows and columns of your zd_product_attached_attributes table using:
SELECT pid,
MAX(CASE WHEN attrid = 1 THEN value END) `attrid_1`,
MAX(CASE WHEN attrid = 2 THEN value END) `attrid_2`,
MAX(CASE WHEN attrid = 3 THEN value END) `attrid_3`
FROM zd_product_attached_attributes
GROUP BY pid
This will pivot your table as shown:
+----+---------+-------+ +----+----------+----------+----------+
| attrid | pid | value | | pid| attrid_1 | attrid_2 | attrid_3 |
+----+---+-------------+ +----+----------+----------+----------+
| 1 | 1 | A | | 1 | A | 10 | AB |
| 2 | 1 | 10 | => | 2 | B | 22 | BB |
| 3 | 1 | AB | | 3 | A | 10 | CC |
| 1 | 2 | B | +----+----------+----------+----------+
| 2 | 2 | 22 |
| 3 | 2 | BB |
| 1 | 3 | A |
| 2 | 3 | 10 |
| 3 | 3 | CC |
+--------+---------+---+
So you can select the products id and title using:
SELECT id, title FROM zd_products
LEFT JOIN
(
SELECT pid,
MAX(CASE WHEN attrid = 1 THEN value END) `attrid_1`,
MAX(CASE WHEN attrid = 2 THEN value END) `attrid_2`,
MAX(CASE WHEN attrid = 3 THEN value END) `attrid_3`
FROM zd_product_attached_attributes
GROUP BY pid
) AS attrib_search
ON id = pid
WHERE ( attrib_1 = 'A' AND attrib_3 = 'AB' )
OR ( attrib_2 = 10 AND attrib_3 = 'CC' )
Note: You can use this type of query when you have guaranteed uniqueness on (pid, attrid)
(thanks #spencer7593)
I haven't tested this, but I think it should work:
select title
from zd_products p
join zd_product_attached_attributes a ON a.pid = p.id
where ( attrid = 1 and value = 'A' )
or ( attrid = 3 and value = 'AB' );
If you want to tack on more "searches" you could append more lines similar to the last one (ie. or "or" statements)