Unable to limit - mysql

I have the following query:
UPDATE pc
INNER JOIN cams
ON cams.cam_id = pc.camuid
SET
timestamp = NOW(),
uid = #out_param:=uid
WHERE zone = 1
AND (unable = '0' OR unable IS NULL)
AND (corrected_plate = '' OR corrected_plate IS NULL)
AND (timestamp IS NULL OR timestamp < (NOW() - INTERVAL #interval MINUTE))
LIMIT 1;
SELECT #out_param;
I am unable to run that query as I have a LIMITclause in a join query. I cannot figure out how to spread it out into a subquery while maintaining performance. I need this query to be as fast as possible, and the optimiser of MySQL has not provided much help so far.
This is the error obtained from MySQL, as the above is not allowed: Incorrect usage of UPDATE and LIMIT

Just use a subquery to decide which uid update.
UPDATE PC
SET timestamp = NOW(),
uid = #out_param:=uid
WHERE uid = (SELECT uid
FROM ( SELECT * FROM pc ) as pc2
INNER JOIN cams
ON cams.cam_id = pc2.camuid
WHERE zone = 1
AND (unable = '0' OR unable IS NULL)
AND (corrected_plate = '' OR corrected_plate IS NULL)
AND (timestamp IS NULL OR timestamp < (NOW() - INTERVAL #interval MINUTE))
ORDER BY uid -- optional
LIMIT 1)

Related

MySQL Union two queries of same table

I have a problem. I created these 2 queries to get the start and end value:
Start value:
SELECT IF(`Order`.action = "Buy", `Order`.transMarketGross, `Order`.transMarketNet) AS startValue
FROM `Order`
WHERE agentId = (SELECT id FROM Agent WHERE owner = "Alexander") AND
`Order`.dateTimeExecuted <= DATE_SUB(curdate(), INTERVAL 1 MONTH)
ORDER BY `Order`.dateTimeExecuted DESC
LIMIT 1;
End value:
SELECT IF(`Order`.action = "Buy", `Order`.transMarketGross, `Order`.transMarketNet) AS endValue
FROM `Order`
WHERE agentId = ( SELECT id
FROM Agent
WHERE owner = "Alexander")
ORDER BY `Order`.dateTimeExecuted DESC LIMIT 1;
But now I want the start and end value in one result row, so I thought I could add UNION between the 2 queries:
SELECT IF(`Order`.action = "Buy", `Order`.transMarketGross, `Order`.transMarketNet) AS startValue
FROM `Order`
WHERE agentId = ( SELECT id
FROM Agent
WHERE owner = "Alexander") AND `Order`.dateTimeExecuted <= DATE_SUB(curdate(), INTERVAL 1 MONTH)
ORDER BY `Order`.dateTimeExecuted DESC LIMIT 1
UNION
SELECT IF(`Order`.action = "Buy", `Order`.transMarketGross, `Order`.transMarketNet) AS endValue
FROM `Order` WHERE agentId = ( SELECT id
FROM Agent
WHERE owner = "Alexander")
ORDER BY `Order`.dateTimeExecuted DESC LIMIT 1
Using these queries seperately, they do their job, but I get an error on the total query that this query is not valid. Can someone tell me what I am doing wrong and how I can fix this? Also if there are any improvements to simplify the query, let me know!
If you want one row, you might as well use two subqueries:
SELECT (SELECT (CASE WHEN o.action = 'Buy', o.transMarketGross, o.transMarketNet) AS startValue
FROM `Order` o JOIN
Agent a
ON o.agentId = a.id
WHERE a.owner = 'Alexander' AND
o.dateTimeExecuted <= DATE_SUB(curdate(), INTERVAL 1 MONTH)
ORDER BY o.dateTimeExecuted DESC
LIMIT 1
),
(SELECT (CASE WHEN o.action = 'Buy', o.transMarketGross, o.transMarketNet) AS startValue
FROM `Order` o JOIN
Agent a
ON o.agentId = a.id
WHERE a.owner = 'Alexander'
ORDER BY o.dateTimeExecuted DESC
LIMIT 1
);
Note that I made the following changes:
Added table aliases so the query is easier to write and read.
Qualified all column references, so the query is unambiguous.
Replaced the in with JOIN, which seems to be the intention.
Replaced double quotes with the SQL standard single quotes for string delimiters.
Use CASE (the standard) rather than the bespoke IF() for conditional logic.

How to decrease Query time of this Query with InnoDB engine

I am trying to optimize this query. Now it takes 28 seconds.
AS used to be missing in my query. After adding, query time dropped 20%
SELECT
g.id,
g.adresid,
g.senaryoid,
g.olayid,
g.gonderilecegitarih
FROM
(
SELECT
adresid
FROM
expose2.800_emsenaryolar_emgidenbulten
WHERE
olayid = '3320'
) AS s
RIGHT JOIN expose2.800_emsenaryolar_emgidenbulten AS g ON s.adresid = g.adresid
WHERE
s.adresid IS NULL
AND g.olayid = '2784'
AND g.durum = '1'
AND g.gonderilecegitarih < DATE_SUB(
'2015-05-13 15:40:15',
INTERVAL 1 DAY
)
If you use s.adresid IS NULL condition in subquery it will join faster then more rows ...
SELECT
g.id,
g.adresid,
g.senaryoid,
g.olayid,
g.gonderilecegitarih
FROM (
SELECT adresid FROM expose2.800_emsenaryolar_emgidenbulten WHERE olayid = '3320' and s.adresid IS NULL
) AS s
RIGHT JOIN expose2.800_emsenaryolar_emgidenbulten AS g ON s.adresid = g.adresid
AND g.olayid = '2784'
AND g.durum = '1'
AND g.gonderilecegitarih < DATE_SUB(
'2015-05-13 15:40:15',
INTERVAL 1 DAY
)
still this query optimized using self join.
For added speed, add this composite index to g:
INDEX(olayid, durum, gonderilecegitarih)
Please provide SHOW CREATE TABLE 800_emsenaryolar_emgidenbulten; I want to verify that you also have an index on adresid.

If date = '' set date temporaraly to sysdate

I have data with begin and end date, sometimes the end date had a null and is now
'0000-00-00'
I have a query that says:
UPDATE w_preletter as L INNER JOIN w_prov_sensitive AS P ON L.IRS_TAX_ID = P.TAX_ID
set L.sensitive_provider = '1', L.OFFSET_RESTRICTED = P.OFFSET_RESTRICTED
where L.DATE_OF_SERVICE_BEG between P.BEGIN_DATE AND P.END_DATE;
but if the date is '0000-00-00' it means it is still effective so I need it to temporarily consider it the sysdate , otherwise it will not consider the date being in between. what would the syntax be for that?
I don't know if its a case statement and if so how I would write it out.
try this
UPDATE w_preletter as L INNER JOIN w_prov_sensitive AS P ON L.IRS_TAX_ID = P.TAX_ID
set L.sensitive_provider = '1', L.OFFSET_RESTRICTED = P.OFFSET_RESTRICTED
where L.DATE_OF_SERVICE_BEG between P.BEGIN_DATE AND P.END_DATE
or (L.DATE_OF_SERVICE_BEG >= P.BEGIN_DATE AND P.END_DATE = '0000-00-00')
Create a temporary table containing all the rows in w_prov_sensitive (CREATE TABLE ... SELECT syntax)
update the temporary table setting all END_DATE's to NOW() if they are 0/NULL/whatever
Do the UPDATE by joining against the temporary table
You could also put boolean logic to work and use a convoluted WHERE clause.
where
( P.END_DATE != 0 and L.DATE_OF_SERVICE_BEG between P.BEGIN_DATE AND P.END_DATE )
OR ( P.END_DATE = 0 and L.DATE_OF_SERVICE_BEG between P.BEGIN_DATE AND NOW() )

Select value in mysql but check another database at clause WHERE

I'm trying to select values from a database, but I need to check another value in another database .
I created this code, but only get 1 result and I don't know why:
SELECT `id` FROM `mc_region`
WHERE `is_subregion` = 'false'
AND lastseen < CURDATE() - INTERVAL 20 DAY
AND (SELECT id_region FROM mc_region_flags
WHERE flag <> 'expire'
AND id_region = mc_region.id
)
LIMIT 0, 30
What I've made wrong?
#Edit
I think I know why this code is not working. At database mc_region_flags not all records from the primary database has flag.
I would like to do the following:
1º Select all records on the first database, where is not subregion and lastseen is more than 20 day
2º Check if any result on the 1st database has flag 'expire', if yes, they are not included in the result.
I cant do this in 1 only SQL Code?
#Edit2
I created this code that simulate FULL JOIN but seems that WHERE is not work
SELECT *
FROM mc_region AS r RIGHT OUTER JOIN
mc_region_flags AS f ON r.id = f.id_region
UNION ALL
SELECT * from
mc_region AS r LEFT OUTER JOIN
mc_region_flags AS f
ON r.id = f.id_region
WHERE r.is_subregion = 'false'
AND f.flag = 'exipre'
AND r.lastseen < CURDATE() - INTERVAL 20 DAY
Problems WHERE not work
f.flag is not 'expire'
f.lastseen is not > 20 days
UPDATED
SELECT *
FROM `mc_region` AS r LEFT JOIN
`mc_region_flags` AS f ON r.`id` = f.`id_region`
WHERE r.`is_subregion` = 'false' AND
r.`lastseen` < CURDATE() - INTERVAL 20 DAY AND
COALESCE(f.`flag`, '-') <> 'expire'
LIMIT 0, 30;
Before the inner nested select add :
id in (select...)

How can optimize these instruction with MYSQL (subqueries for each row)?

My problem is that we make a select, and then, for each row, we run 4 differents request SQL (is madness), as you can guess we make a lot of requests, and the system using this is very slow.
SELECT
deal_source.id,
deal_source.source_name,
deal_source.spider_status,
spider.last_success_date
FROM deal_source
JOIN spider
ON deal_source.id = spider.deal_source_id
Then for each row of this query we make:
$total_query = "SELECT count(id) as total
FROM spider_log
WHERE deal_source_id = '$deal_source_id'
AND date_format(date_created, '%Y-%m-%d') = '$lastdate' ";
$added_query = "SELECT count(id) as added
FROM spider_log
WHERE deal_source_id = '$deal_source_id'
AND action = 'added'
AND date_format(date_created, '%Y-%m-%d') = '$lastdate' ";
$extended_query = "SELECT count(id) as extended
FROM spider_log
WHERE deal_source_id = '$deal_source_id'
AND action = 'extended'
AND date_format(date_created, '%Y-%m-%d') = '$lastdate' ";
$duplicate_query = "SELECT count(id) as duplicate
FROM spider_log
WHERE deal_source_id = '$deal_source_id'
AND action = 'duplicate'
AND date_format(date_created, '%Y-%m-%d') = '$lastdate' ";
SELECT d.id,
d.source_name,
d.spider_status,
s.last_success_date,
COUNT(l.id) AS total,
SUM(l.id IS NOT NULL AND l.action='added' ) AS added,
SUM(l.id IS NOT NULL AND l.action='extended' ) AS extended,
SUM(l.id IS NOT NULL AND l.action='duplicate') AS duplicate
FROM deal_source d
JOIN spider s
ON s.deal_source_id = d.id
JOIN spider_log l
ON l.deal_source_id = d.id
ON l.date_created >= s.last_success_date
AND l.date_created < s.last_success_date + INTERVAL 1 DAY
GROUP BY d.id
Some points:
You can optimize performance of each query, using EXPLAIN and the careful adding of indexes.
You can combine all the queries to a big one, so you don't have to hit the database with a lot of queries.
Besides the lots of queries, The date_format(date_created, '%Y-%m-%d') = '$lastdate' is a performance killer because it apples a function (DATE_FORMAT()) to a column (date_created) so no index can be used and the function is called thosuand or million of times (as many rows are examined). Change such conditions - wherever they are in your code - to:
( date_created >= DATE('$lastdate')
AND date_created < DATE('$lastdate') + INTERVAL 1 DAY
)
or even better, if that $lastdate is a date, to:
( date_created >= '$lastdate'
AND date_created < '$lastdate' + INTERVAL 1 DAY
)
and even more better, if date_created is a DATE column, to:
date_created = '$lastdate'