SQL Optimization - Query take 15 seconds - mysql

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.

Related

MYSQL Inner Join with IF Statement

I'm trying to join several tables in my database.
I need to get account information from the 'accounts' table with the latest meter history on it.
And if an account has no meter history, I want it to show 'meter' related fields as NULL.
Here's my query so far:
SELECT
accounts.id,
accounts.account_order,
acc.id AS accounts_class_id,
acc.zone,
acc.book,
acc.service_class,
acc.size,
acc.account_no AS series_no,
accounts.status,
application_address.address_line,
concessionaires.firstname,
concessionaires.middlename,
concessionaires.lastname,
mb.brand_name,
m.meter_no,
ms.meter_status
FROM
accounts
INNER JOIN
applications
ON accounts.application_id = applications.id
LEFT JOIN
application_address
ON applications.application_no = application_address.application_no
LEFT JOIN
concessionaires
ON applications.concessionaire_no = concessionaires.concessionaire_no
INNER JOIN
accounts_classifications acc
ON accounts.id = acc.account
INNER JOIN meter_history mh
ON mh.id = (SELECT id FROM meter_history mh2
WHERE mh2.account_id = accounts.id
ORDER BY mh2.status_date DESC
LIMIT 1)
LEFT JOIN
meter_status ms
ON mh.meter_status = ms.id
INNER JOIN
meter m
ON mh.meter = m.id
LEFT JOIN
meter_brand mb
ON m.meter_brand = mb.id
WHERE
acc.book = 1 AND acc.zone = 20 AND applications.status = '6' AND acc.status = '1'
This would return only accounts with meter history on it.
Where should I put my IF condition so I get accounts with no history as well, or if that is even possible with my query. Thank you!

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 LEFT JOINing the latest record from another table

I have a training_stats table (current due training) and I also have a completed_training table.
What I want to do is query due training with the last completed date from the completed table.
I've nearly got what I want, I get the due training, but they are duplicated with each completed record(as there are many completed records to each current due), and I only want single rows and the latest completed date.
I've been trying to use MAX, and when I run the MAX query independently, I get the last record. But when the MAX query is in the join, it is returning all completed rows.
This is the query that I am using:
SELECT s.course_stat_id
,o.org_name
,u.id
,u.first_name
,u.last_name
,a.area_id
,a.area_name
,tc.course_id
,tc.course_name
,s.assigned_on
,s.due
,s.pass_mark
,s.completed_on
,completed.complete_training_id
,completed.complete_date
FROM training_stats s
JOIN organisations o ON o.org_id = s.org_id
LEFT JOIN (
SELECT complete_training_id
,user_id
,area_id
,course_id
,max(completed_on) AS complete_date
FROM completed_training
GROUP BY complete_training_id
) completed ON completed.user_id = s.user_id
AND completed.area_id = s.area_id
AND completed.course_id = s.course_id
LEFT JOIN users u ON u.id = s.user_id
LEFT JOIN areas a ON a.area_id = s.area_id
LEFT JOIN training_courses tc ON tc.course_id = s.course_id
WHERE u.active = 1
AND o.active = 1
AND s.assigned = 1
Can you see what I am doing wrong?
Not exactly positive of your expected results, but the failure is PROBABLY for your group by and JOIN. Your group by is ONLY on the training ID, but you are also pulling user, area and course as well as max date completed for said respective training ID, user, area, course. You group by and join should match the unique characteristics.
Without seeing data, the query as I interpret it is that the "complete_training_id" is an auto-increment column for that table. Having said that, there would only ever be one record for that ID.
Having said that, the completed training table can have for a single user, area and course, multiple training days of which you want the most recent. For example someone attending college and needs to take many computer classes and they are refreshers from prior so assume all are same course ID. A person could take in 2012, 2014, 2016. You would want the instance of the user/area/course showing the 2016 dated training. So lets look at that first.
select
ct.user_id,
ct.area_id,
ct.course_id,
max(ct.completed_on) AS complete_date
FROM
completed_training ct
GROUP BY
ct.user_id,
ct.area_id,
ct.course_id
Now, for each user, area and course of study, I have one record with the most recent completion date. NOW lets pull the rest of the details, but since you need the completed training ID too, I applied the MAX() of that in the query below. The ID should by default be increasing every time a new record is added, so one completed a year ago would have a lower value than the ID completed today. So you get both the completed ID and its corresponding date for a given user, area, course.
SELECT
s.course_stat_id,
o.org_name,
u.id,
u.first_name,
u.last_name,
a.area_id,
a.area_name,
tc.course_id,
tc.course_name,
s.assigned_on,
s.due,
s.pass_mark,
s.completed_on,
ct.complete_training_id,
ct.complete_date
FROM
training_stats s
JOIN organisations o
ON s.org_id = o.org_id
AND o.active = 1
LEFT JOIN
( select
ct.user_id,
ct.area_id,
ct.course_id,
max(ct.complete_training_id ) as complete_training_id,
max(ct.completed_on) AS complete_date
FROM
completed_training ct
GROUP BY
ct.user_id,
ct.area_id,
ct.course_id ) ct
on s.user_id = ct.user_id
AND s.area_id = ct.area_id
AND s.course_id = ct.course_id
JOIN users u
ON s.user_id = u.id
AND u.active = 1
LEFT JOIN areas a
ON s.area_id = a.area_id
LEFT JOIN training_courses tc
ON s.course_id = tc.course_id
WHERE
s.assigned = 1
I'm not 100% sure of that. First, run this query. It should list all completed training, with a rnk from 1 (lastest), to n (oldest).
SELECT complete_training_id
,user_id
,area_id
,course_id
,completed_on AS complete_date
,#curRank := case when complete_training_id <> #cur_complete_training_id then 0 else #curRank + 1 end rnk
FROM completed_training, (select #curRank := 0, #cur_complete_training_id := 0)
ORDER BY complete_training_id, completed_on DESC
If true, the answer is :
SELECT s.course_stat_id
,o.org_name
,u.id
,u.first_name
,u.last_name
,a.area_id
,a.area_name
,tc.course_id
,tc.course_name
,s.assigned_on
,s.due
,s.pass_mark
,s.completed_on
,completed.complete_training_id
,completed.complete_date
FROM training_stats s
JOIN organisations o ON o.org_id = s.org_id
LEFT JOIN (
SELECT complete_training_id
,user_id
,area_id
,course_id
,completed_on AS complete_date
,#curRank := case when complete_training_id <> #cur_complete_training_id then 0 else #curRank + 1 end rnk
FROM completed_training, (select #curRank := 0, #cur_complete_training_id := 0)
ORDER BY complete_training_id, completed_on DESC
) completed ON completed.user_id = s.user_id and completed.rnk = 1
AND completed.area_id = s.area_id
AND completed.course_id = s.course_id
LEFT JOIN users u ON u.id = s.user_id
LEFT JOIN areas a ON a.area_id = s.area_id
LEFT JOIN training_courses tc ON tc.course_id = s.course_id
WHERE u.active = 1
AND o.active = 1
AND s.assigned = 1

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

sql query that went it runs it repeats the same result by the number of users

Hi hope someone can advise
I have a sql query that went it runs it repeats the same result by the number of users (entries) made
for example:
post_id
20
20
19
19
18
18
Here is the query it takes no arguments, could someone explain why this is happening please?
Thanks
SELECT DISTINCT post_look.post_id, post_look.look_id, post_look.date_posted, looks.title, looks.item_id, user.user_id, user.first_name, user.last_name, user_account.profile_image, user_account.account_status, add_profile_images.image_name
FROM post_look
JOIN looks ON looks.look_id = post_look.look_id
JOIN add_look_item ON add_look_item.look_id = looks.look_id
JOIN item ON item.item_id = add_look_item.item_id
JOIN add_images ON add_images.item_id = item.item_id
JOIN user_item ON user_item.item_id = item.item_id
JOIN user_account ON user_account.account_id = user_item.account_id
JOIN user ON user.user_id = user_account.user_id
JOIN users_profile_images ON users_profile_images.account_id = user_account.account_id
JOIN add_profile_images ON add_profile_images.image_id = users_profile_images.image_id
ORDER BY post_look.post_id DESC
LIMIT 0 , 30
SELECT DISTINCT c1, c2, c3
means that {c1, c2, c3} will be distinct.
You should consider adding GROUP BY post_look.post_id to your query.
See the difference here:
DISTINCT
GROUP BY
Would you please try it?
SELECT post_look.post_id, post_look.look_id, post_look.date_posted, looks.title, looks.item_id, user.user_id, user.first_name, user.last_name, user_account.profile_image, user_account.account_status, add_profile_images.image_name
FROM post_look
JOIN looks ON looks.look_id = post_look.look_id
JOIN add_look_item ON add_look_item.look_id = looks.look_id
JOIN item ON item.item_id = add_look_item.item_id
JOIN add_images ON add_images.item_id = item.item_id
JOIN user_item ON user_item.item_id = item.item_id
JOIN user_account ON user_account.account_id = user_item.account_id
JOIN user ON user.user_id = user_account.user_id
JOIN users_profile_images ON users_profile_images.account_id = user_account.account_id
JOIN add_profile_images ON add_profile_images.image_id = users_profile_images.image_id
GROUP BY post_look.post_id
ORDER BY post_look.post_id DESC
LIMIT 0 , 30