Any ideas why index is not using on table SD and how to fix it?
I tried to remove the group and sort clauses but still same issue and cant find that is the problem
P.S. dont read this system wont let me post because code is more than description
Query
SELECT sd.filter_group_id, fgd.name AS group_name, sdc.filter_id AS filter_id, fd.name,
COUNT(DISTINCT p2c.product_id) AS total, f.sort_order, sd.sort_order AS sort,
(CASE
WHEN fgd.custom_order = 1
THEN COUNT(p2c.product_id)
END) AS custom_order
FROM oc_sd_filter sd
JOIN oc_product_to_category p2c ON p2c.category_id = sd.category_id
JOIN oc_product_filter sdc18 ON sdc18.product_id = p2c.product_id
JOIN oc_product_filter sdc21 ON sdc21.product_id = p2c.product_id
JOIN oc_product p ON p.product_id = p2c.product_id
JOIN oc_product_filter sdc ON sdc.product_id = p2c.product_id
JOIN oc_filter f ON sdc.filter_id = f.filter_id
JOIN oc_filter_description fd ON sdc.filter_id = fd.filter_id
JOIN oc_filter_group_description fgd ON fd.filter_group_id = fgd.filter_group_id
WHERE sd.category_id = '93'
AND p.status = '1'
AND sd.filter_group_id = fd.filter_group_id
AND sd.status = 1
AND sdc18.filter_id IN (199,200,120,321,611,451,380,542)
AND sdc21.filter_id IN (241,242)
GROUP BY fd.filter_id, fd.filter_group_id
ORDER BY sd.sort_order ASC,
(CASE
WHEN fgd.custom_order = 0
THEN f.sort_order
END) ASC,
(CASE
WHEN fgd.custom_order = 1
THEN COUNT(p2c.product_id)
END) DESC
EXPLAIN
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE sd ALL filter,cat,status NULL NULL NULL 11 Using where; Using temporary; Using filesort
1 SIMPLE fgd ref PRIMARY,filter_group_id PRIMARY 4 example_db.sd.filter_group_id 1
1 SIMPLE p2c ref PRIMARY,category_id, category_id 4 example_db.sd.category_id 59 Using index
1 SIMPLE p eq_ref PRIMARY,status,product_id PRIMARY 4 example_db.p2c.product_id 1 Using where
1 SIMPLE sdc ref PRIMARY PRIMARY 4 example_db.p2c.product_id 9 Using index
1 SIMPLE fd ref PRIMARY,filter PRIMARY 4 example_db.sdc.filter_id 1 Using where
1 SIMPLE f eq_ref PRIMARY PRIMARY 4 example_db.sdc.filter_id 1
1 SIMPLE sdc21 ref PRIMARY PRIMARY 4 example_db.p2c.product_id 9 Using where; Using index
1 SIMPLE sdc18 ref PRIMARY PRIMARY 4 example_db.p2c.product_id 9 Using where; Using index
Table
CREATE TABLE `oc_sd_filter` (
`id` int(11) NOT NULL,
`category_id` int(11) NOT NULL,
`filter_group_id` int(11) NOT NULL,
`status` int(11) NOT NULL,
`sort_order` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
--
-- Indexes for table `oc_sd_filter`
--
ALTER TABLE `oc_sd_filter`
ADD PRIMARY KEY (`id`),
ADD KEY `filter` (`filter_group_id`),
ADD KEY `cat` (`category_id`),
ADD KEY `status` (`status`),
ADD KEY `sort_order` (`sort_order`);
Suggested composite indexes:
sd: INDEX(category_id, status, filter_group_id, sort_order)
fgd: INDEX(filter_group_id, name, custom_order)
sdc: INDEX(product_id, filter_id)
fd: INDEX(filter_group_id, filter_id, name)
p2c: INDEX(category_id, product_id)
f: INDEX(filter_id, sort_order)
oc_product_filter: INDEX(product_id, filter_id)
p: INDEX(status, product_id)
When adding a composite index, DROP index(es) with the same leading columns.
That is, when you have both INDEX(a) and INDEX(a,b), toss the former.
If that does not help enough, come back and we can talk about turning the query inside out -- so that the GROUP BY is done before most of the JOINs. But first, how many rows in the resultset? How many rows if you take out the GROUP BY clause?
Example (from Comment):
SELECT filter_group_id
FROM sd
WHERE status = 1
ORDER BY sort_order
The Optimal index is both composite and "covering"; the order is important:
INDEX(status, sort_order, filter_group_id)
Any longer index starting with those is essentially "as good". Any shorter index (eg, INDEX(status, sort_order) or starting with that) will be "good", but "not as good".
In particular, the 4-column index I provided above is not useful. It is OK to add both indexes; the Optimizer will decide which index to use for each SELECT.
Related
I have a problem with performance with MySQL. How can i improve it?
The situation is following:
Table “backlogsap„ have about 4 mio entries.
Indexes are created
This table have FK and other tables have FK to this table => can’t
create partitions.
This query need about 140 seconds to complete:
select
idmaterial,
materialgroup,
materialgroupcategory,
name,
dispatchgroup,
idsupplier,
group_concat(distinct sellingorganizationname) as sellingorganizationnames,
group_concat(distinct idordertype) as idordertypes,
group_concat(distinct idpositiontype) as idpositiontypes,
sum(newOrUpdated and isCritical) as classA,
sum(newOrUpdated and not isCritical) as classB,
sum(processingstate <3) as classC,
(select count(innerBacklogsAp.idmaterial)
from backlogsap as innerBacklogsAp
where innerBacklogsAp.idmaterial = src.idmaterial and IsDeleted = 0) as countReplacementVehiclerRequests
from
(select
backlogsap.idmaterial as idmaterial,
backlog.processingstate as processingstate,
material.idsupplier as idsupplier,
backlogsap.sellingorganizationname as sellingorganizationname,
backlogsap.idpositiontype as idpositiontype,
backlogsap.idordertype as idordertype,
materialindistributioncenter.dispatchgroup as dispatchgroup,
material.name as name,
material.idmaterialgroup as materialgroup,
materialgroup.idmaterialgroupcategory as materialgroupcategory,
(processingstate = 0 or processingstate = 1) as newOrUpdated,
((cancellation.state is not null and cancellation.state = 0 ) or
(reminderrequest.state is not null and (reminderrequest.state = 2 or reminderrequest.state = 0))
) as isCritical
from backlogsap
join backlog using (idbacklogsap)
left join cancellation using (idcancellation)
left join reminderrequest on backlog.IdReminderRequest = reminderrequest.idreminder
left join material using (idmaterial)
left join materialindistributioncenter using (idmaterial, iddistributioncenter)
left join materialgroup using (idmaterialgroup)
where (idcancellation is null or cancellation.State not in (1)) and
backlogsap.isdeleted = 0 and
backlogsap.idordertype not in ('ZAP', 'ZAK', 'ZAKO', 'ZAKZ', 'ZAPM') and
iddistributioncenter = 1469990
) as src
group by idmaterial
order by classA desc, classB desc, classC, idmaterial desc
Explain
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY <derived3> ALL 26960 Using temporary; Using filesort
3 DERIVED backlogsap index_merge PRIMARY,fk_BacklogSap_OrderType1_idx,
fk_BacklogSap_MaterialInDistributionCenter1_idx,
perform_backlogsap_isdeleted,
fk_BacklogSap_DistributionCenter_idx perform_backlogsap_isdeleted,fk_BacklogSap_DistributionCenter_idx 1,4 35946 Using intersect(perform_backlogsap_isdeleted,fk_BacklogSap_DistributionCenter_idx); Using where
3 DERIVED backlog eq_ref idBacklogSAP_UNIQUE,
fk_Backlog_BacklogSap1_idx,
fk_Backlog_Cancellation1_idx idBacklogSAP_UNIQUE 4 ...backlogsap.IdBacklogSap 1
3 DERIVED cancellation eq_ref PRIMARY PRIMARY 4 ...backlog.IdCancellation 1 Using where
3 DERIVED reminderrequest eq_ref PRIMARY PRIMARY 4 ...backlog.IdReminderRequest 1
3 DERIVED material eq_ref PRIMARY PRIMARY 45 ...backlogsap.IdMaterial 1
3 DERIVED materialindistributioncenter eq_ref PRIMARY,
unqiue_IdDistributionCenter_IdMaterial,
fk_MaterialDistributionCenter_DistributionCenter1_idx,
fk_MaterialDistributionCenter_Material1_idx PRIMARY 49 const,...backlogsap.IdMaterial 1
3 DERIVED materialgroup eq_ref PRIMARY PRIMARY 137 ....material.IdMaterialGroup 1
2 DEPENDENT SUBQUERY innerBacklogsAp ref perform_backlogsap_isdeleted,
idx_backlogsap_IdMaterial idx_backlogsap_IdMaterial 45 func 8 Using where
Solved: created combined Index (idmaterial, IsDeleted)
I have a query which is running a bit slow. It takes a table containing the items that result from a search and then gets the categories that contain 1 or more of these items. The categories (~300) are stored in a nested set model with around 4 levels. I need to know the counts at each level (so an item might be in the grand child category, but also needs to be counted in the child and parent categories).
The query to do this is as follows:-
SELECT category_parent.id,
category_parent.depth,
category_parent.name AS item_sub_category,
category_parent.left_index,
category_parent.right_index,
COUNT(DISTINCT item.id) as total
FROM search_enquiries_found
INNER JOIN item ON search_enquiries_found.item_id = item.id
INNER JOIN category ON item.mmg_code = category.mmg_code
INNER JOIN category category_parent ON category.left_index BETWEEN category_parent.left_index AND category_parent.right_index
WHERE search_enquiries_found.search_enquiry_id = 35
AND item.cost_price > 0
GROUP BY category_parent.id,
category_parent.depth,
item_sub_category,
category_parent.left_index,
category_parent.right_index
ORDER BY category_parent.left_index
With around 12k records found this is taking ~1.5 seconds. The explain follows:-
id select_type table partitions type possible_keys key key_len ref rows Extra
1 SIMPLE category NULL ALL left_index NULL NULL NULL 337 Using temporary; Using filesort
1 SIMPLE item NULL ref PRIMARY,id,mmg_code mmg_code 27 em_entaonline.category.mmg_code 43 Using where
1 SIMPLE search_enquiries_found NULL eq_ref search_enquiry_id,item_id,search_enquiry_id_2,search_enquiry_id_relevance search_enquiry_id 8 const,em_entaonline.item.id 1 Using index
1 SIMPLE category_parent NULL ALL left_index NULL NULL NULL 337 Range checked for each record (index map: 0x2)
The important table is the hierachical category table:-
CREATE TABLE `category` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(128) NOT NULL,
`depth` int(11) NOT NULL,
`is_active` tinyint(1) NOT NULL DEFAULT '1',
`left_index` int(4) NOT NULL,
`right_index` int(4) NOT NULL,
`mmg_code` varchar(25) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `left_index` (`left_index`,`right_index`),
UNIQUE KEY `depth` (`depth`,`left_index`,`right_index`),
KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
This is not usefully using any index on the join to get parent categories.
Changing this query to remove most of the group by fields and just using the unique id (in effect the other fields in the group by are not required, but I tend to include them to keep to strict standards) reduces the time taken down to ~0.5 seconds:-
SELECT category_parent.id,
category_parent.depth,
category_parent.name AS item_sub_category,
category_parent.left_index,
category_parent.right_index,
COUNT(DISTINCT item.id) as total
FROM search_enquiries_found
INNER JOIN item ON search_enquiries_found.item_id = item.id
INNER JOIN category ON item.mmg_code = category.mmg_code
INNER JOIN category category_parent ON category.left_index BETWEEN category_parent.left_index AND category_parent.right_index
WHERE search_enquiries_found.search_enquiry_id = 35
AND item.cost_price > 0
GROUP BY category_parent.id
ORDER BY category_parent.left_index
The explain for this is very slightly different:-
id select_type table partitions type possible_keys key key_len ref rows Extra?
1 SIMPLE category NULL ALL left_index NULL NULL NULL 337 Using temporary; Using filesort
1 SIMPLE item NULL ref PRIMARY,id,mmg_code mmg_code 27 em_entaonline.category.mmg_code 43 Using where
1 SIMPLE search_enquiries_found NULL eq_ref search_enquiry_id,item_id,search_enquiry_id_2,search_enquiry_id_relevance search_enquiry_id 8 const,em_entaonline.item.id 1 Using index
1 SIMPLE category_parent NULL ALL PRIMARY,left_index,depth,name NULL NULL NULL 337 Using where; Using join buffer (Block Nested Loop)
Now I have 2 questions.
1 - Why does removing the fields from the GROUP BY (which are completely dependent on the primary key left in the group by) change the EXPLAIN this way, with such a large change in performance.
2 - In either case the performance is poor, with no key used for the join to the parent category table. Any suggestions on improving this?
As a further point, I have tried reversing the order of the joins and forcing it using STRAIGHT_JOIN. In this case this further improves the performance (~0.2 seconds), but this is an unusual situation (as it is returning all the records from the parent category table - most times the search will return far less records hence far less categories), so I think this will slow things down in more normal situations, and it still isn't using indexes on some of the joins. It would be good if MySQL could decide which way to join the tables depending on the result sets.
SELECT category_parent.id,
category_parent.depth,
category_parent.name AS item_sub_category,
category_parent.left_index,
category_parent.right_index,
COUNT(DISTINCT item.id) as total
FROM category category_parent
STRAIGHT_JOIN category ON category.left_index BETWEEN category_parent.left_index AND category_parent.right_index
STRAIGHT_JOIN item ON item.mmg_code = category.mmg_code
STRAIGHT_JOIN search_enquiries_found ON search_enquiries_found.item_id = item.id
WHERE search_enquiries_found.search_enquiry_id = 35
GROUP BY category_parent.id,
category_parent.depth,
item_sub_category,
category_parent.left_index,
category_parent.right_index
ORDER BY category_parent.left_index
id select_type table partitions type possible_keys key key_len ref rows Extra
1 SIMPLE category_parent NULL ALL left_index NULL NULL NULL 337 Using temporary; Using filesort
1 SIMPLE category NULL ALL left_index NULL NULL NULL 337 Range checked for each record (index map: 0x2)
1 SIMPLE item NULL ref PRIMARY,id,mmg_code mmg_code 27 em_entaonline.category.mmg_code 43 Using index
1 SIMPLE search_enquiries_found NULL eq_ref search_enquiry_id,item_id,search_enquiry_id_2,search_enquiry_id_relevance search_enquiry_id 8 const,em_entaonline.item.id 1 Using index
Our search is very slow on view. we can't define index on view.. Please help how we can improve this .. Below Query took 33.3993 sec.
SELECT
`v_cat_pro`.`product_id`, `v_cat_pro`.`msrp`,
FROM
`v_prod_cat` AS `v_cat_pro`
WHERE
(product_status="1" and msrp >0 AND (search_text = 'de') )
ORDER BY
`msrp` ASC LIMIT 50
Explain query result
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE cat_product index catproducts_product_id,category_product_index category_product_index 8 NULL 941343 Using index; Using temporary; Using filesort
1 SIMPLE dept eq_ref PRIMARY PRIMARY 4 newdhf.cat_product.category_id 1 Using where
1 SIMPLE team eq_ref PRIMARY PRIMARY 4 newdhf.dept.parent_id 1 Using where
1 SIMPLE league eq_ref PRIMARY PRIMARY 4 newdhf.team.parent_id 1 Using where
1 SIMPLE product eq_ref PRIMARY PRIMARY 4 newdhf.cat_product.product_id 1 Using where
CREATE
ALGORITHM=UNDEFINED VIEW v_prod_cat AS
select
dept.id AS dept_id,team.short_name AS shortteamname,team.url AS team_url,team.id AS team_id,league.id AS league_id,product.product_id AS product_id,product.product_status AS product_status,product.upload_image_l AS upload_image_l,dept.name AS department,team.name AS team,league.name AS league,product.title AS title,cat_product.product_url AS product_url,product.discount AS discount,product.discount_start_date AS discount_start_date,product.discount_end_date AS discount_end_date,product.extra_discount AS extra_discount,product.extra_discount_start_date AS extra_discount_start_date,product.extra_discount_end_date AS extra_discount_end_date,product.global_alt_tag AS global_alt_tag,product.msrp AS msrp,product.cost AS cost,product.vendor_id AS vendor_id,if((cat_product.is_default > 0),1,0) AS is_default,
concat(league.name,_utf8' ',team.name,_utf8' ',dept.name,_utf8' ',replace(replace(replace(replace(product.title,'$leaguename',league.name),'$teamname',team.name),'$shortteamname',team.short_name),'$departmentname',dept.name),' ',product.sku_code,_utf8' ',replace(replace(replace(replace(product.site_search_keyword,'$leaguename',league.name),'$teamname',team.name),'$shortteamname',team.short_name),'$departmentname',dept.name)) AS search_text
from
((((categories dept join categories team on(((team.id = dept.parent_id) and (team.category_type = _utf8'team')))) join categories league on(((league.id = team.parent_id) and (league.category_type = _utf8'league')))) join category_products cat_product on((cat_product.category_id = dept.id))) join products product on((product.product_id = cat_product.product_id))) where (dept.category_type = _utf8'department')
order by
dept.id desc;
If you add an index to your 'v_prod_cat' table on the columns that you are searching on then this should help speed up your view.
I've tried doing some searching, but I am having a bit of trouble getting a grasp on this.
Once I explain a query, how can I look at the information and determine the best place to add an index to speed up the query.
For example I have this query:
SELECT a.app_id, DATE_FORMAT(app_datetime, '%c/%d/%y %H:%i') as app_datetime, app_language
, if((select count(w.app_id) from li_app_cnc as w where w.app_id = a.app_id) > 0 , concat('CNC: ',(select cnc_note from li_app_cnc as w where w.app_id = a.app_id)),
if((select count(x.app_id) from li_app_dnc as x where x.app_id = a.app_id) > 0, concat('DNC: ', (select cancel_note from li_app_dnc as x where x.app_id = a.app_id)),
if((select count(y.app_id) from li_app_canceled as y where y.app_id = a.app_id) > 0, concat('Canceled: ', (select cancel_note from li_app_canceled as y where y.app_id = a.app_id)),
concat(h.emp_firstname, ' ', h.emp_lastname)))) as int_id, app_notes, app_facility, app_department
, app_requesting_person, app_service_provider
, cast(AES_DECRYPT(les_name, '$privatekey') as char) as les_name
, les_dob, cast(AES_DECRYPT(les_medicaid_id, '$privatekey') as char) as les_medicaid_id
, billing_total_time, billing_workorder_received, billing_admin_fee
, billing_notes, created_by, created_on, modified_by, modified_on, wo_entered_by
, t.cancel_code, t.cancel_note
, u.cnc_code, u.cnc_note
, v.cancel_code as dnc_code, v.cancel_note as dnc_note
FROM li_appointments.li_appointments as a
left Join orangehrm_li.hs_hr_employee as h on a.terp_id = h.employee_id
left Join li_appointments.li_app_canceled as t on t.app_id = a.app_id
left Join li_appointments.li_app_cnc as u on u.app_id = a.app_id
left Join li_appointments.li_app_dnc as v on v.app_id = a.app_id
where (app_client_id in (select account_number from li_appointments.li_client_access
where id = $userid) or created_by = '$username')
and date(app_datetime) = date(now())
and a.app_id not in (select f.app_id from li_app_dnc as f)
Which takes about 14 secconds
It is explained as:
1 PRIMARY a ALL 37539 Using where
1 PRIMARY h ALL 1036
1 PRIMARY t eq_ref PRIMARY PRIMARY 4 li_appointments.a.app_id 1
1 PRIMARY u eq_ref PRIMARY PRIMARY 4 li_appointments.a.app_id 1
1 PRIMARY v eq_ref PRIMARY PRIMARY 4 li_appointments.a.app_id 1
9 DEPENDENT SUBQUERY f unique_subquery PRIMARY PRIMARY 4 func 1 Using index
8 DEPENDENT SUBQUERY li_client_access ALL 72 Using where
7 DEPENDENT SUBQUERY y eq_ref PRIMARY PRIMARY 4 li_appointments.a.app_id 1
6 DEPENDENT SUBQUERY y eq_ref PRIMARY PRIMARY 4 li_appointments.a.app_id 1 Using index
5 DEPENDENT SUBQUERY x eq_ref PRIMARY PRIMARY 4 li_appointments.a.app_id 1
4 DEPENDENT SUBQUERY x eq_ref PRIMARY PRIMARY 4 li_appointments.a.app_id 1 Using index
3 DEPENDENT SUBQUERY w eq_ref PRIMARY PRIMARY 4 li_appointments.a.app_id 1
2 DEPENDENT SUBQUERY w eq_ref PRIMARY PRIMARY 4 li_appointments.a.app_id 1 Using index
Some thumb rules:
Index all the fields on which you will define WHERE conditions.
Index all the fields on which you will define relationships.
Index all the fields on which you will define grouping criteria.
Avoid indexing everything in your table. Think before creating the indexes.
(Although it depends on your specific needs, I personally avoid creating indexes on floating point columns)
Obviously, the above rules imply that you must index all the fields that work as primary key or as foreign key (if your tables are normalized, you already must have created the appropriate primary keys; if your tables are not normalized, then normalize them).
I'm having real difficulties optimising a MySQL query. I have to use the existing database structure, but I am getting an extremely slow response under certain circumstances.
My query is:
SELECT
`t`.*,
`p`.`trp_name`,
`p`.`trp_lname`,
`trv`.`trv_prosceslevel`,
`trv`.`trv_id`,
`v`.`visa_destcountry`,
`track`.`track_id`,
`track`.`track_datetoembassy`,
`track`.`track_expectedreturn`,
`track`.`track_status`,
`track`.`track_comments`
FROM
(SELECT
*
FROM
`_transactions`
WHERE
DATE(`tr_datecreated`) BETWEEN DATE('2011-07-01 00:00:00') AND DATE('2011-08-01 23:59:59')) `t`
JOIN
`_trpeople` `p` ON `t`.`tr_id` = `p`.`trp_trid` AND `p`.`trp_name` = 'Joe' AND `p`.`trp_lname` = 'Bloggs'
JOIN
`_trvisas` `trv` ON `t`.`tr_id` = `trv`.`trv_trid`
JOIN
`_visas` `v` ON `trv`.`trv_visaid` = `v`.`visa_code`
JOIN
`_trtracking` `track` ON `track`.`track_trid` = `t`.`tr_id` AND `p`.`trp_id` = `track`.`track_trpid` AND `trv`.`trv_id` = `track`.`track_trvid` AND `track`.`track_status` IN ('New','Missing_Info',
'En_Route',
'Ready_Pickup',
'Received',
'Awaiting_Voucher',
'Sent_Client',
'Closed')
ORDER BY `tr_id` DESC
The results of an explain statement on the above is:
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY <derived2> ALL NULL NULL NULL NULL 164 Using temporary; Using filesort
1 PRIMARY track ALL status_index NULL NULL NULL 4677 Using where
1 PRIMARY p eq_ref PRIMARY PRIMARY 4 db.track.track_trpid 1 Using where
1 PRIMARY trv eq_ref PRIMARY PRIMARY 4 db.track.track_trvid 1 Using where
1 PRIMARY v eq_ref visa_code visa_code 4 db.trv.trv_visaid 1
2 DERIVED _transactions ALL NULL NULL NULL NULL 4276 Using where
The query times are acceptable until the value of 'Closed' is included in the very last track.track_status IN clause. The length of time is then increased about 10 to 15 times the other queries.
This makes sense as the 'Closed' status refers to all the clients whose transactions have been dealt with, wihich corresponds to about 90% to 95% of the database.
The issue is, is that in some cases, the search is taking about 45 seconds which is rediculous. I'm sure MySQL can do much better than that and it's just my query at fault, even if the tables do have 4000 rows, but I can't work out how to optimise this statement.
I'd be grateful for some advice about where I'm going wrong and how I should be implementing this query to produce a faster result.
Many thanks
Try this:
SELECT t.*,
p.trp_name,
p.trp_lname,
trv.trv_prosceslevel,
trv.trv_id,
v.visa_destcountry,
track.track_id,
track.track_datetoembassy,
track.track_expectedreturn,
track.track_status,
track.track_comments
FROM
_transactions t
JOIN _trpeople p ON t.tr_id = p.trp_trid
JOIN _trvisas trv ON t.tr_id = trv.trv_trid
JOIN _visas v ON trv.trv_visaid = v.visa_code
JOIN _trtracking track ON track.track_trid = t.tr_id
AND p.trp_id = track.track_trpid
AND trv.trv_id = track.track_trvid
WHERE DATE(t.tr_datecreated)
BETWEEN DATE('2011-07-01 00:00:00') AND DATE('2011-08-01 23:59:59')
AND track.track_status IN ('New','Missing_Info','En_Route','Ready_Pickup','Received','Awaiting_Voucher','Sent_Client', 'Closed')
AND p.trp_name = 'Joe' AND p.trp_lname = 'Bloggs'
ORDER BY tr_id DESC