optimize sql query on website - php

So I have
a friends website and I'm trying to help him optimize the queries
because he got an email from the host, that his website was eating too
much resources. So looking at this query I was thinking, is there any way
this can be faster or smarter (consume less time or less memory) I don't
know which would be the best approach maybe prepared statements or some
improvement of the current code.
$query=#mysql_query("
SELECT
tl.title,
tl.dt,
tk.nr as nr2,
tk.name as cat,
tf.name,
tf.type,
tf.alt,
tt.trupi
FROM
".$tab['news']." as tl,
".$tab['category']." as tk,
".$tab['foto']." as tf,
".$tab['entry']." as th,
".$tab['body']." as tt
WHERE
tl.nr = '$nr' AND
tl.foto = tf.nr AND
tl.kategoria = tk.nr AND
tl.hyrja = th.nr AND
tl.trupi = tt.nr");
I'd appreciate all of your suggestions,
Thank you.

For a start, you could use the ANSI 92 join syntax.
Combined with getting rid of two letter acronyms, that would have the benefit of making it clearer what your queries were doing.

Maybe try...
$nr = mysql_real_escape_string( $nr );
$tab['category'] = mysql_real_escape_string( $tab['category'] );
$tab['foto'] = mysql_real_escape_string( $tab['foto'] );
$tab['entry'] = mysql_real_escape_string( $tab['entry'] );
$tab['body'] = mysql_real_escape_string( $tab['body'] );
$query = #mysql_query("
SELECT
tl.title,
tl.dt,
tk.nr as nr2,
tk.name as cat,
tf.name,
tf.type,
tf.alt,
tt.trupi
FROM
".$tab['news']." as tl
LEFT JOIN ".$tab['category']." as tk ON ( tk.nr = tl.kategoria )
LEFT JOIN ".$tab['foto']." as tf ON ( tf.nr = tl.foto )
LEFT JOIN ".$tab['entry']." as th ON ( th.nr = tl.hyrja )
LEFT JOIN ".$tab['body']." as tt ON ( tt.nr = tl.trupi )
WHERE
tl.nr = '$nr'");
Note the escaping of variables before using them in an SQL string (See Bobby Tables.)
Another thing to bear in mind is the design of the database in it's entirety. If there are any instances of "one-to-one" relationships between tables, then those two tables should be collapsed into a single table, etc.

no need to use as for each and every table, and also use Explain to check how optimal your query is you can find a great guide here http://www.databasejournal.com/features/mysql/article.php/1382791/Optimizing-MySQL-Queries-and-Indexes.htm on how to interpret the result of explain
All added actions to a query will decrease performance in one way or another, but I think the bottleneck is the join itself and the way you relate the tables. Try using integers(ids) when relating one table to another and if that's not possible make sure the column that your relating with is indexed. That would increase your query remarkable im certain.

Related

How to optimize long query that displays thousands of data

I have almost thousands of data to display for my reports and it makes my browser lags due to the heavy data. I think that my query is the real problem. How can I optimized my query? is there something that I should add in my query?
I am using Xampp which supports PHP7.
SELECT
`payroll_billed_units`.`allotment_code`,
`payroll_billed_units`.`category_name`,
`payroll_billed_units`.`ntp_number`,
`payroll_billed_units`.`activity`,
`payroll_billed_units`.`regular_labor`,
`payroll_sub`.`block_number`,
(SELECT
GROUP_CONCAT(DISTINCT `lot_number` SEPARATOR ', ')
FROM
`payroll_billed_units` `lot_numbers`
WHERE
`lot_numbers`.`allotment_code` = `payroll_billed_units`.`allotment_code`
AND `lot_numbers`.`category_name` = `payroll_billed_units`.`category_name`
AND `lot_numbers`.`ntp_number` = `payroll_billed_units`.`ntp_number`
AND `lot_numbers`.`activity` = `payroll_billed_units`.`activity`) AS `lot_numbers`,
(SELECT
COUNT(`billed`.`ntp_id`)
FROM
`regular_ntp` `billed`
WHERE
`billed`.`allotment_code` = `payroll_billed_units`.`allotment_code`
AND `billed`.`category_name` = `payroll_billed_units`.`category_name`
AND `billed`.`ntp_number` = `payroll_billed_units`.`ntp_number`
AND `billed`.`activity` = `payroll_billed_units`.`activity`) AS `billed`,
(SELECT
COUNT(`approved`.`id`)
FROM
`payroll_billed_units` `approved`
WHERE
`approved`.`allotment_code` = `payroll_billed_units`.`allotment_code`
AND `approved`.`category_name` = `payroll_billed_units`.`category_name`
AND `approved`.`ntp_number` = `payroll_billed_units`.`ntp_number`
AND `approved`.`activity` = `payroll_billed_units`.`activity`) AS `approved`
FROM
`payroll_billed_units`
JOIN payroll_transaction ON payroll_billed_units.billing_number =
payroll_transaction.billing_number
JOIN payroll_sub ON payroll_transaction.billing_number =
payroll_sub.billing_number
WHERE payroll_billed_units.billing_date = '2019-02-13'
AND payroll_transaction.contractor_name = 'Roy Codal' GROUP BY allotment_code, category_name, activity
I was expecting that it will load or display all my data.
The biggest problem are the dependendt sub-selects, they are responsible for a bad performance. A sub-select will be executed for EVERY ROW of the outer query. And if you cascade subs-selects, you'll quickly have a query run forever.
If any of the parts would yield only 5 resultsets, 3 sub-select would mean that the database has to run 625 queries (5^4)!
Use JOINs.
Several of your tables need this 'composite' index:
INDEX(allotment_code, category_name, ntp_number, activity) -- in any order
payroll_transaction needs INDEX(contractor_name), though it may not get used.
payroll_billed_units needs INDEX(billing_date), though it may not get used.
For further discussion, please provide SHOW CREATE TABLE for each table and EXPLAIN SELECT ...
Use simply COUNT(*) instead of COUNT(foo). The latter checks the column for being not-NULL before including it. This is usually not needed. The reader is confused by thinking that there might be NULLs.
Your GROUP BY is improper because it is missing ntp_number. Read about the sql_mode of ONLY_FULL_GROUP_BY. I bring this up because you can almost get rid of some of those subqueries.
Another issue... Because of the "inflate-deflate" nature of JOIN with GROUP BY, the numbers may be inflated. I recommend you manually check the values of the COUNTs.

how get multi count from two related table and one not related in one pdoQuery?

I'm making alot of unnecessary querys into my database what causes slow time to first Byte. I'm using PHP PDO Class Wrapper Version 1.2 (Beta).
$row = $this->baseClass->db->pdoQuery('SELECT SUM(user_have_group.user_group = 2 ) AS boosters , count(user.id ) AS created_accounts FROM `user` LEFT JOIN `user_have_group` ON user_have_group.user_id = user.id ')->result();
I manage to get counts from related tables using LEFT JOIN, but I need one more result from not related table, and I have no idea how to do it. Any advice?
I managed to make it in the following way
$row = $this->baseClass->db->pdoQuery('SELECT a.boosters,a.created_accounts,b.happy_clients from (SELECT SUM(user_have_group.user_group = 2 ) AS boosters , count(user.id ) AS created_accounts FROM user LEFT JOIN user_have_group ON user_have_group.user_id = user.id ) AS a,(SELECT SUM(boost_order.status="complete") AS happy_clients FROM boost_order) AS b')->result();
Thanks chris85 for help

update a column with a value coming from an inner join

i need to update a lot of db values, so i guess it's better to use a sql statement, maybe creating and uploading a php file and running it from time to time.
in my db i have 3 related tables, let's say
tableA_label
tableB_image
tableC_text
the relations are as follows:
tableaA_label.ImageID refers to tableB_image.ID
tableB_image.TextID refers to tableC_text.ID
my goal is:
update tableA_label.Name
tableA_label.Name = tableC_text.title
where
tableC_text.ID = tableB_image.TextID
and
tableB_image.ID = tableA_label.ImageID
.....
how can accomplish this using an sql statement?
thank you for supporting
Try this query:
UPDATE tableA_label SET
tableA_label.Name = (SELECT TableC_text.title FROM TableC_text INNER
JOIN TableB_image ON TableB_image.TextID = TableC_text.ID
WHERE TableB_image.ID = tableA_label.imageID)

Retrieving a table alias

I have several queries that have a same filter.
For example:
Filter:
$criteria = "AND id_student = 1 AND id_major = 2 ";
Query 1:
$query1 = "SELECT * FROM student AS t0
LEFT JOIN major AS t1 ON t0.id_major = t1.id
WHERE 1=1 ";
Query 2:
$query2 = "SELECT * FROM lecturer AS t0
LEFT JOIN major AS t1 ON t0.id_major = t1.id
LEFT JOIN student AS t2 ON t2.id_major = t1.id
WHERE 1=1 "
It will be run as
$query1.$criteria
and
$query2.$criteria
As you can see, this will yield a query error, since id_major is ambiguous.
My problem is, that this query is very big in number, so I prefer not to hard-code and change the $criteria and $query individually.
I've tried to change the $criteria into
$criteria = "AND student.id_student = 1 AND major.id_major = 2 ";
But it does no better, it gave me errors of "did you mean t0", "did you mean t2", etc.
So, can I do something like, get an alias of a table? So I can put it in the $criteria
$criteria = "AND (get-alias-code).id_student = 1 AND (get-alias-code).id_major = 2 ";
Or any other method?
I am not the primary developer of this program, just a bug-fixer so I cannot change much of this program (because those queries may affect another page).
Use the aliases you have for your tables already, and set the criteria to be
$criteria = "AND t0.`id_student`='1' AND t1.`id_major`='2' ";
I strongly recommend using backtics and ticks as well, it helps avoiding funny mistakes with table or field names like 'order' :)
Then all you need to ensure is that the table that needs the criteria applied gets the correct alias every time
Edit:
Hm...alternatively, you can remove aliasing from all queries.
I honestly don't think that there's any mysql feature to get the aliases for tables. You could run a string analyser code snippet to find them (knowing all table names it may not even be hard to do) but that looks like a very ugly solution IMHO.
I don't know away to handle it on sql but you can handle it with on array for example
$alias[1]["student"] = "t0"; // it means on query 1 your alias for student is t0
now you can use this variable on your filter
$criteria = "AND ".$alias[1]["student"].".id_student = 1 AND ".$alias[1]["major"].".id_major = 2 ";
I think it's one of fast ways to change your filter without changing all of queries.
hope It helps
Wrap the whole thing in an outer query like this:
"SELECT * FROM (".$query1.") a ".$criteria;
Assuming of course that the original query didn't create multiple columns with the same name, you can now reference them without the alias.

CakePHP: add variables to query in controller

I have a controller action that uses a sql query:
$tag = $this->params['tag'];
$this->set('projects', $this->Project->query('SELECT * FROM projects INNER JOIN projects_tags ON projects.id = projects_tags.project_id INNER JOIN tags on projects_tags.tag_id = tags.id WHERE tags.tag LIKE $tag'));
As you can see at the end I want to use a where clause with the $tag variable but I'm not sure how the syntax would go. As I'm getting the error
Unknown column '$tag' in 'where clause'
Can someone steer me in the right direction?
Ta,
Jonesy
I would strongly advise you to use the Cake ORM instead of raw queries, especially if you're going to plug URL parameters into it. Conditions on HABTM tables can be tricky, but you can build your joins using Cake's ORM syntax as well!
Read the manual, section 3.7.6.9 Joining tables.
Should you want to use Cake's ORM, the following code should provide results equivalent to your raw SQL query:
$this->loadModel('ProjectsTag'); // Load the joining table as pseudo-model
// Define temporary belongsTo relationships between the pseudo-model and the two real models
$this->ProjectsTag->bindModel(array(
'belongsTo' => array('Project','Tag')
));
// Retrieve all the join-table records with matching Tag.tag values
$result_set = $this->ProjectsTag->find('all',array(
'conditions' => array('Tag.tag LIKE' => "%{$tag}%")
));
// Extract the associated Project records from the result-set
$projects = Set::extract('/Project', $result_set);
// Make the set of Project records available to the view
$this->set(compact('projects'));
in php there's a difference between single and double quotes... basically, single quotes dont evaluate the variables... use double quotes instead
And i think that LIKE will need also single quotes.. i'm not really sure
"SELECT * FROM projects INNER JOIN projects_tags ON projects.id = projects_tags.project_id INNER JOIN tags on projects_tags.tag_id = tags.id WHERE tags.tag LIKE '$tag'"
i know.. i know.. people will start talkin' about sql injection.. and the need to scape the caracters... that's another question =)
good luck!
I would at least consider using the cakephp sanitize functions on your tag strings if they are user sourced. See http://book.cakephp.org/view/1183/Data-Sanitization or if using mysql as the db at least consider using http://www.php.net/manual/en/function.mysql-escape-string.php or do something to filter your user input. But the best thing is to make use of the CakePHP orm stuff.
Modify your query as:
$this->set('projects',
$this->Project->query("SELECT * FROM projects
INNER JOIN projects_tags
ON projects.id = projects_tags.project_id
INNER JOIN tags ON projects_tags.tag_id = tags.id
WHERE tags.tag LIKE '" . $tag . "'") //problem was here
);
and it will work.

Categories