mysql query taking too long - mysql

I am new to advanced queries so I likely have something conceptually wrong because when the database has over 1 million records I get this response rom my query...
ERROR 2013: Lost connection to MySQL server during query
Yes! It actually takes so long that it pukes before it finishes.
My query is this...
SELECT users.username,
table_1.field_abc, table_1.field_def,
table_2.field_ghi, table_2.field_jkl
FROM users
LEFT JOIN table_1 ON table_1.username = users.username
LEFT JOIN table_2 ON table_2.username = users.username
WHERE
table_1.field_abc REGEXP "(spork|yellow)" OR
table_1.field_def REGEXP "(spork|yellow)" OR
table_2.field_ghi REGEXP "(spork|yellow)" OR
table_2.field_jkl REGEXP "(spork|yellow)"
GROUP BY users.username
ORDER BY
(
( CASE WHEN table_1.field_abc LIKE "%spork%" THEN 1 ELSE 0 END ) +
( CASE WHEN table_1.field_abc LIKE "%yellow%" THEN 1 ELSE 0 END ) +
( CASE WHEN table_1.field_def LIKE "%spork%" THEN 1 ELSE 0 END ) +
( CASE WHEN table_1.field_def LIKE "%yellow%" THEN 1 ELSE 0 END ) +
( CASE WHEN table_2.field_ghi LIKE "%spork%" THEN 1 ELSE 0 END ) +
( CASE WHEN table_2.field_ghi LIKE "%yellow%" THEN 1 ELSE 0 END ) +
( CASE WHEN table_2.field_jkl LIKE "%spork%" THEN 1 ELSE 0 END ) +
( CASE WHEN table_2.field_jkl LIKE "%yellow%" THEN 1 ELSE 0 END )
)DESC;
I posted a sample dataset (with only a few records) at http://sqlfiddle.com/#!2/cbbda/28
The sample at sqlfiddle runs quick because there are only a few records but I tried duplicating records on my own server and the query ran quick with only a few records and extremely slow after I added a million records.
Is there any possible way to get my results quick?

Well folks... With your help we have a solution... See... http://sqlfiddle.com/#!2/fcfbd/5
BUT I DO STILL HAVE A QUESTION...
I altered the table to add the indexes...
ALTER TABLE `users` ADD FULLTEXT ( `username` );
ALTER TABLE `table_1` ADD FULLTEXT ( `field_abc`,`field_def` );
ALTER TABLE `table_2` ADD FULLTEXT ( `field_ghi`,`field_jkl` );
I then took the advice of #Barmar and changed the code to this...
SELECT users.username,
table_1.field_abc, table_1.field_def,
table_2.field_ghi, table_2.field_jkl
FROM users
LEFT JOIN table_1 ON table_1.username = users.username
LEFT JOIN table_2 ON table_2.username = users.username
WHERE
MATCH(table_1.field_abc,table_1.field_def,table_2.field_ghi,table_2.field_jkl)
AGAINST ("spork yellow" IN BOOLEAN MODE)
GROUP BY users.username
ORDER BY
(
( CASE WHEN MATCH(table_1.field_abc) AGAINST ("spork" IN BOOLEAN MODE) THEN 1 ELSE 0 END ) +
( CASE WHEN MATCH(table_1.field_abc) AGAINST ("yellow" IN BOOLEAN MODE) THEN 1 ELSE 0 END ) +
( CASE WHEN MATCH(table_1.field_def) AGAINST ("spork" IN BOOLEAN MODE) THEN 1 ELSE 0 END ) +
( CASE WHEN MATCH(table_1.field_def) AGAINST ("yellow" IN BOOLEAN MODE) THEN 1 ELSE 0 END ) +
( CASE WHEN MATCH(table_2.field_ghi) AGAINST ("spork" IN BOOLEAN MODE) THEN 1 ELSE 0 END ) +
( CASE WHEN MATCH(table_2.field_ghi) AGAINST ("yellow" IN BOOLEAN MODE) THEN 1 ELSE 0 END ) +
( CASE WHEN MATCH(table_2.field_ghi) AGAINST ("spork" IN BOOLEAN MODE) THEN 1 ELSE 0 END ) +
( CASE WHEN MATCH(table_2.field_ghi) AGAINST ("yellow" IN BOOLEAN MODE) THEN 1 ELSE 0 END )
)DESC;
With over 1,000,000 records in my real database, I got my result in 6.5027 seconds. That is A LOT better than... well, taking so long that it puked!
My only question now is... Why does it only work with IN BOOLEAN MODE and not the other 2 options mentioned at http://dev.mysql.com/doc/refman/5.0/en/fulltext-search.html#function_match or http://dev.mysql.com/doc/refman/5.5/en/fulltext-search.html?

I don't think so - with this table as-is, I doubt you'll make that run fast with all of those LIKEs on them. Those have to run a ridiculous number of times.
If those values are fixed, then you can add new columns to the table called abc_like_yellow and abc_like_spork, etc., and populate those values one time, then you can easily query off of that column.
But if you're trying to do this dynamically, you might be out of luck.

Since we're joining on username it is likely that an index on this column will speed things up.
Also, are you able to use an inner join as opposed to a left join? This can also speed up the query to quite a large extent.
And finally, if necessary, the ordering can be done in memory as opposed to asking the database to do it (ie order the result set after it is returned).

I was using my first solution but found that it gave some false positives that I could not figure out so I came up with this...
(SELECT username, MATCH(field_abc,field_def) AGAINST ("spork yellow" IN BOOLEAN MODE) AS score FROM table_1 HAVING score>0)
UNION ALL
(SELECT username, MATCH(field_ghi,field_jkl) AGAINST ("spork yellow" IN BOOLEAN MODE) AS score FROM table_2 HAVING score >0)
Since each record was returned separately and I can't use GROUP BY I added this PHP code after my query finished:
while($row = mysql_fetch_array($result) )
{
if( in_array($row['username'],$usernames) )
{
$usernames_count[$row['username']] += $row['score'];
}
else
{
array_push($usernames,$row['username']);
$usernames_count[$row['username']]=$row['score'];
}
}
arsort($usernames_count); // Sort the results high->low
foreach($usernames_count as $key=>$value)
{
echo "Username: ".$key." had a score of ".$value." in the search results<br/>";
}
It now seems so simple compared to the other attempts I made.

When your server has to scan through millions of entries, it simply may not be powerful enough to process the query quickly.
In general, to improve the speed of your website, you could try CloudFlare
If you are specifically trying to speed up your SQL, Google Cloud SQL may be able to help. Google's powerful servers are designed to scan through billions of SQL entries, for example when a Google search is performed.
As long as there are no errors being returned, the above two services will help to dramatically speed up your query time.
I hope I could help!
VCNinc

If you have access to SQL Server, highlight your complete query in SQL server, and click + L
This will show the query execution plan. Optimize the query based on these results;
if for example you see table scans then an index may assist.
Write queries that do not use the term distinct.
Do not order results if the order is unimportant.
In your sample the complicated last set of order-by is very expensive.
Rather follow these steps:
Pull the core information into a temporary table, with 9 extra columns (type int, intially set to 0)
after populating the core data, update each of the 8 columns based on the 0 or 1 criteria
update the last column as the sum of the other 8 columns
retrieve info from the table , with only a single "order-by" based on column 9.
In my experience this approach only takes 20% of the time compared to doing the order-by in-house.

Related

MySQL View 20x slower than Select

I have a query that selects ~8000 rows. When I execute the query it takes 0.1 sec.
When I copy the query into a view and execute the view it takes about 2 seconds. In the first row of explain it selects ~570K rows, i dont know why.
I dont understand the first Row and why it shows up only in the view explain
1 PRIMARY ALL NULL NULL NULL NULL
This is the query (yes i know im not a mysql pro and the query is not that efficent, but it works ans 0.1 sek would be ok for me. Does anyone know why it is so slow in a view?
MariaDB 10.5.9
select
`xxxxxxx`.`auftraege`.`Zustandigkeit` AS `Zustandigkeit`,
`xxxxxxx`.`auftraege`.`cms` AS `cms`,
`xxxxxxx`.`auftraege`.`auftrag_id` AS `auftrag_id`,
`xxxxxxx`.`angebot`.`angebot_id` AS `angebot_id`,
`xxxxxxx`.`kunden`.`kunde_id` AS `kid`,
`xxxxxxx`.`angebot`.`kunde_id` AS `kunde_id`,
`xxxxxxx`.`kunden`.`firma` AS `firma`,
`xxxxxxx`.`auftraege`.`gekuendigt` AS `gekuendigt`,
`xxxxxxx`.`kunden`.`ansprechpartnerVorname` AS `ansprechpartnerVorname`,
`xxxxxxx`.`kunden`.`ansprechpartner` AS `ansprechpartner`,
`xxxxxxx`.`auftraege`.`ampstatus` AS `ampstatus`,
`xxxxxxx`.`auftraege`.`autoMahnungen` AS `autoMahnungen`,
`xxxxxxx`.`kunden`.`mail` AS `mail`,
`xxxxxxx`.`kunden`.`ansprechpartnerAnrede` AS `ansprechpartnerAnrede`,
case
`xxxxxxx`.`kunden`.`ansprechpartnerAnrede`
when
'm'
then
concat('Herr ', ifnull(`xxxxxxx`.`kunden`.`ansprechpartnerVorname`, ''), ifnull(`xxxxxxx`.`kunden`.`ansprechpartner`, ''))
else
concat('Frau ', ifnull(`xxxxxxx`.`kunden`.`ansprechpartnerVorname`, ''), ifnull(`xxxxxxx`.`kunden`.`ansprechpartner`, ''))
end
AS `ansprechpartnerfullName`, `xxxxxxx`.`kunden`.`website` AS `website`, `xxxxxxx`.`personal`.`name_betrieb` AS `name_betrieb`, `xxxxxxx`.`kunden`.`prioritaet` AS `prioritaet`, `xxxxxxx`.`auftraege`.`infoemail` AS `infoemail`, `xxxxxxx`.`auftraege`.`keywords` AS `keywords`, `xxxxxxx`.`auftraege`.`ftp_h` AS `ftp_h`, `xxxxxxx`.`auftraege`.`ftp_u` AS `ftp_u`, `xxxxxxx`.`auftraege`.`ftp_pw` AS `ftp_pw`, `xxxxxxx`.`auftraege`.`lgi_h` AS `lgi_h`, `xxxxxxx`.`auftraege`.`lgi_u` AS `lgi_u`, `xxxxxxx`.`auftraege`.`lgi_pw` AS `lgi_pw`, `xxxxxxx`.`auftraege`.`autoRemind` AS `autoRemind`, `xxxxxxx`.`kunden`.`telefon` AS `telefon`, `xxxxxxx`.`kunden`.`mobilfunk` AS `mobilfunk`, `xxxxxxx`.`auftraege`.`kommentar` AS `kommentar`, `xxxxxxx`.`auftraege`.`phase` AS `phase`, `xxxxxxx`.`auftraege`.`datum` AS `datum`, `xxxxxxx`.`angebot`.`typ` AS `typ`,
case
`xxxxxxx`.`auftraege`.`gekuendigt`
when
'1'
then
'Ja'
else
'Nein'
end
AS `Gekuendigt ? `,
(
select
count(`xxxxxxx`.`status`.`aenderung`)
from
`xxxxxxx`.`status`
where
`xxxxxxx`.`status`.`auftrag_id` = `xxxxxxx`.`auftraege`.`auftrag_id`
)
AS `aenderungen`,
`xxxxxxx`.`auftraege`.`vertragStart` AS `vertragStart`,
`xxxxxxx`.`auftraege`.`vertragEnde` AS `vertragEnde`,
case
`xxxxxxx`.`auftraege`.`zahlungsart`
when
'U'
then
'Überweisung'
when
'L'
then
'Lastschrift'
else
'Unbekannt'
end
AS `Zahlungsart`, `xxxxxxx`.`kunden`.`yyyyy_piwik` AS `yyyyy_piwik`,
(
select
max(`xxxxxxx`.`status`.`datum`) AS `mxDTst`
from
`xxxxxxx`.`status`
where
`xxxxxxx`.`status`.`auftrag_id` = `xxxxxxx`.`auftraege`.`auftrag_id`
and `xxxxxxx`.`status`.`typ` = 'SEO'
)
AS `mxDTst`,
(
select
case
`xxxxxxx`.`rechnungen`.`beglichen`
when
'YES'
then
'isOk'
else
'isAffe'
end
AS `neuUwe`
from
(
`xxxxxxx`.`zahlungsplanneu`
join
`xxxxxxx`.`rechnungen`
on(`xxxxxxx`.`zahlungsplanneu`.`rechnungsnummer` = `xxxxxxx`.`rechnungen`.`rechnungsnummer`)
)
where
`xxxxxxx`.`zahlungsplanneu`.`auftrag_id` = `xxxxxxx`.`auftraege`.`auftrag_id`
and `xxxxxxx`.`rechnungen`.`beglichen` <> 'STO' limit 1
)
AS `neuer`,
(
select
group_concat(`xxxxxxx`.`kunden_keywords`.`keyword` separator ',')
from
`xxxxxxx`.`kunden_keywords`
where
`xxxxxxx`.`kunden_keywords`.`kunde_id` = `xxxxxxx`.`kunden`.`kunde_id`
)
AS `keyword`,
(
select
case
count(0)
when
0
then
'Cool'
else
'Uncool'
end
AS `AusfallVor`
from
`xxxxxxx`.`rechnungen`
where
`xxxxxxx`.`rechnungen`.`rechnung_tag` < current_timestamp() - interval 15 day
and `xxxxxxx`.`rechnungen`.`kunde_id` = `xxxxxxx`.`kunden`.`kunde_id`
and `xxxxxxx`.`rechnungen`.`beglichen` = 'NO' limit 1
)
AS `Liquidiert`
from
(
((((`xxxxxxx`.`auftraege`
join
`xxxxxxx`.`angebot`
on(`xxxxxxx`.`auftraege`.`angebot_id` = `xxxxxxx`.`angebot`.`angebot_id`))
join
`xxxxxxx`.`kunden`
on(`xxxxxxx`.`angebot`.`kunde_id` = `xxxxxxx`.`kunden`.`kunde_id`))
left join
`xxxxxxx`.`kunden_keywords`
on(`xxxxxxx`.`angebot`.`kunde_id` = `xxxxxxx`.`kunden_keywords`.`kunde_id`))
join
`xxxxxxx`.`personal`
on(`xxxxxxx`.`kunden`.`bearbeiter` = `xxxxxxx`.`personal`.`personal_id`))
left join
`xxxxxxx`.`status`
on(`xxxxxxx`.`auftraege`.`auftrag_id` = `xxxxxxx`.`status`.`auftrag_id`)
)
group by
`xxxxxxx`.`auftraege`.`auftrag_id`
order by
NULL
UPDATE 1
1. The View Itself (Duration 1.83 sec)
1.1 Create the View: This is the View i created, it only contains the query from above.
1.2 Executing the View: It takes 1.83 sek to execute the view
1.3 Analyze the View: This is the explain of the view
2. The view with added where clause (Duration 1.86 sec)
2.1 Analyze the View with added where clause #rick wanted me to add a where clause to the view, if i understood him correctly. This is the explain of the view, where i added a where clause, takes 1.86 sec.
3. The Query, that is the source of the view (Duration: 0.1 sec)
3.1 Execute the query directly This is the query, that is the source of the view, when i execute it directly to the server. It takes ~0.1 - 0.2 seconds.
3.2 Analyze the direct queryAnd this is the explain of the pure query.
Why the view is so much slower, by only cupsuling the query inside of the view?
Update 2
These are the indexes I have set
ALTER TABLE angebot ADD INDEX angebot_idx_angebot_id (angebot_id);
ALTER TABLE auftraege ADD INDEX auftraege_idx_auftrag_id (auftrag_id);
ALTER TABLE kunden ADD INDEX kunden_idx_kunde_id (kunde_id);
ALTER TABLE kunden_keywords ADD INDEX kunden_keywords_idx_kunde_id (kunde_id);
ALTER TABLE personal ADD INDEX personal_idx_personal_id (personal_id);
ALTER TABLE rechnungen ADD INDEX rechnungen_idx_rechnungsnummer_beglichen (rechnungsnummer,beglichen);
ALTER TABLE rechnungen ADD INDEX rechnungen_idx_beglichen_kunde_id_rechnung (beglichen,kunde_id,rechnung_tag);
ALTER TABLE status ADD INDEX status_idx_auftrag_id (auftrag_id);
ALTER TABLE status ADD INDEX status_idx_typ_auftrag_id_datum (typ,auftrag_id,datum);
ALTER TABLE zahlungsplanneu ADD INDEX zahlungsplanneu_idx_auftrag_id (auftrag_id);
Be consistent between tables. kunde_id, for example, seems to be declared differently between tables. This may be preventing some obvious optimizations. (There are 6 JOINs that say func in EXPLAIN`.)
Remove the extra parentheses in JOINs. They may be preventing what the Optimizer is happy to do -- rearrange the tables in a JOIN.
Turn the query inside out. By this, I mean to do the minimum amount of work to do the main JOIN. Collect mostly id(s). Then do the dependent subqueries in an outer select. Something like:
SELECT ... ( SELECT ... ), ...
FROM ( SELECT a1.id
FROM a AS a1
JOIN b ON ..
JOIN c ON .. )
JOIN a AS a2 ON a2.id = a1.id
JOIN d ON ...
The "inside-out" kludge may eliminate the need for the GROUP BY. (Your query is too complex for me to see for sure.) If so, then I call the problem "explode-implode" -- Your query first JOINs, producing a temp table with lots of rows ("explodes"). Then it does a GROUP BY ("implodes").
More
These indexes will probably help:
status: (auftrag_id, typ, datum, aenderung)
rechnungen: (beglichen, kunde_id, rechnung_tag)
rechnungen: (rechnungsnummer, beglichen)
zahlungsplanneu: (auftrag_id, rechnungsnummer)
kunden_keywords: (kunde_id, keyword) -- (unless `kunde_id` is the PK)
(I see from all 3 EXPLAINs that you probably have sufficient indexes on kunden_keywords and status. Show me what indexes you have, so I can see if the existing indexes are as good as my suggestions.) "Using index" == "covering index".
Near the end is this LEFT JOIN, but I did not spot any use for the table; perhaps it can be removed?
left join `kunden_keywords` on(`angebot`.`kunde_id` = `kunden_keywords`.`kunde_id`))

Multiple in select statements in single query

Currently, I am trying to select a list of posts from people who are friends and/or are being followed by the current user. However, I am running into an issue when I add in the second statement for the following table.
SELECT * FROM login, threads WHERE login.id=threads.poster_id AND deleted=0 AND login.id
IN ((SELECT CASE WHEN second_id=? THEN first_id ELSE second_id END FROM friends WHERE first_id=? OR second_id=?)
AND (SELECT CASE WHEN follower=? THEN following ELSE follower END FROM following WHERE follower=?))
ORDER BY threads.posted DESC LIMIT 20
Just use two explicit IN conditions with the two subqueries:
SELECT *
FROM login
INNER JOIN threads
ON login.id = threads.poster_id
WHERE
deleted = 0 AND
(login.id IN (SELECT CASE WHEN second_id=? THEN first_id ELSE second_id END
FROM friends WHERE first_id=? OR second_id=?) AND
login_id IN (SELECT CASE WHEN follower=? THEN following ELSE follower END
FROM following WHERE follower=?))
ORDER BY
threads.posted
DESC LIMIT 20;
This may fix the syntax errors and even give you the output you expect, but I also feel that we may be able to clean up the query a bit more. Sample data would go a long way towards that end.

MYSQL Check for record existence while fetching records

I've ran into some performance issues with my database structure "or better to say my query instead "
I have a the following table :
http://sqlfiddle.com/#!9/348cb
And following query trying to fetch certain data, and after that trying to check if there are other records matching my conditions, it's all in the following query.
it is working as expected, the only reason that I'm asking this question is that if there is a way I could increase its performance or use another way to get the results.
As you can see, There two ( SELECT )'s which trying to check if there are any other records containing current query data.
SELECT (
SELECT COUNT(*) FROM log AS LIKES
WHERE L.target_account=LIKES.target_account
AND LIKES.type='like'
) as liked,
(
SELECT COUNT(*) FROM log AS COMMENTS
WHERE L.target_account=COMMENTS.target_account
AND COMMENTS.type='follow_back'
) as follow_back,
(
SELECT COUNT(*) FROM log AS FOLLOW_BACK
WHERE L.target_account=FOLLOW_BACK.target_account
AND COMMENTS.type='follow_back'
) as follow_back,
L.*
FROM `log` as L
WHERE `L`.`information` = '".$target_name."'
AND `L`.`account_id` = '".$id."'
AND `L`.`date_ts` BETWEEN CURDATE() - INTERVAL ".$limit." DAY AND CURDATE()
This query takes too much time to fetch the data.
Thanks in advance.
You may be able to rewrite the query, depending on the relationship between target account and account id.
In the meantime, you want indexes. The two you want are instagram_log(target_account, type) and instagram_log(account_id, information, date_ts):
create index idx_instagram_log_1 on instagram_log(target_account, type);
create index idx_instagram_log_2 on instagram_log(account_id, information, date_ts);
SELECT SUM(LIKES) LIKES,SUM(FOLLOW_BACK) FOLLOW_BACK,SUM(COMMENTS) FROM
(
SELECT
CASE WHEN L.type='like' THEN 1 ELSE 0 END LIKES,
CASE WHEN L.type='follow_back' THEN 1 ELSE 0 END FOLLOW_BACK,
CASE WHEN L.type='comments' THEN 1 ELSE 0 END COMMENTS
FROM `log` as L
WHERE `L`.`information` = '".$target_name."'
AND `L`.`account_id` = '".$id."'
AND `L`.`date_ts` BETWEEN CURDATE() - INTERVAL ".$limit." DAY AND CURDATE()
)Z
Try the above query.

MySQL CASE based on previous CASE value

In MySQL, is it possible to have two CASE statements in the SELECT clause, where the second CASE statement relies on the first CASE statement?
For example, consider the following query:
SELECT CASE WHEN `user`.`id` < 500 THEN 'awesome' ELSE 'lame' END
AS `status`
, CASE WHEN `status` = 'awesome' THEN 'You rock' ELSE 'You stink' END
AS `message`
FROM `user`
Basically, the user ID determines the status, and then the status determines the message.
However, as you might have guessed, this query generates this error:
Unknown column 'status'
The only solution I have found so far is two generate a temporary table, view, or subquery, and then the message is determined by the status returned in this subquery.
Is there a way to write this query without the use of a temporary table, view or subquery? I'm trying to avoid these constructs to keep the query simple and optimized if possible. Thank you!
You can, using temporary variables:
select
#status1 := (case
when user.id < 500 then 'awesome'
else 'lame'
end) as `status`,
(case
when #status1 = 'awesome' then 'You rock'
else 'You stink'
end) as message
from
user;
Some things you must know about temp variables:
They are always preceded by #
Avoid using reserved words, just in case (that's the reason I named the variable #status1
After the # symbol, they must begin with a letter, and must not have spaces
When you update them in a single query, they are updated "left-to-right" (talking about columns) and "first-to-last" (talking about rows). That can help you calculate cummulative sums or averages.
Example (for point 2):
select #t := 1, #t := #t + 1;
#t1 | #t2
----+----
1 | 2
Example (for point 3):
select myTable.x, #t := #t + myTable.x as cummulative_x
from
(select #t := 0) as init, -- You need to initialize the variable,
-- otherwise the results of the evaluation will be NULL
myTable
order by myTable.x -- Always specify how to order the rows,
-- or the cummulative values will be quite odd
-- (and maybe not what you want)
;
x | cummulative_x
---+---------------
1 | 1
1 | 2
2 | 4
3 | 7
Temporary variables can help you do some awesome things... feel free to play around ;)
Update
If you want to define conditions on the result of this query, there are two ways to do it:
Use the above query as a data-source for a second query (i.e. make it a subquery in the from clause of another query
Create a temp table and query on it
Option 1:
select a.*
from (
-- The query with temp variables defined
)
where -- ATTENTION: you need to write the references to the column names of the subquery
Option 2: (my personal favorite)
drop table if exists temp_my_temp_table;
create temporary table temp_my_temp_table
select
#status1 := (case
when user.id < 500 then 'awesome'
else 'lame'
end) as `status`,
(case
when #status1 = 'awesome' then 'You rock'
else 'You stink'
end) as message
from
user;
-- Add all appropriate indexes to this newly created table:
-- alter table temp_my_temp_table
-- add index idx_status(`status`),
-- add index idx_mess(message);
-- Make your queries on this new temp table
select * from temp_my_temp_table
-- where ...
;
Things you must know about a temp table:
They are created on RAM (by default, and only if the table is not too big)
They are only visible to the connection that created it
They are eliminated once the connection that created it is closed (or terminated in any way)
You can't use it more than once in a FROM clause. Other than that, you can use it as any other table in your database
Another update
Just by chance I came across this question and its answer. If you want to use the result of your column (calculated with temp variables) as a condition, MySQL allows this:
select
#status1 := (case
when user.id < 500 then 'awesome'
else 'lame'
end) as `status`,
(case
when #status1 = 'awesome' then 'You rock'
else 'You stink'
end) as message
from
user
having
`status` = 'awesome';
Instead of using where use having, and refer not to the temp variable, but to the alias of the column.

Update with SELECT and group without GROUP BY

I have a table like this (MySQL 5.0.x, MyISAM):
response{id, title, status, ...} (status: 1 new, 3 multi)
I would like to update the status from new (status=1) to multi (status=3) of all the responses if at least 20 have the same title.
I have this one, but it does not work :
UPDATE response SET status = 3 WHERE status = 1 AND title IN (
SELECT title FROM (
SELECT DISTINCT(r.title) FROM response r WHERE EXISTS (
SELECT 1 FROM response spam WHERE spam.title = r.title LIMIT 20, 1)
)
as u)
Please note:
I do the nested select to avoid the famous You can't specify target table 'response' for update in FROM clause
I cannot use GROUP BY for performance reasons. The query cost with a solution using LIMIT is way better (but it is less readable).
EDIT:
It is possible to do SELECT FROM an UPDATE target in MySQL. See solution here
The issue is on the data selected which is totaly wrong.
The only solution I found which works is with a GROUP BY:
UPDATE response SET status = 3
WHERE status = 1 AND title IN (SELECT title
FROM (SELECT title
FROM response
GROUP BY title
HAVING COUNT(1) >= 20)
as derived_response)
Thanks for your help! :)
MySQL doesn't like it when you try to UPDATE and SELECT from the same table in one query. It has to do with locking priorities, etc.
Here's how I would solve this problem:
SELECT CONCAT('UPDATE response SET status = 3 ',
'WHERE status = 1 AND title = ', QUOTE(title), ';') AS sql
FROM response
GROUP BY title
HAVING COUNT(*) >= 20;
This query produces a series of UPDATE statements, with the quoted titles that deserve to be updated embedded. Capture the result and run it as an SQL script.
I understand that GROUP BY in MySQL often incurs a temporary table, and this can be costly. But is that a deal-breaker? How frequently do you need to run this query? Besides, any other solutions are likely to require a temporary table too.
I can think of one way to solve this problem without using GROUP BY:
CREATE TEMPORARY TABLE titlecount (c INTEGER, title VARCHAR(100) PRIMARY KEY);
INSERT INTO titlecount (c, title)
SELECT 1, title FROM response
ON DUPLICATE KEY UPDATE c = c+1;
UPDATE response JOIN titlecount USING (title)
SET response.status = 3
WHERE response.status = 1 AND titlecount.c >= 20;
But this also uses a temporary table, which is why you try to avoid using GROUP BY in the first place.
I would write something straightforward like below
UPDATE `response`, (
SELECT title, count(title) as count from `response`
WHERE status = 1
GROUP BY title
) AS tmp
SET response.status = 3
WHERE status = 1 AND response.title = tmp.title AND count >= 20;
Is using GROUP BY really that slow ? The solution you tried to implement looks like requesting again and again on the same table and should be way slower than using GROUP BY if it worked.
This is a funny peculiarity with MySQL - I can't think of a way to do it in a single statement (GROUP BY or no GROUP BY).
You could select the appropriate response rows into a temporary table first then do the update by selecting from that temp table.
you'll have to use a temporary table:
create temporary table r_update (title varchar(10));
insert r_update
select title
from response
group
by title
having count(*) < 20;
update response r
left outer
join r_update ru
on ru.title = r.title
set status = case when ru.title is null then 3 else 1;