How to get rid of OR condition in inner join [duplicate] - mysql

This question already has answers here:
How to handle multiple joins
(3 answers)
Closed 6 years ago.
I have a complex query which requires fields from a total of 4 tables. I have one inner join statement which has an OR clause, and this is slowing the query down drastically.
This is my query:
SELECT
pending_corrections.sightinguid AS 'pending_corrections_sightinguid',
vehicle_ownership.id AS 'fk_vehicle_owner',
#bill_id AS 'fk_bills',
#nullValue AS 'fk_final_sightings_sightinguid',
TRIM(pending_corrections.corrected_plate) AS 'vrn',
pending_corrections.seenDate AS 'seen_date',
cameras.in_out AS 'in_out',
vehicle_vrn.fk_sysno AS 'fk_sysno',
cameras.zone AS 'fk_zones',
'0' AS 'auto_generated'
FROM
(pending_corrections
INNER JOIN cameras ON pending_corrections.camerauid = cameras.camera_id)
INNER JOIN
vehicle_vrn ON (pending_corrections.corrected_plate = vehicle_vrn.vrn500
OR pending_corrections.corrected_plate = vehicle_vrn.vrnno)
INNER JOIN
vehicle_ownership ON vehicle_vrn.fk_sysno = vehicle_ownership.fk_sysno
WHERE
pending_corrections.corrected_plate <> ''
AND pending_corrections.corrected_plate IS NOT NULL
AND pending_corrections.unable_to_correct <> '1'
AND pending_corrections.seenDate >= #dateFrom
AND pending_corrections.seenDate <= #dateTo
AND (cameras.in_out = 1 OR cameras.in_out = 0)
AND cameras.zone IN (SELECT
zone_number
FROM
zones
WHERE
fk_site = #siteId)
AND seenDate >= vehicle_vrn.vrn_start_date
AND (seenDate <= vehicle_vrn.vrn_end_date
OR vehicle_vrn.vrn_end_date IS NULL
OR vehicle_vrn.vrn_end_date = '0001-01-01 00:00:00')
AND seenDate >= vehicle_ownership.ownership_start_date
AND (seenDate <= vehicle_ownership.ownership_end_date
OR vehicle_ownership.ownership_end_date IS NULL
OR vehicle_ownership.ownership_end_date = '0001-01-01 00:00:00')
ORDER BY pending_corrections.corrected_plate , pending_corrections.seenDate ASC;
How can I achieve the same effect but without the OR in one of the joins? The reason for the OR clause is because the pending_corrections.corrected_plate value has to match either the vrn500 or vrnno columns in the vehicle_vrn table.

Instead of using two equals expressions with an OR, you could use a IN expression such as:
FROM
(pending_corrections
INNER JOIN cameras ON pending_corrections.camerauid = cameras.camera_id)
INNER JOIN
vehicle_vrn ON pending_corrections.corrected_plate IN(vehicle_vrn.vrn500, vehicle_vrn.vrnno)
INNER JOIN
vehicle_ownership ON vehicle_vrn.fk_sysno = vehicle_ownership.fk_sysno

You can replace the first OR using IN(), as Scott mentioned
pending_corrections.corrected_plate IN (vehicle_vrn.vrn500, vehicle_vrn.vrnno)
If you can UPDATE the vrn_end_date and ownership_end_date columns from '0001-01-01 00:00:00' to NULL, the other OR conditions can be simplified to
AND (seenDate <= IFNULL(vehicle_vrn.vrn_end_date, seenDate))

What's the execution plan look like? SQL probably can't optimize this join into a hash or merge, so is just having to do table scans.
Sometimes with ORs, a UNION works well. It's more code but can run faster because SQL can optimize them better.
; WITH PendingCorrectionsCte AS
(
SELECT pc.corrected_plate,
pc.seen_date,
c.in_out,
pc.sightinguid AS 'pending_corrections_sightinguid',
FROM pending_corrections pc
INNER JOIN cameras c ON pending_corrections.camerauid = cameras.camera_id
WHERE pc.seenDate BETWEEN '2015-01-01 00:00:00' AND '2015-01-31 23:59:59'
AND NULLIF(LTRIM(pc.corrected_plate), '') IS NOT NULL
AND pending_corrections.unable_to_correct <> '1'
AND pending_corrections.seenDate >= #dateFrom
AND pending_corrections.seenDate <= #dateTo
AND c.in_out IN (0, 1)
AND c.zone IN (SELECT zone_number FROM zones WHERE fk_site = #siteId)
),
VrnCte AS
(
SELECT pc.corrected_plate,
pc.seen_date,
vrn.fk_sysno,
pc.in_out,
pc.pending_corrections_sightinguid
FROM PendingCorrectionsCte pc
INNER JOIN vehicle_vrn vrn ON pc.corrected_plate = vehicle_vrn.vrn500
-- Could also do this inline in the where clause, but chaining isnull and nullif could get hard to read
CROSS APPLY (SELECT NULLIF(vrn.vrn_end_date, '0001-01-01 00:00:00') AS value) vrn_end_date
WHERE pc.seenDate BETWEEN vrn.vrn_start_date AND ISNULL(vrn_end_date.value, seenDate)
UNION
SELECT pc.corrected_plate,
pc.seenDate,
vrn.fk_sysno,
pc.in_out,
pc.pending_corrections_sightinguid
FROM pending_corrections pc
INNER JOIN vehicle_vrn vrn ON pc.corrected_plate = vehicle_vrn.vrnno
-- Could also do this inline in the where clause, but chaining isnull and nullif could get hard to read
CROSS APPLY (SELECT NULLIF(vrn.vrn_end_date, '0001-01-01 00:00:00') AS value) vrn_end_date
WHERE pc.seenDate BETWEEN vrn.vrn_start_date AND ISNULL(vrn_end_date.value, seenDate)
)
SELECT pending_corrections.sightinguid AS 'pending_corrections_sightinguid',
vo.id AS 'fk_vehicle_owner',
#bill_id AS 'fk_bills',
#nullValue AS 'fk_final_sightings_sightinguid',
TRIM(VrnCte.corrected_plate) AS 'vrn',
VrnCte.seenDate AS 'seen_date',
VrnCte.in_out AS 'in_out',
VrnCte.fk_sysno AS 'fk_sysno',
VrnCte.pending_corrections_sightinguid
'0' AS 'auto_generated'
FROM VrnCte
INNER JOIN vehicle_ownership vo ON VrnCte.fk_sysno = vo.fk_sysno
CROSS APPLY (SELECT NULLIF(vo.ownership_end_date, '0001-01-01 00:00:00') AS value) ownership_end_date
WHERE VrnCte.seenDate BETWEEN vehicle_ownership.ownership_start_date AND ISNULL(ownership_end_date.value, seenDate)
ORDER BY
VrnCte.corrected_plate,
VrnCte.seenDate ASC

Related

SQL calculate new column based on other queries with inner join

I'm building up a large SQL query to calculate everything in one go based on a table. I need a column called total_revenue, which should be the sum of total_commission and total_profit together.
Right now, I'm constructing a new inner join to do this, but this inner join that calculates total_revenue is an overhead.
How can I, instead, run a inner join on the result of my upper query to then do a sum of total_commission + total_profit without doing another inner join on another table, here's my current SQL:
SELECT
# Affilaite
Conversion.aff_id,
# Revenue
JoinedRevenue.total_revenue,
# Commission
JoinedCommission.total_commission,
# Profit
JoinedProfit.total_profit
FROM
tlp_conversions AS Conversion
INNER JOIN
(
SELECT
Commission.aff_id,
Commission.created,
SUM(Commission.amount) AS total_commission
FROM
tlp_commissions AS Commission
WHERE
Commission.created >= '2022-10-03 00:00:00'
AND
Commission.created <= '2022-10-03 23:59:59'
GROUP BY
Commission.aff_id
) AS JoinedCommission
ON JoinedCommission.aff_id = Conversion.aff_id
INNER JOIN
(
SELECT
Application.tlp_aff_id,
ApplicationResponse.application_id,
ApplicationResponse.result,
Commission.seller_code,
Commission.application_response_id,
Commission.created,
SUM(Commission.amount) AS total_profit
FROM
tlp_commissions AS Commission
INNER JOIN tlp_application_responses AS ApplicationResponse
ON ApplicationResponse.id = Commission.application_response_id
INNER JOIN tlp_applications AS Application
ON Application.id = ApplicationResponse.application_id
WHERE
Commission.created >= '2022-10-03 00:00:00'
AND
Commission.created <= '2022-10-03 23:59:59'
AND
ApplicationResponse.result = 'Accepted'
AND
Commission.seller_code = 44
GROUP BY
Application.tlp_aff_id
) AS JoinedProfit
ON JoinedProfit.tlp_aff_id = Conversion.aff_id
INNER JOIN
(
SELECT
Application.tlp_aff_id,
ApplicationResponse.application_id,
Commission.application_response_id,
Commission.created,
SUM(Commission.amount) AS total_revenue
FROM
tlp_commissions AS Commission
INNER JOIN tlp_application_responses AS ApplicationResponse
ON ApplicationResponse.id = Commission.application_response_id
INNER JOIN tlp_applications AS Application
ON Application.id = ApplicationResponse.application_id
WHERE
Commission.created >= '2022-10-03 00:00:00'
AND
Commission.created <= '2022-10-03 23:59:59'
GROUP BY
Application.tlp_aff_id
) AS JoinedRevenue
ON JoinedRevenue.tlp_aff_id = Conversion.aff_id
WHERE
Conversion.conversion_time >= '2022-10-03 00:00:00'
AND
Conversion.conversion_time <= '2022-10-03 23:59:59'
AND
Conversion.aff_id IS NOT NULL
GROUP BY
Conversion.aff_id
I was hoping I could just do another SQL join that loops over each returned result in my query and appends total_revenue based on the row column of each?
INNER JOIN
(
SELECT SUM(JoinedProfit.total_profit + JoinedCommission.total_commission) AS total_revenue
) AS JoinedRevenue
But this isn't the right syntax here.

Is there a way to optimize this query

I have written a query but it's taking a lot of time. I want to know if there exists any solution to optimize it without making a temp table in MYSQL. Is there a way to optimize the subquery part since AccessLog2019 is huge so it's taking forever)
Here is my query
SELECT distinct l.ListingID,l.City,l.ListingStatus,l.Price,l.Bedrooms,l.FullBathrooms, gc.Latitude,gc.Longitude , count(distinct s.AccessLogID) AS access_count, s.LBID , lb.CurrentListingID
from lockbox.Listings l
JOIN lockbox.GeoCoordinates gc ON l.ListingID = gc.ID
LEFT JOIN lockbox.LockBox lb ON l.ListingID = lb.CurrentListingID
LEFT JOIN
(SELECT * FROM lockbox.AccessLog2019 ac where ac.AccessType not in('1DayCodeGen','BluCodeGen','SmartMACGen') AND DATEDIFF(NOW(), ac.UTCAccessedDT ) < 1 ) s
ON lb.LBID = s.LBID
WHERE l.AssocID = 'AS00000000CC' AND (gc.Confidence <> '5 - Unmatchable' OR gc.Confidence IS NULL OR gc.Confidence = ' ')
group BY l.ListingID
Thanks
If you can avoid the outer group by, that is a big win. I am thinking:
SELECT l.ListingID, l.City, l.ListingStatus, l.Price, l.Bedrooms, l.FullBathrooms,
gc.Latitude, gc.Longitude,
(select count(*)
from lockbox.LockBox lb join
lockbox.AccessLog2019 ac
on lb.LBID = ac.LBID
where l.ListingID = lb.CurrentListingID and
ac.AccessType not in ('1DayCodeGen', 'BluCodeGen', 'SmartMACGen') and
DATEDIFF(NOW(), ac.UTCAccessedDT) < 1
) as cnt
from lockbox.Listings l JOIN
lockbox.GeoCoordinates gc
ON l.ListingID = gc.ID
WHERE l.AssocID = 'AS00000000CC' AND
(gc.Confidence <> '5 - Unmatchable' OR
gc.Confidence IS NULL OR
gc.Confidence = ' '
)
Note: This does not select s.LBID or lb.CurrentListingID because these don't make sense in your query. If I understand correctly, these could have different values on different rows.
You could try breaking out the subquery to the JOIN clause.
It might give a hint to the optimizer that it can use the LBID field first, and then test the AccessType later (in case the optimizer doesn't figure that out when you have the sub-select).
SELECT distinct l.ListingID,l.City,l.ListingStatus,l.Price,l.Bedrooms,l.FullBathrooms, gc.Latitude,gc.Longitude , count(distinct s.AccessLogID) AS access_count, s.LBID , lb.CurrentListingID
from lockbox.Listings l
JOIN lockbox.GeoCoordinates gc ON l.ListingID = gc.ID
LEFT JOIN lockbox.LockBox lb ON l.ListingID = lb.CurrentListingID
LEFT JOIN AccessLog2019 s
ON lb.LBID = s.LBID
AND s.AccessType not in('1DayCodeGen','BluCodeGen','SmartMACGen')
AND DATEDIFF(NOW(), s.UTCAccessedDT ) < 1
WHERE l.AssocID = 'AS00000000CC' AND (gc.Confidence <> '5 - Unmatchable' OR gc.Confidence IS NULL OR gc.Confidence = ' ')
group BY l.ListingID
Note that this is one of those cases where conditions in the JOIN clause gives different behavior than using a WHERE clause. If you just had lb.LBID = s.LBID and then had the conditions I wrote in the WHERE of the outer query the results would be different. They would exclude the records matching lb.LBID = s.LBID. But in the JOIN clause, it is part of the conditions of the outer join.
SELECT * --> Select only the columns needed.
SELECT DISTINCT ... GROUP BY -- Do one or the other, not both.
Need composite INDEX(AssocID, ListingID) (in that order)
DATEDIFF(NOW(), ac.UTCAccessedDT ) < 1 --> ac.UTCAccessedDT > NOW() - INTERVAL 1 DAY (or whatever your intent was. Then add INDEX(UTCAccessedDT)
OR is hard to optimize; consider cleansing the data so that Confidence does not have 3 values that mean the same thing.

MySQL LEFT JOIN ignores AND criteria

I have a functional LEFT JOIN MySQL query structured like this:
SELECT
COUNT(HTG_ScheduleRequest.ID) AS current_job,
HTG_TechProps.EmpNumber,
HTG_TechProps.EmpFirstName,
HTG_TechProps.EmpLastName,
HTG_TechProps.Veh_Number
FROM HTG_TechProps
LEFT JOIN HTG_ScheduleRequest ON HTG_TechProps.EmpNumber = HTG_ScheduleRequest.SSR
AND (HTG_ScheduleRequest.ScheduleDateCurrent = CURDATE() || HTG_ScheduleRequest.ScheduleDateExact = CURDATE())
AND RecordType = '1'
AND HTG_ScheduleRequest.JobStatus IN (2,5,8,3,4,7)
GROUP BY HTG_TechProps.EmpNumber ORDER BY HTG_TechProps.EmpNumber ASC
I need to add some criteria to the initial SELECT table like this:
HTG_TechProps.EmpStatus='A'
I get a Syntax error when I add a WHERE statement prior to the LEFT JOIN and when I add an AND like this after the LEFT JOIN it is ignored an still returning records that are not equal to A.
LEFT JOIN HTG_ScheduleRequest ON HTG_TechProps.EmpNumber = HTG_ScheduleRequest.SSR
AND HTG_TechProps.EmpStatus='A'
Conditions on the first table in a LEFT JOIN should go in a WHERE clause:
SELECT COUNT(sr.ID) AS current_job,
tp.EmpNumber, tp.EmpFirstName, tp.EmpLastName, tp.Veh_Number
FROM HTG_TechProps tp LEFT JOIN
HTG_ScheduleRequest sr
ON tp.EmpNumber = sr.SSR AND
(sr.ScheduleDateCurrent = CURDATE() OR sr.ScheduleDateExact = CURDATE()
) AND
sr.RecordType = '1' AND -- assume this comes from SR
sr.JobStatus IN (2, 5, 8, 3, 4, 7)
WHERE tp.EmpStatus='A'
GROUP BY tp.EmpNumber -- this is okay assuming that it is unique or (equivalently) a primary key
ORDER BY tp.EmpNumber ASC;
Note that this introduces table aliases so the query is easier to write and to read.
You should Use OR
SELECT
COUNT(HTG_ScheduleRequest.ID) AS current_job,
HTG_TechProps.EmpNumber,
HTG_TechProps.EmpFirstName,
HTG_TechProps.EmpLastName,
HTG_TechProps.Veh_Number
FROM HTG_TechProps
LEFT JOIN HTG_ScheduleRequest ON HTG_TechProps.EmpNumber = HTG_ScheduleRequest.SSR
AND (HTG_ScheduleRequest.ScheduleDateCurrent = CURDATE() OR HTG_ScheduleRequest.ScheduleDateExact = CURDATE())
AND RecordType = '1'
AND HTG_ScheduleRequest.JobStatus IN (2,5,8,3,4,7)
GROUP BY HTG_TechProps.EmpNumber
ORDER BY HTG_TechProps.EmpNumber ASC
and if you want apply where condtion for main table's columns you could use where eg:
SELECT
COUNT(HTG_ScheduleRequest.ID) AS current_job,
HTG_TechProps.EmpNumber,
HTG_TechProps.EmpFirstName,
HTG_TechProps.EmpLastName,
HTG_TechProps.Veh_Number
FROM HTG_TechProps
LEFT JOIN HTG_ScheduleRequest ON HTG_TechProps.EmpNumber = HTG_ScheduleRequest.SSR
AND HTG_ScheduleRequest.JobStatus IN (2,5,8,3,4,7)
WHERE (HTG_ScheduleRequest.ScheduleDateCurrent = CURDATE() OR HTG_ScheduleRequest.ScheduleDateExact = CURDATE())
AND HTG_TechProps.RecordType = '1'
GROUP BY HTG_TechProps.EmpNumber
ORDER BY HTG_TechProps.EmpNumber ASC

Query WHERE NOT IN (SELECT) INNER JOIN

SELECT
SUM(m_out) AS totalOut
FROM
m_detal
WHERE
opers = '25'
AND (m_type = 'Out'
OR m_type = 'Merged')
AND m_date <= '2018-11-28 07:30:00'
AND mark_delete IS NULL
AND m_ids NOT IN (SELECT
m.m_ids
FROM
(SELECT
m_ids
FROM
m_detal
WHERE
opers = '25'
AND (m_type = 'Out'
OR m_type = 'Merged')
AND (m_onhold != 'onhold'
OR m_onhold IS NULL)
AND mark_delete IS NULL
AND m_date <= '2018-11-28 07:30:00') AS m
INNER JOIN
n_combine_tbl AS t ON (t.comb_id1 = m.m_ids
OR t.comb_id2 = m.m_ids)
AND t.time <= '2018-11-28 07:30:00');
This query took me more than 30sec or more! The query inside the NOT IN is little bit huge around 7-9k of ids. Is there a more efficient way of doing this? I think the inner join part is make it slow where the checking the two column of n_combine_tables which is (comb_id1 or comb_id2).
Is there a more efficient way of doing this?
I'd remove duplicated conditions in sub-queries and do something like this (two NOT in could be replaced with UNION):
SELECT
SUM(m.m_out) AS totalOut
FROM
m_detal AS m
WHERE
m.opers = '25'
AND m.m_type IN ('Out', 'Merged')
AND m.m_date <= '2018-11-28 07:30:00'
AND m.mark_delete IS NULL
AND m.m_onhold = 'onhold'
AND m.m_ids NOT IN (
SELECT
t1.comb_id1
FROM
n_combine_tbl AS t1
WHERE
t1.time <= '2018-11-28 07:30:00'
)
AND m.m_ids NOT IN (
SELECT
t2.comb_id2
FROM
n_combine_tbl AS t2
WHERE
t2.time <= '2018-11-28 07:30:00'
)
;
OR with NOT EXISTS:
SELECT
SUM(m.m_out) AS totalOut
FROM
m_detal AS m
WHERE
m.opers = '25'
AND m.m_type IN ('Out', 'Merged')
AND m.m_date <= '2018-11-28 07:30:00'
AND m.mark_delete IS NULL
AND m.m_onhold = 'onhold'
AND NOT EXISTS (
SELECT
*
FROM
n_combine_tbl AS t
WHERE
t.time <= '2018-11-28 07:30:00'
AND (
t.comb_id1 = m.m_ids
OR t.comb_id2 = m.m_ids
)
)
;
You should check the 'onhold' logic.
You should try to add indexes on n_combine_tbl: (time, comb_id1), (time, comb_id2), (comb_id1, time), (comb_id2, time) and check what is good for your data.
Multi-column indexes on m_detal table should also be considered.

MySQL 500 million rows table in select query with join

I'm concerned about the performance of the query below once the tables are fully populated. So far it's under development and performs well with dummy data.
The table "adress_zoo" will contain about 500 million records once fully populated. "adress_zoo" table looks like this:
CREATE TABLE `adress_zoo`
( `adress_id` int(11) NOT NULL, `zoo_id` int(11) NOT NULL,
UNIQUE KEY `pk` (`adress_id`,`zoo_id`),
KEY `adress_id` (`adress_id`) )
ENGINE=InnoDB DEFAULT CHARSET=latin1;
The other tables will contain maximum 500 records each.
The full query looks like this:
SELECT a.* FROM jos_zoo_item AS a
JOIN jos_zoo_search_index AS zsi2 ON zsi2.item_id = a.id
WHERE a.id IN (
SELECT r.id FROM (
SELECT zi.id AS id, Max(zi.priority) as prio
FROM jos_zoo_item AS zi
JOIN jos_zoo_search_index AS zsi ON zsi.item_id = zi.id
LEFT JOIN jos_zoo_tag AS zt ON zt.item_id = zi.id
JOIN jos_zoo_category_item AS zci ON zci.item_id = zi.id
**JOIN adress_zoo AS az ON az.zoo_id = zi.id**
WHERE 1=1
AND ( (zci.category_id != 0 AND ( zt.name != 'prolong' OR zt.name is NULL))
OR (zci.category_id = 0 AND zt.name = 'prolong') )
AND zi.type = 'telefoni'
AND zsi.element_id = '44d3b1fd-40f6-4fd7-9444-7e11643e2cef'
AND zsi.value = 'Small'
AND zci.category_id > 15
**AND az.adress_id = 5**
GROUP BY zci.category_id ) AS r
)
AND a.application_id = 6
AND a.access IN (1,1)
AND a.state = 1
AND (a.publish_up = '0000-00-00 00:00:00' OR a.publish_up <= '2012-06-07 07:51:26')
AND (a.publish_down = '0000-00-00 00:00:00' OR a.publish_down >= '2012-06-07 07:51:26')
AND zsi2.element_id = '1c3cd26e-666d-4f8f-a465-b74fffb4cb14'
GROUP BY a.id
ORDER BY zsi2.value ASC
The query will usually return about 25 records.
Based on your experience, will this query perform acceptable (respond within say 3 seconds)?
What can I do to optimise this?
As adviced by #Jack I ran the query with EXPLAIN and got this:
This part is an important limiter:
az.adress_id = 5
MySQL will limit the table to only those records where adress_id matches before joining it with the rest of the statement, so it will depend on how big you think that result set might be.
Btw, you have a UNIQUE(adress_id, zoo_id) and a separate INDEX. Is there a particular reason? Because the first part of a spanning key can be used by MySQL to select with as well.
What's also important is to use EXPLAIN to understand how MySQL will "attack" your query and return the results. See also: http://dev.mysql.com/doc/refman/5.5/en/execution-plan-information.html
To avoid subquery you can try to rewrite your query as:
SELECT a.* FROM jos_zoo_item AS a
JOIN jos_zoo_search_index AS zsi2 ON zsi2.item_id = a.id
INNER JOIN
(
SELECT ** distinct ** r.id FROM (
SELECT zi.id AS id, Max(zi.priority) as prio
FROM jos_zoo_item AS zi
JOIN jos_zoo_search_index AS zsi ON zsi.item_id = zi.id
LEFT JOIN jos_zoo_tag AS zt ON zt.item_id = zi.id
JOIN jos_zoo_category_item AS zci ON zci.item_id = zi.id
**JOIN adress_zoo AS az ON az.zoo_id = zi.id**
WHERE 1=1
AND ( (zci.category_id != 0 AND ( zt.name != 'prolong' OR zt.name is NULL))
OR (zci.category_id = 0 AND zt.name = 'prolong') )
AND zi.type = 'telefoni'
AND zsi.element_id = '44d3b1fd-40f6-4fd7-9444-7e11643e2cef'
AND zsi.value = 'Small'
AND zci.category_id > 15
**AND az.adress_id = 5**
GROUP BY zci.category_id ) AS r
) T
on a.id = T.id
where
AND a.application_id = 6
AND a.access IN (1,1)
AND a.state = 1
AND (a.publish_up = '0000-00-00 00:00:00' OR a.publish_up <= '2012-06-07 07:51:26')
AND (a.publish_down = '0000-00-00 00:00:00' OR a.publish_down >= '2012-06-07 07:51:26')
AND zsi2.element_id = '1c3cd26e-666d-4f8f-a465-b74fffb4cb14'
GROUP BY a.id
ORDER BY zsi2.value ASC
This approach don't perform subquery for each candidate row. Performance may be increased only if T is calculated in few milliseconds.