How to optimize double select query - mysql

SELECT `id`, `field2`, `field3`, `field4` as Title FROM `articles`
WHERE `category_id` = 'X'
AND `id` NOT IN
(SELECT `articleid` FROM `article-seen` WHERE `userid` = 'Y')
How can I optimize this?
I think double select is bad, but im new to mysql

Try using JOIN that will get you the same result but makes the query looks simpler

The optimization depends (I think) on the version of MySQL you are using.
This is how you write it as a join:
SELECT `id`, `field2`, `field3`, `field4` as Title
FROM `articles` a left outer join
`articles_seen` arts
on a.id = arts.articleid and arts.userid = 'Y'
where a.`category_id` = 'X' and
arts.id is null;
This query, at least, doesn't materialize the subquery, which (I think) your originally query would.
To makes this faster, you want to add indexes. The ones that come to mind are: articles(category_id, id) and articles_seen(userid, articleid). You could also add the fields in the select to the first index, so the entire query can be satisfied by looking at the index, rather than returning to the main table.

Related

SQL query not selecting property

I have query like this
SELECT *
FROM `GSheets`
WHERE `sheetcat` = 'Unsubscribed'
AND `sheetcat` IS NOT NULL
AND `user` LIKE '%,r00t,%'
OR `user` LIKE 'r00t,%'
OR '%,r00t'
OR `user` = 'r00t'
I specify sheetcat to be Unsubscribed, but query response is NULL (or blank)?
Why?
This is a problem when you store lists of things in delimited strings. Bad, bad, bad data design. The first and primary advice is to create a new table with one row per sheet and one row per user.
Now, sometimes we are stuck with other people's really bad, bad, bad design decisions. MySQL offers find_in_set() which does what you want:
SELECT g.*
FROM `GSheets` g
WHERE `sheetcat` = 'Unsubscribed' AND
FIND_IN_SET('r00t', `user`) > 0;
Note that sheetcat IS NOT NULL is redundant. A NULL value would fail the first condition. Unless you possibly intend:
SELECT g.*
FROM `GSheets` g
WHERE `sheetcat` = 'Unsubscribed' OR
(sheetcat IS NOT NULL AND FIND_IN_SET('r00t', `user`) > 0);
But that would not be my first guess as to your intention.
Your query would also work if you put parentheses around the OR conditions. But even that is too complicated. A simpler version using LIKE would be:
WHERE `sheetcat` = 'Unsubscribed' AND
CONCAT(',', `user`, ',') LIKE '%,r00t,%'
AND takes precedence over OR. You need to group your OR conditions:
SELECT *
FROM `GSheets`
WHERE `sheetcat` = 'Unsubscribed'
AND
( `user` LIKE '%,r00t,%'
OR `user` LIKE 'r00t,%'
OR `user` LIKE '%,r00t'
OR `user` = 'r00t'
)
The second sheetcat condition is also redundant, you can remove the NULL check.

How do I optimize these SQL queries?

I have 1 variable which is used as WHERE condition in 3 initial queries. Based on the response from one of those I need to create queries to 2 more queries.
$var1 = $_GET['id']; $var2 = $_GET['truck'];
//first two are easy
SELECT `name`, `address` FROM `company` WHERE `id`='".$var1."' LIMIT 1; //q1
SELECT `value`, `date` FROM `checks` WHERE `truck`='".$var2."'; //q2
//the 3rd query may have multiple results and for every result i need **q4** and **q5** to be executed
SELECT `loadNumber`, `cfnNumber` FROM `loads` WHERE `truck`='".$var2."' ; //q3
//notice that WHERE conditions use values from **q3**
SELECT `value`, `date` FROM `finances` WHERE `load`='".loadNumber."'; //q4
SELECT `address` FROM `stops` WHERE `load`='".loadNumber."'; //q5
My question is about optimization as I am trying to combine all these queries into one if possible, hoping it will mean lesser server load time. I am not very familiar with JOINS, so ultimately this is how my code looks like with real data. And here is the result, also with real data. Is there a need to optimize/join these queries in order to decrease server load?
I would suggest to combine just last 3 queries, keep first 2 as is:
SELECT
l.`loadNumber`, l.`cfnNumber` ,
f.`value`, f.`date`,
s.`address`
FROM `loads` l
LEFT JOIN `finances` f
ON f.`load`= l.loadNumber
LEFT JOIN `stops` s
ON s.`load`= l.loadNumber
WHERE `truck`='".$var2."'
Do a subquery. Be something like this:
SELECT `item1_t1`, `item2_t1` FROM `table1` WHERE `item2_t1` in (SELECT `item1_t2` FROM `table2` WHERE `item2_t2`='".$var."');

Returning results from multiple rows into separate variables with one query

Question: What is the quickest way to assign values from multiple rows of a single column into separate variables?
Using MySQL 5.6, I have a table that stores data in regular time intervals. In the insert trigger, I am attempting to detect when, among other similar things, a "peak" in the values has occurred, defined as two consecutive increasing values to a highest value, followed by two consecutive decreasing values.
Because there are several similar calculations to perform on the same data, I am first retrieving the values from a column for the last five rows into variables. I have the ID numbers of the rows.
One way to do this would be individual SELECT queries for each ID. However, I would like to consolidate them into a single query for speed, since the data will be entered into the database in blocks of 40k rows at a time.
Individual Selects:
SET currentValue = (SELECT `value` FROM `table` WHERE `tableid`=currentID);
SET prev1Value = (SELECT `value` FROM `table` WHERE `tableid`=prev1ID);
SET prev2Value = (SELECT `value` FROM `table` WHERE `tableid`=prev2ID);
SET prev3Value = (SELECT `value` FROM `table` WHERE `tableid`=prev3ID);
SET prev4Value = (SELECT `value` FROM `table` WHERE `tableid`=prev4ID);
I know another way to do this would be using joins on the same table. However, this seems like this could be a slow way to go about it. Is there a faster way to do this without using JOINs? Thanks.
Multiple JOINs:
SELECT a0.`value`, a1.`value`, a2.`value`, a3.`value`, a4.`value`
INTO currentValue, prev1Value, prev2Value, prev3Value, prev4Value
FROM (SELECT `value` FROM `table` WHERE `tableid`=currentID) AS a0
INNER JOIN (SELECT `value` FROM `table` WHERE `tableid`=prev1ID) AS a1
INNER JOIN (SELECT `value` FROM `table` WHERE `tableid`=prev2ID) AS a2
INNER JOIN (SELECT `value` FROM `table` WHERE `tableid`=prev3ID) AS a3
INNER JOIN (SELECT `value` FROM `table` WHERE `tableid`=prev4ID) AS a4
Another thing I thought of is something like:
SELECT IF(`tableid`=currentID, `value`, NULL)
, IF(`tableid`=prev1ID, `value`, NULL)
, IF(`tableid`=prev2ID, `value`, NULL)
, IF(`tableid`=prev3ID, `value`, NULL)
, IF(`tableid`=prev4ID, `value`, NULL)
INTO currentValue, prev1Value, prev2Value, prev3Value, prev4Value
FROM `table`
WHERE `tableid` IN (currentID, prev1ID, prev2ID, prev3ID, prev4ID);
But I haven't tested it yet.
For now I am going to go with the JOINs until everything is in place, and I can test the IF statement model. However, if someone knows that isn't going to work or has another method that would work better, I would appreciate it. Also, would putting this into a VIEW help with the speed of the query?
Thanks.

Simple MySQL query runs very slow

I found some strange(for me) behavour in MySQL. I have a simple query:
SELECT CONVERT( `text`.`old_text`
USING utf8 ) AS stext
FROM `text`
WHERE `text`.`old_id` IN
(
SELECT `revision`.`rev_text_id`
FROM `revision`
WHERE `revision`.`rev_id`
IN
(
SELECT `page_latest`
FROM `page`
WHERE `page_id` = 108
)
)
when i run it, phpmyadmin show execution time of 77.0446 seconds.
But then i replace
WHERE `text`.`old_id` IN
by
WHERE `text`.`old_id` =
it's execution time falls to about 0.001 sec. Result of this query
SELECT `revision`.`rev_text_id`
FROM `revision`
WHERE `revision`.`rev_id`
IN
(
SELECT `page_latest`
FROM `page`
WHERE `page_id` = 108
)
is
+------------+
|rev_text_id |
+------------+
|6506 |
+------------+
Can somebody please explain this behavour?
try to add INDEX on the following columns,
ALTER TABLE `text` ADD INDEX idx_text (old_id);
ALTER TABLE `revision` ADD INDEX idx_revision (rev_text_id);
and Execute the following query
SELECT DISTINCT CONVERT(a.`old_text` USING utf8 ) AS stext
FROM `text` a
INNER JOIN `revision` b
ON a.`old_id` = b.`rev_text_id`
INNER JOIN `page` c
ON b.`rev_id` = c.`page_latest`
WHERE c.`page_id` = 108
PS: Can you run also the following query and post their respective results?
DESC `text`;
DESC `revision`;
DESC `page`;
There are two primary ways you can increase your query performance here
Add Indexes (such as Kuya mentioned)
Rid yourself of the subqueries where possible
For Indexes, add an index on the columns you are searching for your matches:
text.old_id, revision.rev_text_id & page.page_id
ALTER TABLE `text` ADD INDEX idx_text (old_id);
ALTER TABLE `revision` ADD INDEX idx_revision (rev_text_id);
ALTER TABLE `page` ADD INDEX idx_page (page_id);
Your next issue is that nested-sub-selects are hell on your query execution plan. Here is a good thread discussing JOIN vs Subquery. Here is an article on how to get execution plan info from mySQL.
First looks at an execution plan can be confusing, but it will be your best friend when you have to concern yourself with query optimization.
Here is an example of your same query with just joins ( you could use inner or left and get pretty much the same result). I don't have your tables or data, so forgive synax issues (there is no way I can verify the code works verbatim in your environment, but it should give you a good starting point).
SELECT
CONVERT( `text`.`old_text` USING utf8 ) AS stext
FROM `text`
-- inner join only returns rows when it can find a
-- matching `revision`.`rev_text_id` row to `text`.`old_id`
INNER JOIN `revision`
ON `text`.`old_id` = `revision`.`rev_text_id`
-- Inner Join only returns rows when it can find a
-- matching `page_latest` row to `page_latest`
INNER JOIN `page`
ON `revision`.`rev_id` = `page`.`page_latest`
WHERE `page`.`page_id` = 108
MySQLDB is looping through each result of the inner query and comparing it with each record in the outer query.
in the second inner query;
WHERE `revision`.`rev_id`
IN
( SELECT `page_latest`
FROM `page`
WHERE `page_id` = 108
you should definitely use '=' instead of IN, since you're selecting a distinct record, there would be no point in looping through a result when you know only one record will be returned each time

Strange Mysql Query Performance on View

I have a view : vcompanyendofday
The following query executes in just 0.7 secs
Select * from vcompanyendofday
But a simple where condition to this query takes around 200.0 secs
select * from vcompanyendofday where companyid <= 51;
This is the view definition:
CREATE VIEW `vcompanyendofday` AS
select `c`.`companyid` AS `companyid`,
`c`.`scripcode` AS `scripcode`,
`e`.`eoddate` AS `eoddate`,
`e`.`prevclose` AS `prevclose`,
`e`.`delqty` AS `delqty`
from (
`company` `c`
left join
`endofday` `e`
on ((`c`.`companyid` = `e`.`companyid`)))
where (`e`.`eoddate` =
(
select max(`e2`.`eoddate`) AS `max(eoddate)`
from `endofday` `e2`
where (`e2`.`companyid` = `c`.`companyid`)
)
);
Seems you don't have an index on endofday.companyid
When you add the condition, company becomes leading in the join, and kills all performance.
Create an index on endofday.companyid:
CREATE INDEX ix_endofday_companyid ON endofday(companyid)
By the way, if you want all companies to be returned, you need to put the subquery into the ON clause of the OUTER JOIN, or your missing endofday's will be filtered out:
CREATE VIEW `vcompanyendofday` AS
select `c`.`companyid` AS `companyid`,
`c`.`scripcode` AS `scripcode`,
`e`.`eoddate` AS `eoddate`,
`e`.`prevclose` AS `prevclose`,
`e`.`delqty` AS `delqty`
from (
`company` `c`
left join
`endofday` `e`
on `c`.`companyid` = `e`.`companyid`
AND `e`.`eoddate` =
(
select max(`e2`.`eoddate`) AS `max(eoddate)`
from `endofday` `e2`
where (`e2`.`companyid` = `c`.`companyid`)
)
Have you tried the select used to create the view by itself with the WHERE clause to see what happens?
If the problem happens with that, run EXPLAIN on that query to see what's happening.
At a guess, there's no index on companyid in one of the tables, most likely endofday.