mysql left join with 9 tables - php

So the situation is I have a query that involves 9 tables and I need to write it so it returns all records even when the impactid in the workorderstates table is NULL.
Previous to the below query I noticed I wasn't getting all results that were "open" because initially I just had where workorderstates.impactid = impactdefiniton.impactid and in the situations where impactid is NULL in the workorderstates table this condition would not be true, thus eliminating records that should be returned because they are in fact "open".
So I devised this query below but every time I run it it will not work. It will will return not unique table alias workorder. If I use aliases for tables it just moves on the right tables in the join as not being unique. Can anyone offer me any help on restructuring the query so it will work? I've tried a lot of variations and interestingly enough the second query ALMOST works but it returns duplicate records (in this case four of the same record)
select workorder.workorderid, workorder.siteid,
FROM_UNIXTIME(workorder.CREATEDTIME/1000, '%m-%d-%Y %H:%i:%s') as createdate,
categoryname, IFNULL(workorderstates.impactid, "No Set") as impactid,
IFNULL(impactdefinition.name, "Not Set") as impactname, first_name,
sdorganization.name, statusname, title
from workorder, statusdefinition, sitedefinition, sdorganization,
prioritydefinition, categorydefinition, sduser, aaauser, workorderstates
left Join impactdefinition on workorderstates.impactid = impactdefinition.impactid
left join workorder on workorder.workorderid = workorderstates.workorderid
left join workorderstates on workorderstates.statusid = statusdefinition.statusid
left join workorder on workorder.siteid = sitedefinition.siteid
left join sitedefinition on sitedefinition.siteid = sdorganization.org_id
left join workorderstates on workorderstates.categoryid = categorydefinition.categoryid
left join workorder on workorder.requesterid = sduser.userid
left join sduser on sduser.userid = aaauser.user_id
where statusname='Open' and workorder.createdtime >= '1352678400000'
and sdorganization.name='MAPL'
order by workorder.workorderid
Query that almost works but is ugly (returns duplicated records):
select workorder.workorderid, workorder.siteid,
FROM_UNIXTIME(workorder.CREATEDTIME/1000, '%m-%d-%Y %H:%i:%s') as createdate,
categoryname, IFNULL(workorderstates.impactid, "No Set") as impactid,
IFNULL(impactdefinition.name, "Not Set") as impactname, first_name,
sdorganization.name, statusname, title
from workorder, statusdefinition, sitedefinition, sdorganization,
prioritydefinition, categorydefinition, sduser, aaauser, workorderstates
left Join impactdefinition on workorderstates.impactid = impactdefinition.impactid
where workorder.workorderid = workorderstates.workorderid
and workorderstates.statusid = statusdefinition.statusid
and workorder.siteid = sitedefinition.siteid
and sitedefinition.siteid = sdorganization.org_id
and workorderstates.categoryid = categorydefinition.categoryid
and workorder.requesterid = sduser.userid and sduser.userid = aaauser.user_id
and statusname='Open' and workorder.createdtime >= '1352678400000'
and sdorganization.name='MAPL'
order by workorder.workorderid
Any ideas of how to get this query working??? Thanks guys!

I took a look at your query and I think you have some basic misunderstandings about JOINs and how to write them. It's like you're just guessing at syntax at random, and that's not the way to write code.
I examined your query and converted it into SQL-92 syntax. I had to make some inferences about join conditions, so I can't guarantee it's correct for your application, but it's a lot closer to a legal query.
Only I couldn't find any condition in your example for the join to your prioritydefinition table. That's likely to be the cause of your duplicate rows. You're generating what's called a Cartesian product.
select workorder.workorderid, workorder.siteid,
FROM_UNIXTIME(workorder.CREATEDTIME/1000, '%m-%d-%Y %H:%i:%s') as createdate,
categoryname, IFNULL(workorderstates.impactid, "No Set") as impactid,
IFNULL(impactdefinition.name, "Not Set") as impactname, first_name,
sdorganization.name, statusname, title
from workorder
inner join statusdefinition on workorderstates.statusid = statusdefinition.statusid
inner join sitedefinition on workorder.siteid = sitedefinition.siteid
inner join sdorganization on sitedefinition.siteid = sdorganization.org_id
inner join prioritydefinition ...NO JOIN CONDITION FOUND...
inner join categorydefinition on workorderstates.categoryid = categorydefinition.categoryid
inner join sduser on workorder.requesterid = sduser.userid
inner join aaauser on sduser.userid = aaauser.user_id
inner join workorderstates on workorder.workorderid = workorderstates.workorderid
left Join impactdefinition on workorderstates.impactid = impactdefinition.impactid
where statusname='Open'
and workorder.createdtime >= '1352678400000'
and sdorganization.name='MAPL'
order by workorder.workorderid
You really need to get someone who knows your application and also knows how to write SQL to tutor you before you write any more SQL joins.

I too reformatted your query but have it more visually hierarchical to show relations from the first (left-side) table to what it is getting its details from (right-side) table. As Bill mentioned, you had an extra table that was not doing anything and thus your Cartesian product.
Now, if you ARE stuck and have no one else to really help, here is a basic of LEFT-JOIN vs INNER-JOIN. Left-join basically says I want every record from the table on the left (as I have listed first) REGARDLESS of there being a record found on the right side. If there IS a match, great, no problem... but if no match, your query will still run.
So, I've set to all LEFT-JOINs. You can change as needed for those you know MUST always exist... such as a work order is entered by a "user". So that you could change. Hopefully this helps you out. Also look at how I've nested from work order to work order states and from work order states to get the corresponding status definition and other things associated with the work order states table. Others were directly related with the work order, so those are at THAT hierarchical level.
One last note... not all your fields were table.field reference (which I changed to aliases to help readability). QUALIFY ALL your fields so you and others trying to help, or read your code in the future know the origin of the field, not just guessing (its in one of the tables)
select
WO.workorderid,
WO.siteid,
FROM_UNIXTIME(WO.CREATEDTIME/1000, '%m-%d-%Y %H:%i:%s') as createdate,
categoryname,
IFNULL(WOS.impactid, "No Set") as impactid,
IFNULL(ImpD.name, "Not Set") as impactname, first_name,
SDO.name,
statusname,
title
from
workorder WO
LEFT JOIN workorderstates WOS
ON WO.workorderid = WOS.workorderid
LEFT JOIN statusdefinition StatD
ON WOS.statusid = StatD.statusid
LEFT JOIN categorydefinition CatD
ON WOS.categoryid = CatD.categoryid
LEFT JOIN impactdefinition ImpD
ON WOS.impactid = ImpD.impactid
LEFT JOIN sitedefinition SiteD
ON WO.siteid = SiteD.siteid
LEFT JOIN sdorganization SDO
ON SiteD.siteid = SDO.org_id
and SDO.name = 'MAPL'
LEFT JOIN sduser U
ON WO.requesterid = U.userid
LEFT JOIN aaauser AU
ON U.userid = AU.user_id
where
statusname = 'Open'
and WO.createdtime >= '1352678400000'
order by
WO.workorderid

Related

MySQL query with multiple left joins and different WHERE clauses for each

I'm modifying a query that previously left joined two tables, but now needs to add a third, and I'm struggling with some of the conditions being applied.
This is the original query:
$sql = "SELECT COUNT(act.rowId) q4Act
FROM accounts a
LEFT JOIN leads l ON a.accountId = l.accountId
LEFT JOIN activities act ON act.leadId = l.leadId AND act.classification = 'Positive' AND activityDate >= '2016-10-31' AND activityDate < '2017-02-01'
WHERE a.accountId = '$id'
GROUP BY a.accountName ";
This worked fine, ensuring that the only results from the activities table were meeting the three AND conditions and the accountId was the one being queried.
However, I split two tables and wanted to update this query. My first attempt was this next query, where I added the new left join, removed a condition from the first one and added it to the second one. This, however, returns totally different results.
$sql = "SELECT COUNT(act.rowId) q4Act
FROM accounts a
LEFT JOIN leads l ON a.accountId = l.accountId
LEFT JOIN activities act ON act.leadId = l.leadId AND activityDate >= '2016-10-31' AND activityDate < '2017-02-01'
LEFT JOIN interestingMoments im ON im.rowId = act.imId AND im.classification = 'Positive'
WHERE a.accountId = '$id'
GROUP BY a.accountName ";
My next attempt was to move the condition on the new table below the joins like this:
$sql = "SELECT COUNT(act.rowId) q4Act
FROM accounts a
LEFT JOIN leads l ON a.accountId = l.accountId
LEFT JOIN activities act ON act.leadId = l.leadId AND activityDate >= '2016-10-31' AND activityDate < '2017-02-01'
LEFT JOIN interestingMoments im ON im.rowId = act.imId
WHERE a.accountId = '$id'
AND im.classification = 'Positive'
GROUP BY a.accountName ";
The above returns the same results when there's a positive count, though it doesn't return a 0 count like the first query did. This isn't a big deal, but I'd like to see if this is the proper way to construct this query.
Is this the best way, or should I be going about it a different way?
Placing the constraint on the JOIN versus in the WHERE clause really creates no difference. If the tables is huge, it may affect time to run the SELECT, but that difference is likely small and can still probably be mitigated by indexes.

MYSQL/PHP: Concat returning to many fields on LEFT JOIN

I had a SELECT query with a LEFT JOIN working as desired. I then added one more table via a smilar LEFT JOIN and now I am getting a wierd result. Basically, for a group_concat where I was getting one item for every record, I am getting eight records. I don't see why this is happening because the join to the new table is analagous to several other joins that do not have this problem (that I have omitted from the example for clarity).
Here is the query that is fine:
$sql = "SELECT t.*,
group_concat(tf.todoid) as `tftodoid`,
group_concat(tf.id) as `tfid`,
group_concat(tf.filedescript) as `tffiledescript`,
group_concat(tf.filename) as `tffilename`,
group_concat(tf.founderid) as `tffounderid`,
group_concat(tf.ext) as `tfext`,
group_concat(tf.lasttouched) as `tilt`
FROM titles `t`
LEFT JOIN titlefiles `tf`
ON (tf.todoid = t.id AND tf.founderid = '$userid')
WHERE t.userid='$userid'
GROUP BY t.id";
And here is the query with the extra join that is now spilling out the multiple copies of the items:
$sql = "SELECT t.*,
group_concat(tf.todoid) as `tftodoid`,
group_concat(tf.id) as `tfid`,
group_concat(tf.filedescript) as `tffiledescript`,
group_concat(tf.filename) as `tffilename`,
group_concat(tf.founderid) as `tffounderid`,
group_concat(tf.ext) as `tfext`,
group_concat(tf.lasttouched) as `tilt`,
group_concat(s.id) as `stepid`,
group_concat(s.step) as `steps`
FROM titles `t`
LEFT JOIN titlefiles `tf`
ON (tf.titleid = t.id AND tf.founderid = '$userid')
LEFT JOIN steps `s`
ON s.titleid = t.id
WHERE t.userid='$userid'
GROUP BY t.id";
Here is an example of output in JSON showing the difference:
First query:
"tfid":"56,57,58,59,60,61,62,63,64,65,66,67,68,75,76,81"
Second query:
"tfid":"56,57,58,59,60,61,62,63,64,65,66,67,68,75,76,81,56,57,58,59,60,61,62,63,64,65,66,67,68,75,76,81,56,57,58,59,60,61,62,63,64,65,66,67,68,75,76,81,56,57,58,59,60,61,62,63,64,65,66,67,68,75,76,81,56,57,58,59,60,61,62,63,64,65,66,67,68,75,76,81,56,57,58,59,60,61,62,63,64,65,66,67,68,75,76,81,56,57,58,59,60,61,62,63,64,65,66,67,68,75,76,81,56,57,58,59,60,61,62,63,64,65,66,67,68,75,76,81,56,57,58,59,60,61,62,63,64,65,66,67,68,75,76,81",
I suspect the problem has something to do with the JOIN or with the Group By statements but I can't see how to fix.
How can I ensure that I get only one fileid per file as opposed to eight?
Alter the line as follows:
group_concat(DISTINCT tf.id) as `tfid`,
This then only gets you the unique ids.
If you want them ordered add:
group_concat(DISTINCT tf.id ORDER BY tf.id ASC) as `tfid`,

How can i optimize MySQL query, event if it have index

Here is my query which is taking 17.9397 sec time to get response:
SELECT allbar.iBarID AS iBarID,
allbar.vName AS vName,
allbar.tAddress AS tAddress,
allbar.tDescription AS tDescription,
(SELECT COUNT(*)
FROM tbl_post p
WHERE p.vBarIDs = allbar.iBarID) AS `total_post`,
allbar.bar_usbg AS bar_usbg,
allbar.bar_enhance AS bar_enhance,
(SELECT count(*)
FROM tbl_user
WHERE FIND_IN_SET(allbar.iBarID,vBarIDs)
AND (eType = 'Bartender'
OR eType = 'Bar Manager'
OR eType = 'Bar Owner')) AS countAss,
allbar.eStatus AS eStatus
FROM
(SELECT DISTINCT b.iBarID AS iBarID,
b.vName AS vName,
b.tAddress AS tAddress,
(CASE LENGTH(b.tDescription) WHEN 0 THEN '' WHEN LENGTH(b.tDescription) > 0
AND LENGTH(b.tDescription) < 50 THEN CONCAT(LEFT(b.tDescription, 50),'...') ELSE b.tDescription END) AS tDescription,
b.usbg AS bar_usbg,
b.enhance AS bar_enhance,
b.eStatus AS eStatus
FROM tbl_bar b,
tbl_user u
WHERE b.iBarID <> '-10') AS allbar
I have tried EXPLAIN, here is the result of that:
Can anyone explain me this EXPLAIN result?
You should totaly rewrite that query, it's complete nonsense.
In this part
(SELECT DISTINCT b.<whatever>
FROM tbl_bar b,
tbl_user u
WHERE b.iBarID <> '-10') AS allbar
what you're basically doing is connecting every row from table tbl_bar with every row from tbl_user. Then filter tbl_bar, and when everything is selected (maybe MySQL has to write everything in a temporary table before doing this) return the result set without duplicates. You don't ever want to do that. Especially when you don't even select anything from tbl_user. When there's a connection, specify it. If there's none, don't join those tables or create a connection. I don't know if or how your tables are connected, but it should look something like this:
(SELECT DISTINCT b.<whatever>
FROM tbl_bar b
JOIN tbl_user u ON b.user_id = u.id /*or whatever the connection is*/
WHERE b.iBarID <> '-10') AS allbar
Then you have this ugly subquery.
(SELECT COUNT(*)
FROM tbl_post p
WHERE p.vBarIDs = allbar.iBarID) AS `total_post`,
allbar.bar_usbg AS bar_usbg,
allbar.bar_enhance AS bar_enhance,
which is by the way dependent (see your explain output). Which means, that this subquery is executed for every row of your outer query (yes, the one with the cross join as discussed above). Instead of this subquery, join the table in the outer query and work with GROUP BY.
So far the query should look something like this:
SELECT
b.iBarID AS iBarID,
b.vName AS vName,
b.tAddress AS tAddress,
b.tDescription AS tDescription,
COUNT(*) AS `total_post`,
allbar.bar_usbg AS bar_usbg,
allbar.bar_enhance AS bar_enhance
FROM
tbl_bar b
JOIN tbl_user u ON b.user_id = u.id
JOIN tbl_post p ON p.vBarIDs = b.iBarID
WHERE b.iBarID <> '-10'
GROUP BY b.iBarID
(In fact, this is not really right. Rule is, every column in the SELECT clause should either be in the GROUP BY clause as well or have an aggregate function (like count() or max() applied to it. Otherwise a random row of each group is displayed. But this is just an example. You will have to work out the details.)
Now comes the worst part.
(SELECT count(*)
FROM tbl_user
WHERE FIND_IN_SET(allbar.iBarID,vBarIDs)
AND (eType = 'Bartender'
OR eType = 'Bar Manager'
OR eType = 'Bar Owner')) AS countAss,
allbar.eStatus AS eStatus
The use of FIND_IN_SET() suggests, that you're storing multiple values in one column. Again, you never ever want to do that. Please read this answer to Is storing a delimited list in a database column really that bad? and then redesign your database. I won't help you with this one, as this clearly is stuff for a separate question.
All this didn't really explain the EXPLAIN result. For this question, I would have to write a whole tutorial, which I won't do, since everything is in the manual, as always.

MySQL Inner Join, selecting from multiple tables

I'm really struggling to get my head around this. I am trying to run a SELECT query from multiple tables.
This is what I have so far that doesn't work;
SELECT jira_issues.*, session_set.* FROM jira_issues, session_set
INNER JOIN reports on jira_issues.report_id = reports.id
WHERE jira_issues.report_id = 648
I have other tables (session_set, report_device) which has a ReportID and report_id column respectively.
I have a report table which has a Primary Key id. In the other tables the report.id key is linked with foreign keys.
Ultimately what I am trying to achieve is this:
I have an entry in the reports table with an id of 648. In the other tables (jira_issues, report_device, session_set), I also have entries which has a foreign key linked to the report id in the report table.
I want to run one SELECT Query to query the tables (jira_issues, report_device and session_set) and get all the data from them based on the report.id.
Thanks!
What about this:
SELECT * FROM jira_issues ji
LEFT JOIN session_set ss ON ji.report_id = ss.ReportID
LEFT JOIN report_device rd ON rd.report_id = ji.report_id
WHERE ji.report_id = 648;
Just say "no" to commas in the from clause. Always use explicit join syntax:
SELECT ji.*, session_set.*
FROM jira_issues ji inner join
reports r
on ji.report_id = r.id inner join
session_set ss
on ss.ReportId = r.report_id
WHERE ji.report_id = 648;
If some of the tables might have no corresponding rows, you might want left outer join instead of inner join.
Kindly try this out. You may get syntax error.
SELECT a., b. FROM jira_issues a, session_set b, reports c
Where a.report_id = c.id and b.report_id = c.id AND a.report_id = 648

get count of posts based on count(*)

i am trying to get number of posts that i have
Here is my query
$Query="
SELECT t.*,u.*,c.*
FROM posts as t
LEFT JOIN relations as r on r.post_id = t.post_id
LEFT JOIN users as u on t.auther_id = u.auther_id
LEFT JOIN categories as c on c.cate_id = r.cate_id
GROUP BY t.post_id
";
$Query=mysql_query($Query);
$numberOfPosts=mysql_num_rows($Query);
This query is works very well
but i am trying to convert it, i want make it faster
i want use count(*) instead of t.*
because when i use t.*, it gets the full data of posts and categories
but i want to get count only, So i decided to use count(*) but i don't know how to use it with query like this
Edit
i've replaced SELECT t.*,u.*,c.* with SELECT count(t.*)
But i got mysql Error Warning: mysql_fetch_assoc(): supplied argument
Edit 2:
i am trying SELECT count(t.post_title)
I Got this results
Array ( [count(t.post_id)] => 10 )
But i have only 2 posts!
$Query="
SELECT t.*,u.*,c.*
FROM posts as t
LEFT JOIN relations as r on r.post_id = t.post_id
LEFT JOIN users as u on t.auther_id = u.auther_id
LEFT JOIN categories as c on c.cate_id = r.cate_id
GROUP BY t.post_id
";
$Query=mysql_query($Query);
$numberOfPosts=mysql_num_rows($Query);
Let's take a step back and analyze this query for a moment.
You're selecting everything from three out of four tables used in the query. The joins create some logic to limit what you select to the proper categories, authors, etc. At the end of the day you are getting a lot of data from the database, then in PHP simply asking it how many rows were returned (mysql_num_rows). Instead, what #Dagon is trying to suggest in comments, is that you have MySQL simply count the results, and return that.
Let's refactor your query:
$query = "
SELECT COUNT(t.post_id) AS qty
FROM posts as t
LEFT JOIN relations AS r ON r.post_id = t.post_id
LEFT JOIN users AS u ON t.auther_id = u.auther_id
LEFT JOIN categories AS c ON c.cate_id = r.cate_id
GROUP BY t.post_id
";
$result = mysql_query($query);
$result_row = mysql_fetch_assoc($result);
$numberOfPosts = $result_row['qty'];
(You could also use Barattlo's custom execute_scalar function to make it more readable.)
I would need to see your table structures to be of more help on how to optimize the query and get the desired results.
try doing this:
$Query="
SELECT count(t.*) as count_all
FROM posts as t
LEFT JOIN relations as r on r.post_id = t.post_id
LEFT JOIN users as u on t.auther_id = u.auther_id
LEFT JOIN categories as c on c.cate_id = r.cate_id
GROUP BY t.post_id
";
$Query=mysql_query($Query);
$numberOfPosts=mysql_num_rows($Query);
You want to do
SELECT count(t.id) AS count FROM ....
//do query with PDO or whatever you are using
$rows = mysql_fetch_assoc();
$num_rows = $rows['count'];
You should probably simply use
SELECT count(*) as postingcount FROM posts
Why?
Because you do not have a WHERE clause, so there are no restrictions. Your JOINS do not ADD more rows to the resultset, and in the end your GROUP BY merges every duplicate occurance of a post_id that might have occurred because of joining back into one row. The result should only be counted, so assuming that the real number you want to know is the number of data sets inside the table posts, you do not need any join, and doing count(*) really is a very fast operation on tables in MySQL.
Remember to check if mysql_query returns false, because then you have to check mysql_error() and see why your query has an error.

Categories