Inner Join and Outer Joins Producing the same Result - php

I have 2 tables in Mysql one is holding contractors and another is holding Projects, I want to produce a contractor-Project Report showing the approtining of the projects. problem is INNER JOIN, LEFT and RIGHT OUTER JOINS, all produce the same result only showing the contractor with a project even when i leave out the condition which seems Weird. here are my statements
SELECT DISTINCT (tbl_contractor.name_v), count( tbl_project.name_v )
FROM tbl_contractor
INNER JOIN tbl_project
ON tbl_project.Contractor=tbl_contractor.contractor_id_v
ON tbl_project.Contractor = tbl_contractor.contractor_id_v
LIMIT 0 , 30;
SELECT DISTINCT (tbl_contractor.name_v), count( tbl_project.name_v )
FROM tbl_contractor
LEFT OUTER JOIN tbl_project
ON tbl_project.Contractor = tbl_contractor.contractor_id_v
LIMIT 0 , 30;

You have an aggregate function, COUNT(), without a GROUP BY. This means youir query will return one row only.
You probably need a GROUP BY (contractor):
SELECT tbl_contractor.name_v, COUNT( tbl_project.name_v )
FROM tbl_contractor
LEFT OUTER JOIN tbl_project
ON tbl_project.Contractor = tbl_contractor.contractor_id_v
GROUP BY tbl_contractor.contractor_id_v
LIMIT 0 , 30;

By doing SELECT DISTINCT (tbl_contractor.name_v) the query will only return one row for each contractor name, try removing the distinct and see if you get a better contractor - project result.

These queries are really group by queries on the contractor. If every contractor has at least one project, then the inner and left outer joins will return the same results. If there are contractors without projects, then the results are affected by the LIMIT clause. You are only getting the first 30, and, for whatever reason, the matches are appearing first.

Related

Limit LEFT JOIN results to 1 with flexible where clause

my query looks like that:
SELECT
count(users.id)
FROM users
LEFT JOIN mail_sender_jobs_actions ON mail_sender_jobs_actions.userID = users.id
LEFT JOIN table2 ON table2.userID = users.id
LEFT JOIN table3 ON table3.userID = users.id
WHERE {$flexibleWhereClause}
Now, the mail_sender_jobs_actions table CAN (doesnt need to return anything) return multiple entries. I dont want to group the results but still limit the returns of mail_sender_jobs_actions to 1 so I dont get duplicates... Otherwise the count wouldnt work properly.
Scraped the whole web and found nothing working for me as I want to keep the where clause flexible. Any solution?
EDIT
so to explain the situation. We have a table with users (users). We have a table with actions (mail_seder_jobs_actions). We have other tables related to that query which are not relevant (table1, table2, table3)
If a user does an action, an entry is being created in the actions table.
The where clause is flexible, meaning it is possible that somebody wants to only show users with a specific action.
It is also possible that an action is not relevant to the user, so this entry gets ignored.
With where criteria you have there is no point using left join, since the where criteria applies to the table on the right hand side, effectively turning the left join into an inner join.
Apparently yo do not use any columns from the right hand side table, so instead of using joins, I would use an exists subquery.
SELECT
1 as count,
users.email
FROM users
WHERE EXISTS (SELECT 1
FROM mail_sender_jobs_actions
WHERE mail_sender_jobs_actions.userID = users.id
AND mail_sender_jobs_actions.type = '1'
AND mail_sender_jobs_actions.jobID = '106'
AND {$flexibleWhereClause})
However, there is little point in having the count() because it will always return 1. If you want to count how many records each user has in the mail_sender_jobs_actions table, then you have to use left join, group by, and move the where criteria into the join condition:
SELECT
count(mail_sender_jobs_actions.userID),
users.email
FROM users
LEFT JOIN mail_sender_jobs_actions ON mail_sender_jobs_actions.userID = users.id
AND mail_sender_jobs_actions.type = '1'
AND mail_sender_jobs_actions.jobID = '106'
AND {$flexibleWhereClause}
GROUP BY users.email

What structure must be mysql indexed for my inner join sql statement?

I have nearly 1 million records for my firma table and this statement
SELECT firma.adi,firma.tel, firma.cep, firma.adres,
firma.etiket, sehir.ad, ilce.ilce_adi, ustkat.ust_adi
FROM firma
inner join sektor on sektor.s_fid=firma.id
inner join ustkat on ustkat.ust_id=sektor.s_ustid
inner join sehir on sehir.id=firma.sehir
inner join ilce on ilce.ilceid=firma.ilce
WHERE firma.uyeliktur<4 and firma.onay=1 and
firma.zoom>0 and firma.koordinat>0
GROUP BY firma.id
ORDER BY firma.uyeliktur desc, firma.bastarih desc
how can i use mysql index for this.
You would want an index with all the columns in the where clause. The first should be onay (because of the equality condition). The second should be whichever condition is most restrictive. One of these:
firma(onay, uyeliktur, zoom, koordinat)
firma(onay, zoom, uyeliktur, koordinat)
firma(onay, koordinat, uyeliktur, zoom)
Only the first two columns are used directly for looking up the rows for the where. The other two are a slight efficiency so the where values are there.
If, by the way, these are actually flags, you are better off saying zoom = 1 than zoom > 0.

MySql - Joining another table with multiple rows, inserting a query into a another query?

I've been racking my brain for hours trying work out how to join these two queries..
My goal is to return multiple venue rows (from venues) based on certain criteria... which is what my current query does....
SELECT venues.id AS ven_id,
venues.venue_name,
venues.sub_category_id,
venues.score,
venues.lat,
venues.lng,
venues.short_description,
sub_categories.id,
sub_categories.sub_cat_name,
sub_categories.category_id,
categories.id,
categories.category_name,
((ACOS( SIN(51.44*PI()/180)*SIN(lat*PI()/180) + COS(51.44*PI()/180)*COS(lat*PI()/180)*COS((-2.60796 - lng)*PI()/180)) * 180/PI())*60 * 1.1515) AS dist
FROM venues,
sub_categories,
categories
WHERE
venues.sub_category_id = sub_categories.id
AND sub_categories.category_id = categories.id
HAVING
dist < 5
ORDER BY score DESC
LIMIT 0, 100
However, I need to include another field in this query (thumbnail), which comes from another table (venue_images). The idea is to extract one image row based on which venue it's related to and it's order. Only one image needs to be extracted however. So LIMIT 1.
I basically need to insert this query:
SELECT
venue_images.thumb_image_filename,
venue_images.image_venue_id,
venue_images.image_order
FROM venue_images
WHERE venue_images.image_venue_id = ven_id //id from above query
ORDER BY venue_images.image_order
LIMIT 1
Into my first query, and label this new field as "thumbnail".
Any help would really be appreciated. Thanks!
First of all, you could write the first query using INNER JOIN:
SELECT
...
FROM
venues INNER JOIN sub_categories ON venues.sub_category_id = sub_categories.id
INNER JOIN categories ON sub_categories.category_id = categories.id
HAVING
...
the result should be identical, but i like this one more.
What I'd like to do next is to JOIN a subquery, something like this:
...
INNER JOIN (SELECT ... FROM venue_images
WHERE venue_images.image_venue_id = ven_id //id from above query
ORDER BY venue_images.image_order
LIMIT 1) first_image
but unfortunately this subquery can't see ven_id because it is evaluated first, before the outer query (I think it's a limitation of MySql), so we can't use that and we have to find another solution. And since you are using LIMIT 1, it's not easy to rewrite the condition you need using just JOINS.
It would be easier if MySql provided a FIRST() aggregate function, but since it doesn't, we have to simulate it, see for example this question: How to fetch the first and last record of a grouped record in a MySQL query with aggregate functions?
So using this trick, you can write a query that extracts first image_id for every image_venue_id:
SELECT
image_venue_id,
SUBSTRING_INDEX(
GROUP_CONCAT(image_id order by venue_images.image_order),',',1) as first_image_id
FROM venue_images
GROUP BY image_venue_id
and this query could be integrated in your query above:
SELECT
...
FROM
venues INNER JOIN sub_categories ON venues.sub_category_id = sub_categories.id
INNER JOIN categories ON sub_categories.category_id = categories.id
INNER JOIN (the query above) first_image on first_image.image_venue_id = venues.id
INNER JOIN venue_images on first_image.first_image_id = venue_images.image_id
HAVING
...
I also added one more JOIN, to join the first image id with the actual image. I couldn't check your query but the idea is to procede like this.
Since the query is now becoming more complicated and difficult to mantain, i think it would be better to create a view that extracts the first image for every venue, and then join just the view in your query. This is just an idea. Let me know if it works or if you need any help!
I'm not too sure about your data but a JOIN with the thumbnails table and a group by on your large query would probably work.
GROUP BY venues.id

Left Join only returning one row

I am trying to join two tables. I would like all the columns from the product_category table (there are a total of 6 now) and count the number of products, CatCount, that are in each category from the products_has_product_category table. My query result is 1 row with the first category and a total count of 68, when I am looking for 6 rows with each individual category's count.
<?php
$result = mysql_query("
SELECT a.*, COUNT(b.category_id) AS CatCount
FROM `product_category` a
LEFT JOIN `products_has_product_category` b
ON a.product_category_id = b.category_id
");
while($row = mysql_fetch_array($result)) {
echo '
<li class="ui-shadow" data-count-theme="d">
' . $row['product_category_name'] . '<span class="ui-li-count">' . $row['CatCount'] . '</span></li>';
}
?>
I have been working on this for a couple of hours and would really appreciate any help on what I am doing wrong.
In absence of a GROUP BY clause, the COUNT() aggregate can only return one row, holding the total count for the table after filtering by the WHERE clause. I suspect you mean to GROUP BY b.category_id in a LEFT JOIN against a subquery:
SELECT
a.*,
catcount
FROM
product_category a
LEFT JOIN (
SELECT category_id, COUNT(*) AS catcount
FROM products_as_product_category
GROUP BY category_id
) subcount ON a.product_category_id = subcount.category_id
It is because MySQL is lenient about the contents and presence of the GROUP BY clause that your query was syntactically successful in the first place. It would have failed in most other RDBMS because of a missing GROUP BY. Depending on the contents of your table product_category, MySQL may permit you to do the above without the joined subquery, and instead include the correct columns from product_category in the GROUP BY clause, but without knowing the contents of that table I can't say for sure, and the above method is standard and portable across other RDBMS without relying on MySQL's lenience.
A final note, although I have done it above, I never recommend doing SELECT * in a JOIN query. Common column names between the joined tables require aliases to differentiate in the PHP API, introducing confusion. Always best to be explicit about the columns you actually need in the SELECT list.

Slow mysql query with subqueries

I'm experiencing a problem with this query in PHP. It is so slow I cannot connect to the server for few minutes after executing it. I've been removing separate subqueries away from main query and found out, that it works perfectly smooth, after I remove the last inner query.
Table "u" has 30.000 rows, table "u_r" around 17.000, table "u_s" around 13.000 and table "s" around 100. Even though tables "u_r" and "u_s" have lots of rows, only 2-3 rows have same "id_u" that matches the condition.
I hope, I provided enough information. If you need to know anything else, feel free to ask in the comments.
SELECT DISTINCT id_p
FROM u
WHERE
'$x' IN(
SELECT id_p
FROM u_p
WHERE id_u=u.id_u
)
AND
(
'$y' IN (
SELECT id_r
FROM u_r
WHERE id_u=u.id_u
)
OR
'$y' IN (
SELECT DISTINCT id_r
FROM s
WHERE id_s IN (
SELECT id_s //without this query, everything works fine
FROM u_s
WHERE id_u=u.id_u
)
)
)
I think you can change:
SELECT DISTINCT id_r
FROM s
WHERE id_s IN (
SELECT id_s //without this query, everything works fine
FROM u_s
WHERE id_u=u.id_u
to
SELECT s.id_s
FROM u_s
INNER JOIN s ON u_s=id_u=u.id_u
WHERE u_s.id_u = u.id_u
GROUP BY s.id_r
Not tested as I'm trying to wrap my head around the structure.
Also remember to check the indexes. As a rough guide, you should have anything in the "WHERE" and anything in the "ON" indexed:
u_p.id_u
u_r.id_u
u_s.id_u
s.id_s
Test that this produces the same results - should be much faster.
Replace the IN statements with inner joins. More opportunities for query optimiser to optimise this way
Eliminate your OR statement - I did this using a UNION. I think you could probably eliminate the OR using a LEFT JOIN instead if you wanted.
You'll need to test this - I don't have any sample data.
select distinct id_p
from u
inner join u_p
on u.id_u = u_p.id_u
and u_p.id_u = '$x'
inner join
(
select id_r.id_u
from u_r
where id_r = '$y'
union
select id_r
from s
inner join u_s
on s.id_u = u_s.id_u
where id_r = '$y'
) as subq
on u.id_u = subq.id_u

Categories