MYSQL LEFT JOIN optimization with CASE - mysql

I spent some time trying to get working this SELECT with CASE but I failed... (thank to that I'm using COLASCE() now)
How could I optimize this SELECT by using CASE/IF sentences? Is this a fast way to query from different tables selected by a field?
SELECT a.folderid, a.foldername, a.contenttype, COALESCE(b.descriptor, c.descriptor, d.descriptor, e.descriptor, f.descriptor) as descriptor
FROM t_folders a
LEFT JOIN t_files b
ON a.contenttype = 'file' AND a.contentid = b.fileid
LEFT JOIN t_links c
ON a.contenttype = 'link' AND a.contentid = c.linkid
LEFT JOIN t_extfiles d
ON a.contenttype = 'extfile' AND a.contentid = d.extfileid
LEFT JOIN t_videos e
ON a.contenttype = 'video' AND a.contentid = e.videoid
LEFT JOIN t_exams f
ON a.contenttype = 'exam' AND a.contentid = f.examid
WHERE a.folderid = $folderId
ORDER BY a.folderid DESC

Using case statement will not make the query faster in your case, but since you asked for it, below is how it would look like.
SELECT a.folderid, a.foldername, a.contenttype,
(CASE a.contenttype
WHEN 'file' THEN b.descriptor
WHEN 'link' THEN c.descriptor
WHEN 'extfile' THEN d.descriptor
WHEN 'video' THEN e.descriptor
ELSE f.descriptor
END CASE) AS descriptor
FROM t_folders a
LEFT JOIN t_files b ON a.contenttype = 'file' AND a.contentid = b.fileid
LEFT JOIN t_links c ON a.contenttype = 'link' AND a.contentid = c.linkid
LEFT JOIN t_extfiles d ON a.contenttype = 'extfile' AND a.contentid = d.extfileid
LEFT JOIN t_videos e ON a.contenttype = 'video' AND a.contentid = e.videoid
LEFT JOIN t_exams f ON a.contenttype = 'exam' AND a.contentid = f.examid
WHERE a.folderid = $folderId
ORDER BY a.folderid DESC
If each of the t_files, t_links, etc tables has the folder_id field, I would also try doing a UNION on these tables and then left join the result with t_folders to get the folderid and foldername.

The joins have to be done this way because the come out of different tables. You cannot use CASE to switch which table a query is coming out of: the statement has to be parsed, including its data sources, before there are values to compare.
More to the point, CASE returns values, whereas table names are.. I don't know the technical term.. structural components of the query. It's sort of the same reason you can't select out of table "Cool_Stuff" with:
select * from "Cool_" + "Stuff"
Hope this answers your question!

You may do the following
select master.* , COALESCE(b.descriptor, c.descriptor, d.descriptor, e.descriptor, f.descriptor) as descriptor
from
( SELECT a.folderid, a.foldername, a.contenttype
FROM t_folders a
WHERE a.folderid = $folderId
ORDER BY a.folderid DESC ) master
LEFT JOIN t_files b
ON master.contenttype = 'file' AND master.contentid = b.fileid
LEFT JOIN t_links c
ON master.contenttype = 'link' AND master.contentid = c.linkid
LEFT JOIN t_extfiles d
ON master.contenttype = 'extfile' AND master.contentid = d.extfileid
LEFT JOIN t_videos e
ON master.contenttype = 'video' AND master.contentid = e.videoid
LEFT JOIN t_exams f
ON master.contenttype = 'exam' AND master.contentid = f.examid
While minimizing the result on table a, the joins operation can be optmizsed

Related

GROUP BY with HAVING but unrecognized table in MySQL

I have this query, and I am trying to split the answer column RES (that is divided by commas) into more rows (instead). But when I try to do that, the HAVING part is not working at all, and it's throwing: Unknow column.
This is the original query:
SELECT ad.ID_ANSWER as idRes
, IFNULL(c.NOMBRE_FACTURA,a.INTERVIEWED_NAME) AS nomPdv
, c.CODIGO
, q.`NAME` AS nombreEncuesta
, ad.ID_QUESTION AS idCue
, que.QUESTION
, GROUP_CONCAT(ad.ANSWER) AS RES
, a.LATITUDE
, a.LONGITUDE
, cm.DIRECCION
, c.NIT
, cm.NOMREPRESENTANTE
, cm.FECHA_NACIMIENTO
, a.ID_QUESTIONARY
, cm.TELEFONO, cm.FECHA_NACIMIENTO, cm.NITREPRESENTANTE,
CONCAT(cm.APELLIDO_PATERNO,' ',cm.APELLIDO_MATERNO,' ', cm.NOMBRES_CLIENTE) as RAZONSOCIAL, a.USUARIO_ID, a.FECHA_ID, R.NOMBRE AS RUTA, CASE WHEN D.NOMBRE IS NULL THEN DP.NOMBRE ELSE D.NOMBRE END AS DEPARTAMENTO, P.NOMBRE AS ENCUESTADOR
FROM VM_ANSWER AS a
JOIN VM_ANSWER_DETAIL ad
ON ad.ID_ANSWER = a.ID_ANSWER
JOIN VM_QUESTIONARY AS q
ON q.ID_QUESTIONARY = a.ID_QUESTIONARY
JOIN VM_QUESTION AS que
ON que.ID_QUESTION = ad.ID_QUESTION
JOIN VM_QUESTIONARY_RANGE AS qr
on qr.ID_QUESTIONARY = a.ID_QUESTIONARY
AND qr.OPERACION_ID = 1
JOIN AD_USUARIO U
ON U.USERNAME = a.USUARIO_ID
JOIN GL_PERSONA P
ON P.ID_PERSONA = U.ID_PERSONA
LEFT
JOIN (SELECT * FROM VP_VENDEDOR V GROUP BY V.ID_PERSONA) V ON V.ID_PERSONA = U.ID_PERSONA
LEFT JOIN VP_SUCURSAL S ON S.ID_SUCURSAL = V.ID_SUCURSAL
LEFT JOIN GL_DEPARTAMENTO D ON D.ID_DEPARTAMENTO = S.ID_DEPARTAMENTO
LEFT JOIN VP_CLIENTE AS c ON c.ID_CLIENTE = a.ID_CLIENT
LEFT JOIN VM_CLIENTE_MOVIL AS cm ON cm.ID_CLIENTE = c.ID_CLIENTE
LEFT JOIN GL_DEPARTAMENTO DP ON DP.ID_DEPARTAMENTO = cm.ID_CIUDAD
LEFT JOIN VM_RUTA_VENDEDOR RV ON RV.ID_VENDEDOR = V.ID_VENDEDOR AND RV.OPERACION_ID > 0
LEFT JOIN VM_RUTA R ON R.ID_RUTA = RV.ID_RUTA
WHERE a.OPERACION_ID = 1 AND qr.ID_BRANCH = 3 AND IF (61 = 0, TRUE, a.ID_QUESTIONARY = 61)
GROUP BY a.ID_QUESTIONARY,a.ID_ANSWER
ORDER BY a.ID_QUESTIONARY,a.ID_ANSWER
When I add the following code
LEFT JOIN GL_TIPO T ON T.ID_TIPO = que.ID_TYPE
WHERE a.OPERACION_ID = 1 AND qr.ID_BRANCH = 3 AND IF (61 = 0, TRUE, a.ID_QUESTIONARY = 61)
GROUP BY a.ID_QUESTIONARY,a.ID_ANSWER, ad.ID_ANSWER_DETAIL HAVING T.ID_TIPO=1012
ORDER BY a.ID_QUESTIONARY,a.ID_ANSWER
throws me the error of not recognized table T.ID_TIPO, but if I take off the HAVING word, the left join works normally.
this is the normal result, that as you can see in the column RES, it's divided by a comma when there is more than an answer:
I know it's complicated to understand, 1012 is a multi-select answer type from table GL_TIPO, and it's used in the table Question to define the type of it.
If you can help me to understand what I am doing wrong with the HAVING reserved word it would be awesome, but if you have any suggestion is also welcome too.
Try this:
GROUP BY a.ID_QUESTIONARY,a.ID_ANSWER, que.ID_TIPO=1012
This will put the questions with ID_TIPO=1012 into its own row, and all other ID_TIPO values will be grouped together. Both of these are nested within the ID_QUESTIONARY, ID_ANSWER groups.

Mysql adding index for column type text does not improve performance while using select query

I am using InnoDB. From this question, I found out that I have to specify the length if I want to add index to columns which type is TEXT.
But after successfully adding index, the performance for the select query stay the same. Anyone know why? I did check the index with show index from tableName and the index did exist.
So it was the last two table EventResultsFinalSummary and EventResultsPrelims.
CREATE OR REPLACE VIEW ScheduleView AS
SELECT s.ScheduleID, e.EventRound, e.EventRoundsID, e.EventID, e.NumberCouplesInRound, n.NumberOnBack, eic.EventName AS 'Division',
CONCAT(a1.FirstName, ' ', a1.LastName, ' - ', a2.FirstName, ' ', a2.LastName) AS 'Couple',
s.SessionID AS 'Session', erfs.CouplePlace, c.CoupleID,
s.Timeslot, s.SubFloor ,s.itemDuration,s.HeatNumber, o.ActivityName, st.StudioName AS 'DanceStudio', a.AgeName AS 'Age', s.competition_id, erp.CoupleVotes
FROM Schedule AS s
LEFT JOIN EventRounds AS e ON s.EventRoundID = e.EventRoundsID AND s.competition_id = e.competition_id
LEFT JOIN OtherActivities AS o ON s.OtherActivitiesID = o.OtherActivitiesID AND s.competition_id = o.competition_id
LEFT JOIN EntriesEvents AS ee ON e.EventID = ee.EventID AND e.EventRound <= ee.EventRound AND e.Competition_id = ee.Competition_id
LEFT JOIN Couples AS c ON ee.EntryID = c.CoupleID AND ee.Competition_id = c.Competition_id
LEFT JOIN NumSysComps AS n ON c.CompetitorIDMan = n.CompetitorIDMan AND c.Competition_id = n.Competition_id
LEFT JOIN Attendees AS a1 ON c.CompetitorIDMan = a1.AttendeeID AND c.Competition_id = a1.Competition_id
LEFT JOIN Attendees AS a2 ON c.CompetitorIDLady = a2.AttendeeID AND c.Competition_id = a2.Competition_id
LEFT JOIN Studios AS st ON a1.StudioID = st.StudioID AND a1.Competition_id = st.Competition_id
LEFT JOIN EventsInComp AS eic ON eic.EventID = e.EventID AND eic.Competition_id = e.Competition_id
LEFT JOIN ProAmSingleDanceEvents AS psd ON eic.ProAmSingleDanceEventID = psd.ProAmSingleDanceEventID AND eic.Competition_id = psd.Competition_id
LEFT JOIN ProAmMultiDanceEvents AS pmd ON eic.ProAmMultiDanceEventID = pmd.ProAmMultiDanceEventID AND eic.Competition_id = pmd.Competition_id
LEFT JOIN Ages AS a ON (
psd.AgeID = a.AgeID AND psd.Competition_id = a.Competition_id
OR
pmd.AgeID = a.AgeID AND pmd.Competition_id = a.Competition_id
)
LEFT JOIN EventResultsFinalSummary AS erfs ON e.EventID = erfs.EventID AND c.CoupleID = erfs.CoupleID AND s.Competition_id = erfs.Competition_id
LEFT JOIN EventResultsPrelims AS erp ON e.EventID = erp.EventID AND erp.EventRound = e.EventRound AND c.CoupleID = erp.CoupleID AND s.Competition_id = erp.Competition_id
ORDER BY s.ScheduleID;
I added index to the column that I joined.
EventID, CoupleId, Competition_id for EventResultsFinalSummary and EventID, EventRound, Competition_id for EventResultsPrelims by using query like following.
My question is that when those columns have type like varchar or int, the select * query will only take 1s. But it take 26s when the type is Text.
ALTER TABLE `EventResultsPrelims` ADD INDEX(`EventID`(6));

mysql: inner query is not working inside selectif statement

I want result for below query be Y if Email is blacklisted, else run the inner query and return the specific facility Name.
Select if (EmailBlacklisted = 'Y', 'Y',
'select a.facilityname from facility a
inner join domainfacility b on a.facility_ID=b.facility_facility_ID
inner join domain c on b.domain_Domain_ID = c.Domain_ID
where a.facilitystatus="A" and c.DomainName = "cd.com" ')
from domain where DomainName= 'cd.com';
BUT query returning same inner query as RESULT if emailBlacklisted is N in Domain table. please help
Normally, you would approach this just by returning the facility names:
select f.facilityname
from facility f inner join
domainfacility df
on a.facility_ID = b.facility_facility_ID inner join
domain d
on df.domain_Domain_ID = d.Domain_ID
where f.facilitystatus = 'A' and d.DomainName = 'cd.com'
where d.DomainName = 'cd.com' and d.EmailBlacklisted <> 'Y';
Then, if this returns nothing, that would suggest being blacklisted. You could just return Y for such names:
select (case when d.EmailBlacklisted = 'Y' then 'Y' else f.facilityname end)
from facility f inner join
domainfacility df
on a.facility_ID = b.facility_facility_ID inner join
domain d
on df.domain_Domain_ID = d.Domain_ID
where f.facilitystatus = 'A' and d.DomainName = 'cd.com'
where d.DomainName = 'cd.com';
But that seems a strange confusion of mixing names with flags.
Try this:
SELECT IF(d.EmailBlacklisted = 'Y', 'Y', a.facilityname)
FROM domain d
LEFT OUTER JOIN domainfacility b ON b.domain_Domain_ID = d.Domain_ID
LEFT OUTER JOIN facility a ON b.facility_facility_ID = a.facility_ID AND a.facilitystatus = 'A'
WHERE d.DomainName= 'cd.com';

IF/ELSE LEFT JOIN

I need to do a LEFT JOIN with IF/ELSE, this is my query:
IF (M.idArtVar=null,
LEFT JOIN ArtMaga G
ON (G.idMagazzino = V.idMagazzino AND G.idArticolo = M.idArticolo),
LEFT JOIN ArtMaga G
ON (G.idMagazzino = V.idMagazzino AND G.idArticolo = M.idArticolo AND
G.idArtVar = M.idArtVar)
)
But it doesn't work.
I also tried like this:
LEFT JOIN ArtMaga AM
ON IF(M.idArtVar IS NULL,
(AM.idMagazzino = TM.idMagazzino AND AM.idArticolo = A.idArticoli),
(AM.idMagazzino = TM.idMagazzino AND AM.idArtVar = M.idArtVar))
But this query is too slow.
How can I do?
Thanks.
EDIT: This is full query:
SELECT F.Codice AS "CodiceFornitore", F.RagioneSociale AS "RagioneSocialeFornitore", A.ArticoloFornitore, C.Descrizione AS CatDes, S.Descrizione AS Settore, U.Sigla AS Um, U2.Sigla AS Um2, A.Moltiplicatore AS Molt, A.Collo, TM.
dMagazzino, M.idArtVar, AM.Esistenza, AM.Disponibilita, AM.QtaImpegnata, AM.QtaOrdinata, TM.TipoSoggetto, TM.idSoggetto, ST.DataMovimento, MC.Codice, ST.Quantita, ST.Prezzo, ST.Sconti, M.idMagaRigMov
FROM MagaRigMov M
LEFT JOIN Articoli A ON A.idArticoli = M.idArticolo
LEFT JOIN UnMisura U ON U.idUnMisura = A.idUnMisura1
LEFT JOIN UnMisura U2 ON U2.idUnMisura = A.idUnMisura2
LEFT JOIN Iva I ON I.idIva = A.idIva
LEFT JOIN Settori S ON S.idSettori = A.idSettore
LEFT JOIN Fornitori F ON F.idFornitori = A.idFornitore
LEFT JOIN ArtCategorie C ON C.idArtCategorie = A.idArtCategoria
LEFT JOIN MagaTesMov TM ON TM.idMagaTesMov = M.idMagaTesMov
LEFT JOIN STORICO ST ON (ST.idSoggetto = TM.idSoggetto AND ST.TipoSoggetto = TM.TipoSoggetto AND ST.idArticolo = M.idArticolo)
LEFT JOIN MagaCausali MC ON MC.idMagaCausali = ST.idMagaCausale
LEFT JOIN ArtMaga AM ON IF(M.idArtVar IS NULL,(AM.idMagazzino = TM.idMagazzino AND AM.idArticolo = A.idArticoli),
(AM.idMagazzino = TM.idMagazzino AND AM.idArtVar = M.idArtVar))
This query is too slow.. but works..
You can't use an IF to make a conditional join. Because IF is not part of the SELECT syntax and even if it was (like CASE expressions) it wouldn't be allowed to be used like this. You can move the logic to the ON statement though:
LEFT JOIN ArtMaga G
ON (G.idMagazzino = V.idMagazzino AND G.idArticolo = M.idArticolo)
AND M.idArtVar IS NULL
OR (G.idMagazzino = V.idMagazzino AND G.idArticolo = M.idArticolo AND
G.idArtVar = M.idArtVar)
AND M.idArtVar IS NOT NULL
which can be simplified to:
LEFT JOIN ArtMaga G
ON (G.idMagazzino = V.idMagazzino AND G.idArticolo = M.idArticolo)
AND (M.idArtVar IS NULL OR G.idArtVar = M.idArtVar)
Also notice that you can't use equality to check if an expression is null.
M.idArtVar = null will never be true because NULL can never be equal to anything (not even to NULL). The way to check if an expression is null is with IS NULL.
Your second query, that words, is using the IF() function of MySQL and seems to be correct (although I see a difference in the code with the first query, the G.idArticolo = M.idArticolo condition has been removed from one part.)
Why a query is slow depends on many factors and using functions on the join conditions can be one of the many. Try the change I suggest above. If it still slow, you'll have to examine the execution plan and the available indexes on the tables.
Just put the condition in the on clause. If is not part of a SQL statement.
SELECT F.Codice AS "CodiceFornitore", F.RagioneSociale AS "RagioneSocialeFornitore",
A.ArticoloFornitore, C.Descrizione AS CatDes, S.Descrizione AS Settore, U.Sigla AS Um, U2.Sigla AS Um2, A.Moltiplicatore AS Molt, A.Collo, TM.
dMagazzino, M.idArtVar, AM.Esistenza, AM.Disponibilita, AM.QtaImpegnata, AM.QtaOrdinata, TM.TipoSoggetto, TM.idSoggetto, ST.DataMovimento, MC.Codice, ST.Quantita, ST.Prezzo, ST.Sconti, M.idMagaRigMov
FROM MagaRigMov M
LEFT JOIN Articoli A ON A.idArticoli = M.idArticolo
LEFT JOIN UnMisura U ON U.idUnMisura = A.idUnMisura1
LEFT JOIN UnMisura U2 ON U2.idUnMisura = A.idUnMisura2
LEFT JOIN Iva I ON I.idIva = A.idIva
LEFT JOIN Settori S ON S.idSettori = A.idSettore
LEFT JOIN Fornitori F ON F.idFornitori = A.idFornitore
LEFT JOIN ArtCategorie C ON C.idArtCategorie = A.idArtCategoria
LEFT JOIN MagaTesMov TM ON TM.idMagaTesMov = M.idMagaTesMov
LEFT JOIN STORICO ST ON (ST.idSoggetto = TM.idSoggetto AND ST.TipoSoggetto = TM.TipoSoggetto AND ST.idArticolo = M.idArticolo)
LEFT JOIN MagaCausali MC ON MC.idMagaCausali = ST.idMagaCausale
LEFT JOIN ArtMaga AM ON (AM.idMagazzino = TM.idMagazzino AND (m.idartVar is NULL and AM.idArtVar = M.idArtVar or AM.idArticolo = A.idArticoli))

SQL calculating time between assignments

I have to write an SQL statement which contain a field that contain two different values consecutively but in the way I have wrote it, it return always null because it is interpreted as having the two value in the same time!
My conditions should be : (ci.field = 'Group' and ci.oldString = 'Triage' ) and (ci.field='assignee' and ci.newString is not NULL)
That means calculate time between: when the issue is assigned to group named Triage and when the issue is assigned to a person.
How can I fix it?
My SQL statement:
select TIMEDIFF(a.created,b.created)
from
(select g.created, g.issueid as groupid1
from changegroup g
join changeitem ci on (ci.groupid = g.id)
join jiraissue ji on (ji.id = g.issueid)
join project p on (p.id = ji.project)
join priority pr on (pr.id = ji.priority)
where ci.field = 'Group'
and ci.oldString = 'Triage'
and ci.field='assignee'
and ci.newString is not NULL
and p.pname = 'Test'
and pr.pname='P1'
and ji.created between '2011-08-11 14:01:00' and '2011-08-12 14:11:00'
) a
left join (
select ji.created, ji.id as groupid2
from jiraissue ji
join changegroup g on (g.issueid = ji.id)
join project p on (p.id = ji.project)
where p.pname = 'Test'
and ji.created between '2011-08-11 14:01:00' and '2011-08-12 14:11:00'
) b ON (a.groupid1 = b.groupid2);
This is the table from which I should retrieve data
See my comment about the quality of your question but a hint at how to solve this goes like (assuming you can make sure this doesn't create 1-n joins)
select groupid_orsomething_else, TIMEDIFF(a.created, b.created)
from yourtable
left join
(select groupid_orsomething_else, created
from yourtable
where field = 'Group' and oldstring is 'Triage'
) a
on a.groupid_orsomething_else = yourtable.groupid_orsomething_else
left join
(select groupid_orsomething_else, created
from yourtable
where field = 'assignee' and oldstring is null) b
on b.groupid_orsomething_else = yourtable.groupid_orsomething_else