How to optimize performance of count query in symfony doctrine (mysql)? - mysql

I used to below query to count rows (~ 1M record and left join many table) :
SELECT COUNT(DISTINCT u0_.id) AS sclr_0
FROM user u0_
LEFT JOIN user_detail u1_ ON u0_.id = u1_.user_id
LEFT JOIN recruitment_info r2_ ON u0_.id = r2_.user_id
LEFT JOIN user u3_ ON u0_.master_account_id = u3_.id
LEFT JOIN applicants_partners a4_ ON u0_.id = a4_.applicant_id
LEFT JOIN partner p5_ ON a4_.partner_id = p5_.id
WHERE u0_.type <> 'PARTNER'
AND u0_.is_delete = 0
ORDER BY u0_.id DESC;
In my symfony, i got total :
$total = $queryBuilder->getQuery()->getSingleScalarResult();
It worked well but it took ~ 2.5s.
So, I would like to improve performance of it. I changed it into :
SELECT COUNT(u0_.id) AS sclr_0
FROM user u0_
LEFT JOIN user_detail u1_ ON u0_.id = u1_.user_id
LEFT JOIN recruitment_info r2_ ON u0_.id = r2_.user_id
LEFT JOIN user u3_ ON u0_.master_account_id = u3_.id
LEFT JOIN applicants_partners a4_ ON u0_.id = a4_.applicant_id
LEFT JOIN partner p5_ ON a4_.partner_id = p5_.id
WHERE u0_.type <> 'PARTNER'
AND u0_.is_delete = 0
GROUP BY u0_.id
ORDER BY u0_.id DESC;
The change here is remove DISTINCT and add GROUP BY.
Then I apply in symfony by count array result:
$result = $queryBuilder->getQuery()->getArrayResult();
$total = count($result);
So the total is correct but this time, it took ~ 20s , OMG. When I tried run only raw query in Sequel Pro tool, it only took ~ 40ms. Maybe there is a problem in getArrayResult() ? . Please help me, thank you.

As Akina mentions it in his comment, you can simplify this query by removing all the left join, the order by and even the distinct.
Your query will become something like this :
SELECT COUNT(u0_.id) AS sclr_0
FROM user u0_
WHERE u0_.type <> 'PARTNER'
AND u0_.is_delete = 0;
And yes, you have to use getSingleScalarResult() on your Doctrine Query instance.

Related

SQL Optimization - Query take 15 seconds

I have 400 rows every tables. So, I will try to relationship every tables using LEFT JOIN based on ID, but my query takes 15 seconds, and this is my query:
SELECT
sender.id AS id,
sender.letter AS letter,
sender.date AS date,
mediaseller.contract_number AS contract,
sender.company AS company,
brand.value AS brand,
sender.message_category AS message_category,
sender.message_format AS message_format,
sender.senderid AS senderid,
cpname.value AS cpname,
sid.value AS sid,
status.status AS status,
sender.remarks AS remarks,
user.name AS name,
sender.id AS download,
mediaseller.value AS mediaseller,
lob.value AS lob,
lob.subvalue AS sublob,
sms_type.value AS type_sms,
status.approval_date,
status.batch_date,
status.done_date,
status.decline_date
FROM status
LEFT JOIN sender ON status.trxid = sender.trxid
LEFT JOIN user ON status.userid = user.id
LEFT JOIN mediaseller ON sender.mediaseller = mediaseller.id
LEFT JOIN lob ON sender.industry_category = lob.id
LEFT JOIN sms_type ON sender.type_sms = sms_type.id
LEFT JOIN cpname ON sender.cpname = cpname.id
LEFT JOIN sid ON sender.trxid = sid.trxid
LEFT JOIN brand ON sender.brand = brand.id
WHERE status.hidden = 0
ORDER BY status.id DESC LIMIT 10
I hopeful is query takes one seconds :D
Please give me advice, Thankyou!
You are not filtering by anything other than the status. So try this:
FROM (SELECT s.*
FROM status s
WHERE s.hidden = 0
ORDER BY status.id DESC
LIMIT 10
) status
LEFT JOIN sender ON status.trxid = sender.trxid
LEFT JOIN user ON status.userid = user.id
LEFT JOIN mediaseller ON sender.mediaseller = mediaseller.id
LEFT JOIN lob ON sender.industry_category = lob.id
LEFT JOIN sms_type ON sender.type_sms = sms_type.id
LEFT JOIN cpname ON sender.cpname = cpname.id
LEFT JOIN sid ON sender.trxid = sid.trxid
LEFT JOIN brand ON sender.brand = brand.id
WHERE status.hidden = 0
ORDER BY status.id DESC LIMIT 10
You still need the outer ORDER BY and LIMIT, but they should be on much less data resulting in a performance improvement.
Note: I assume that you have declared all the ids as primary keys, so they have indexes.

How can i Get Better Time Result on This Mysql Query

i am Using This Mysql Query and is working Good, But i need go get a Better Time result. How can i do it?
SELECT TblExistencias.id as ID, TblExistencias.codigo as Codigo,
TblPartes.detalle as Detalle,TblPartes.neto1 as PrecioActual,
TblExistencias.Condicion_Producto as Condicion,TblCategorias.categoria as Categoria,
TblSubcategorias.subcategoria as Subcategoria, TblExistencias.costo as Costo,
TblExistencias.serial as Serial, TblExistencias.vendido as Vendido,
TblConceptosFacturas.ventaonline as VentaOnline, TblRemitos.nroremitocompleto as Remito,
TblFacturas.nrofacturacompleto as Factura, TblFacturas.fecha as FechaVenta,
TblConceptosFacturas.ventaTotUn as Venta,TblConceptosFacturas.comisionmlunit as Comision,
TblFacturas.costoenvio as Envio, if(TblExistencias.vendido =1,
TblConceptosFacturas.ventaTotUn - TblExistencias.Costo - TblConceptosFacturas.comisionmlunit - TblFacturas.costoenvio,0) as Ganancia,
TblProveedores.razonsocial as Proveedor, TblFacturasCompras.nrofacturacompleto as Compra,
TblFacturasCompras.fecha as FechaCompra, TblClientes.razonsocial as Cliente
from TblExistencias
left join TblPartes on TblExistencias.codigo = TblPartes.codigo1
left join TblRemitos on TblExistencias.id_RemitoVenta = TblRemitos.id
left join TblFacturasCompras on TblExistencias.id_factura = TblFacturasCompras.id
left join TblClientes on TblRemitos.id_cliente = TblClientes.id
left join TblFacturas on TblRemitos.id_factura = TblFacturas.id
left join TblConceptosFacturas on TblFacturas.id=TblConceptosFacturas.id_factura and TblConceptosFacturas.codigoproducto = TblExistencias.codigo
left join TblCategorias on TblCategorias.id = TblPartes.id_categoria
left join TblSubcategorias on TblPartes.id_subcategoria = TblSubcategorias.id
left join TblProveedores on TblFacturasCompras.id_proveedor = TblProveedores.id
order by comision desc
The solution is create an index on each relational columns, like:
TblFacturasCompras.id_proveedor = TblProveedores.id
Problem was in order clause, Query passed from 87 Secs to 4 Secs. Thanks for help.

mysql query very slow when adding subquery

I have below query , only 800 record taking 5 minits to run, can you some help please
SELECT
vtiger_salesorder.salesorderid,vtiger_salesorder.salesorder_no,vtiger_salesorder.sostatus,
(SELECT se.s_date
FROM
softMax_events as se
INNER JOIN vtiger_salesorder as bm ON bm.salesorderid = se.orderNum
where (bm.sostatus = 'Order' AND se.orderNum = vtiger_salesorder.salesorderid) AND se.appointTyp='60'
group by bm.salesorderid Limit 0,1) As sdate
FROM
vtiger_salesorder
Inner Join vtiger_crmentity ON vtiger_salesorder.salesorderid = vtiger_crmentity.crmid
WHERE (vtiger_salesorder.sostatus = 'Order')
and ( vtiger_crmentity.deleted<>'1')
Try this query, hope so this will help you,
SELECT vtiger_salesorder.salesorderid,vtiger_salesorder.salesorder_no,vtiger_salesorder.sostatus,se.s_date
FROM vtiger_salesorder
Inner Join vtiger_crmentity ON vtiger_salesorder.salesorderid = vtiger_crmentity.crmid
INNER JOIN softMax_events se ON se.orderNum = salesorderid
WHERE (vtiger_salesorder.sostatus = 'Order') AND/OR
se.orderNum = vtiger_salesorder.salesorderid AND se.appointTyp='60'
and ( vtiger_crmentity.deleted<>'1')
Edit:
I noticed that there is some relation between vtiger_salesorder and softMax_events and you can use a join for both table, and in this way you can remove that inner query, i tried it you may test it. this will help you for sure after a bit modification.

Mysql optimization based on explain

I have the following query that takes a really long time to execute. I need to speed it up, but I'm at a lost as to what technique to use. This is the query:
SELECT
`User`.`id`,
`User`.`username`,
`User`.`password`,
`User`.`role`,
`User`.`created`,
`User`.`modified`,
`User`.`email`,
`User`.`other_user_id`,
`User`.`first_name`,
`User`.`last_name`,
`User`.`place_id`,
`Resume`.`id`,
`Resume`.`user_id`,
`Resume`.`other_resume_id`,
`Resume`.`other_user_id`,
`Resume`.`file_extension`,
`Resume`.`created`,
`Resume`.`modified`,
`Resume`.`is_deleted`,
`Resume`.`has_file`,
`Resume`.`is_stamped`,
`Resume`.`is_active`
FROM
`streetofwalls`.`users` AS `User`
LEFT JOIN `my_database`.`attempts` AS `Attempt`
ON (`Attempt`.`user_id` = `User`.`id` AND `Attempt`.`test_id` != 5)
LEFT JOIN `my_database`.`reports` AS `Resume`
ON (`Resume`.`user_id` = `User`.`id`)
WHERE
`Attempt`.`test_id` = 8
AND `Attempt`.`score` > 60
AND `User`.`id` IN (
SELECT
`User1`.`id`
FROM
`my_database`.`users` AS User1
LEFT JOIN `my_database`.`tags_users` AS TagUser
ON (`User1`.`id`= `TagUser`.`user_id`)
LEFT JOIN `my_database`.`tags` AS Tag
ON (`TagUser`.`tag_id`= `Tag`.`id`)
WHERE `Tag`.`id` = (8) )
AND `User`.`id` NOT IN (
SELECT
`User1`.`id`
FROM
`my_database`.`users` AS User1
LEFT JOIN `my_database`.`tags_users` AS TagUser
ON (`User1`.`id`= `TagUser`.`user_id`)
LEFT JOIN `my_database`.`tags` AS Tag
ON (`TagUser`.`tag_id`= `Tag`.`id`)
WHERE `Tag`.`id` = (3) )
AND `Resume`.`has_file` = 1
GROUP BY `User`.`id`
ORDER BY `Attempt`.`score` DESC;
This query generates the following explain:
As you can see, I have several indexes on this query. At the moment only the resume table is not able to be indexed. Is is possible to index this table in the context of this query? Is there some other way to speed this query up that I have not thought of? Its prohibitively slow for its intended function and I'm out of ideas. Thank you to anyone who can help. Please let me know if any other information is needed.
try inner join instead of sub-query
it is default to guide query without running on data,but may be following the query will help you.
SELECT User.id, User.username, User.password, User.role, User.created, User.modified, User.email, User.other_user_id, User.first_name, User.last_name, User.place_id, Resume.id, Resume.user_id, Resume.other_resume_id, Resume.other_user_id, Resume.file_extension, Resume.created, Resume.modified, Resume.is_deleted, Resume.has_file, Resume.is_stamped, Resume.is_active
FROM
streetofwalls.users AS User
LEFT JOIN my_database.attempts AS Attempt ON (Attempt.user_id = User.id AND Attempt.test_id != 5)
LEFT JOIN my_database.reports AS Resume ON (Resume.user_id = User.id)
, my_database.users AS User1
LEFT JOIN my_database.tags_users AS TagUser on (User1.id= TagUser.user_id)
LEFT JOIN my_database.tags AS Tag ON (TagUser.tag_id= Tag.id)
WHERE
User.id = User1.id
AND Attempt.test_id = 8
AND Attempt.score > 60
AND Resume.has_file = 1
AND Tag.id = '8' AND Tag.id != '3'
GROUP BY User.id
ORDER BY Attempt.score DESC;

SQL query wrong result

i have this query:
SELECT `completed`.`ID` AS `ID`,`completed`.`level` AS `level`,`completed`.`completed_in` AS `completed_in`, COUNT(1) AS `right_answers_num`
FROM `completed`
INNER JOIN `history` ON `history`.`ID` = `completed`.`ID`
INNER JOIN `questions` ON `questions`.`ID` = `history`.`question`
WHERE `completed`.`student_id` = '1' AND `questions`.`answer` = `history`.`answer`
GROUP BY `completed`.`ID`
ORDER BY `completed`.`completed_in` DESC
what i need is to get info of each test in completed table (id,level,completed_in,right_answer_num)
the problem with that query is that if there is no one right answer(history.answer = questions.answer) then it doesn't return the row, while it should return the row(id,level,completed_in) and the right_answer_num(counter) should be zero..
please help me,, thanks ahead.
SELECT
completed.ID AS ID,
completed.level AS level,
completed.completed_in AS completed_in,
COUNT(questions.answer) AS right_answers_num
FROM completed
INNER JOIN history ON history.ID = completed.ID
LEFT JOIN questions ON questions.ID = history.question AND questions.answer = history.answer
WHERE
completed.student_id = '1'
GROUP BY
completed.ID
ORDER BY completed.completed_in DESC
use a LEFT OUTER JOIN intead of an INNER JOIN.
The second inner join is what's causing rows with no record in the questions table to be omitted. An inner join will only return rows that have data in all corresponding tables. Change the second inner join to a left join like so:
SELECT
completed.ID AS ID,
completed.level AS level,
completed.completed_in AS completed_in,
COUNT(questions.answer) AS right_answers_num
FROM completed
INNER JOIN history ON history.ID = completed.ID
LEFT JOIN questions ON questions.ID = history.question
WHERE completed.student_id = 1
GROUP BY completed.ID
ORDER BY completed.completed_in DESC