I have the following SQL query that is bottlenecking at some point. It will make it halfway through around 900 records it returns before timing out. When I change my second select to only return certain columns it helped a bit but it is still bottlenecking somewhere.
I'm thinking there is something wrong with the way that I have the JOINs written but could use any advice.
SELECT * FROM
(SELECT * FROM TABLE1 B
INNER JOIN TABLE2 A ON B.ID_NBR = A.ID_NBR
INNER JOIN TABLE3 L ON A.CUST_NUM = L.CUST_NUM
WHERE B.SUPP_NBR = '17' AND STAT_NUM <> 4 AND BGHT_ID is not null
AND
NOT EXISTS (SELECT 1 FROM TABLE4 A WHERE A.ID_NBR = B.ID_NBR)) APPL
INNER JOIN TABLE5 ON M.PRSN_NUM = PRSN_NUM
WHERE M.LOC_ID = 'US' and (APPL.STAT_CD <> 'S' OR (APPL.STAT_CD =
'S' AND M.TXT_LOC !=
'UNKNOWN'));
UPDATE: I ended up using a LEFT JOIN instead of NOT EXISTS, changed the SELECT *, and used GROUP BY to correct the indexing issue:
SELECT * FROM
(SELECT B.ID_NBR, B.SUPP_NBR, B.STAT_NUM, B.BGHT_ID, A.CUST_NUM,
L.CUST_NUM, L.STAT_CD FROM TABLE1 B
INNER JOIN TABLE2 A ON B.ID_NBR = A.ID_NBR
INNER JOIN TABLE3 L ON A.CUST_NUM = L.CUST_NUM
LEFT JOIN TABLE4 C ON C.ID_NBR = B.ID_NBR
WHERE C.ID_NBR is null AND B.SUPP_NBR = '17' AND B.STAT_NUM <> 4
AND B.BGHT_ID is not null
GROUP BY B.ID_NBR, B.SUPP_NBR, B.STAT_NUM, B.BGHT_ID, A.CUST_NUM,
L.CUST_NUM, L.STAT_CD)
APPL
INNER JOIN TABLE5 ON M.PRSN_NUM = PRSN_NUM
WHERE M.LOC_ID = 'US' and
(APPL.STAT_CD <> 'S' OR M.TXT_LOC != 'UNKNOWN');
Can't this
(APPL.STAT_CD <> 'S'
OR (APPL.STAT_CD = 'S' AND M.TXT_LOC != 'UNKNOWN')
)
be simplified to
(APPL.STAT_CD <> 'S' OR M.TXT_LOC != 'UNKNOWN')
(It probably won't speed up the query much.)
Some of these indexes may help:
M: INDEX(LOC_ID, TXT_LOC, PRSN_NUM)
APPL: INDEX(STAT_CD)
B: INDEX(SUPP_NBR, ID_NBR)
A: INDEX(ID_NBR, CUST_NUM)
L: INDEX(CUST_NUM)
Related
Am trying to convert this Sql server script to mysql.
SELECT A.*
FROM TableA A
CROSS APPLY
(
SELECT TOP 1 UID
FROM TableB
WHERE BetID = A.BetID
AND BETCODE = A.BETCODE
ORDER BY CASE WHEN InfoCode > '' THEN 0 ELSE 1 END,UID
)Z
WHERE Z.UID = A.UID
Can please advise as how, I know I can change CROSS JOIN and Limit 1 but still it fails
in you can't use TOP but use limit so you could try and in MySql the scope for subquery table is differente respect to SQL_SERVER so you must buil the join inside the subquery
SELECT A.*
FROM TableA A
CROSS JOIN
( SELECT UID
FROM TableB
INNER JOIN TABLEA A ON BetID = A.BetID
AND BETCODE = A.BETCODE
ORDER BY CASE WHEN InfoCode > '' THEN 0 ELSE 1 END,UID
LIMIT 1
) Z
WHERE Z.UID = A.UID
or a cross join with a single result could be also translate as an inner join
SELECT A.*
FROM TableA A
INNER JOIN
( SELECT UID
FROM TableB
INNER JOIN TABLEA A ON BetID = A.BetID
AND BETCODE = A.BETCODE
ORDER BY CASE WHEN InfoCode > '' THEN 0 ELSE 1 END,UID
LIMIT 1
) Z ON Z.UID = A.UID
I don't know why my case code is having an error but when I execute it not using a parameter by replacing the #PersonId by 2 it runs well. I try to exchange2x the JOIN but I am still having an error. Anyone knows?.
case when ISNULL(dbo.EducationalBackground.SchoolId,'') = '' then
(Select a.SchoolName
from dbo.EducationalBackground as A INNER JOIN
dbo.PersonEducationalBackground as B on a.EducationalBackgroundId = b.EducationalBackgroundId INNER JOIN
dbo.EducationLevel as C on a.EducationalLevelId = c.EducationLevelId
where b.PersonId = #PersonId and a.EducationalLevelId in (2,3))
else (select dbo.school.SchooldName from dbo.School INNER JOIN dbo.EducationalBackground
ON dbo.School.SchoolId = dbo.EducationalBackground.SchoolId INNER JOIN
dbo.PersonEducationalBackground ON dbo.EducationalBackground.EducationalBackgroundId = dbo.PersonEducationalBackground.EducationalBackgroundId
where dbo.PersonEducationalBackground.PersonId = #PersonId)
Cheers. Thanks.
I have the below query to find the row prior to MAX row. i feel like i am missing something, can somebody please help with it. I ammlooking forward to get the b.usercode_1 as row prior to a.usercode_1 not the min or any other random row but the ROW prior to the MAX.
Please suggest.
Select distinct
c.ssn
, c.controlled_group_Status CG_status
, c.last_name || ' , '|| c.first_name FULL_NAME
, a.usercode_1 Current_REG
, a.eff_date effective_since1
, b.usercode_1 PRIOR_REG
, b.eff_date effective_since2
, d.term_eff_date
from employee_eff_date c
, emp_cg_data a
, emp_cg_data b
, emp_ben_elects d
where c.control_id = 'XYZ'
and c.controlled_group_Status <> 'D'
and c.eff_date = (select max( c1.eff_date)
from emp_cg_data c1
where c.control_id = c1.control_id
and c.ssn = c1.ssn)
and a.control_id = c.control_id
and a.ssn = c.ssn
and a.eff_date = (select max(a1.eff_date )
from emp_cg_data a1
where a.control_id = a1.control_id
and a.ssn = a1.ssn)
and a.usercode_1 = 'REG26'
and b.control_id = c.control_id
and b.ssn = c.ssn
and b.eff_date = (select max( b1.eff_date)
from emp_cg_data b1
where b.control_id = b1.control_id
and b.ssn = b1.ssn
and b1.eff_date < a.eff_date)
and b.usercode_1 like 'REG%'
and d.control_id = c.control_id
and d.ssn = c.ssn
and d.life_event_date = (select max( d1.life_event_date)
from emp_ben_elects d1
where d.control_id = d1.control_id
and d.ssn = d1.ssn)
and d.le_seq_no= (select max( d1.le_seq_no)
from emp_ben_elects d1
where d.control_id = d1.control_id
and d.ssn = d1.ssn
and d.life_event_date = d1.life_event_date)
and d.term_eff_date is null
;
NOTE: this is not a complete answer... its a helpful suggestion of what you should start with.
you are doing a Cartesian Product of the four tables, filtered by a WHERE... so something like this
Implicit Join -- generally not a good practice as it can be very difficult to keep the where filters apart from the join conditions.
SELECT *
FROM tableA a, TableB b
WHERE b.id = a.id
another way to write a JOIN (the more generally accepted way)
SELECT *
FROM tableA a
JOIN tableB b ON b.id = a.id
Use the ON clause to join two tables together.
You should change your joins to this format so that others can read your query and understand it better.
suggestion to solve your problem
a fairly simple way to get the second to last row is to use a row counter.
so something like
SELECT *, #row_count := #row_count + 1
FROM tableA a
JOIN tableB b on b.id = a.id AND -- any other conditions for the join.
CROSS JOIN (SELECT #row_count := 0) t
then from here you can get the MAX row, whether thats the ID or something else. and then get the #row_num -1. aka the previous row.
I have two tables that I believe I want to JOIN. I'm very new to this and am not completely sure…
The first table is called venues with the variables id, slug, name, etc. The second table is venue_terms with the variables id, option, venue, value. The matching variables are obviously venues.id and venue_terms.venue.
What I want to do is query venue_terms for matching values and then SELECT * FROM venues that match.
I've been working with the following query, but haven't been able to get it to work. I know INTERSECT isn't the solution, but I'm nut sure which JOIN I should use.
SELECT venue
FROM venue_terms
WHERE `option` = '1' AND `value` = '10'
INTERSECT
SELECT venue
FROM venue_terms
WHERE `option` = '2' AND `value` = '4';
I want to match those venue_terms.venue to the venues table. Can someone point me in the right direction?
UPDATE: To clarify, I'm trying to search multiple option/value combinations that ultimately have the same venue.id's. Basically, I want to able to find all of the venues where (option = 1 and value = 4) AND (option = 2 and value = 10) AND etc… where all of these are true.
You want to find venues that match conditions in two rows in table venue_terms. This can be accomplished by various methods. The most usual is by joining that table twice (another would be by a grouping query).
Here's the first way. Join twice to the venue_terms table:
SELECT v.id --- whatever columns you need
, v.slug --- from the venues table
, v.name
FROM venues AS v
INNER JOIN venue_terms AS vt1
ON vt1.venue = v.id
INNER JOIN venue_terms AS vt2
ON vt2.venue = v.id
WHERE ( vt1.option = 1 AND vt1.value = 10 )
AND ( vt2.option = 2 AND vt2.value = 4 ) ;
If you have 3 conditions, join thrice. If you have 10 conditions, join 10 times. It would be good for the efficiency of the query to have a compound index on (option, value, venue) in the terms table.
try this
SELECT venue.*, venue_terms.*
FROM venue
INNER JOIN venue_terms ON venue.id = venue_terms.venue
WHERE venue_terms.option IN ( 1 ,2)
AND venue_terms.value IN (10,4)
GROUP BY venue.id
How about this?
SELECT t1.*, t2.*
FROM venue t1 JOIN venue_terms t2
ON t1.id = t2.venue
WHERE (t2.option = 1 AND t2.value = 10)
NOTE: I believe option and value are of type INT.
If they are of type varchar then change above query to
SELECT t1.*, t2.*
FROM venue t1 JOIN venue_terms t2
ON t1.id = t2.venue
WHERE (t2.option = '1' AND t2.value = '10')
Update 1
As per your new requirement, you will just need to add that condition with OR option as shown below.
SELECT t1.*, t2.*
FROM venue t1 JOIN venue_terms t2
ON t1.id = t2.venue
WHERE
(t2.option = 1 AND t2.value = 10)
OR
(t2.option = 3 AND t2.value = 14)
This will join the two tables and print out the venues which matches the attributes (option, value) in venue_terms:
SELECT v.* FROM venue v, venue_terms vt
WHERE v.id = vt.venue
AND vt.option = 1
AND vt.value = 10
I have a statement that looks like:
QUERY A
UNION
QUERY B
ORDER BY SomeColumn
Query A and Query B each take a nominal amount of time to run, but when I put them in the UNION, it take 7-9 seconds which is unacceptable. In this case Query A returns 6 rows, Query B returns 7. So confusing...
I have absolutely no idea what would cause this, help would be much appreciated!
Here is an anonymized version of the script (I did not write this, so don't hate):
SELECT
'XXX' l_t,
s.p,
srst.c_b,
srd.a_d_f,
s.s_c,
sf.c_s,
srst.p_f,
s.s_r_i,
s.s_i,
s.s_d,
srd.m_s_d_l s_d,
srd.m_e_d_l e_d,
CASE WHEN (srs.s_s_t IS NOT NULL AND srs.s_s_t <> srs.s_e_t)
THEN 1 ELSE 0 END 's_f',
CASE WHEN (srs.c_s_t IS NOT NULL AND srs.c_s_t <> srs.c_e_t)
THEN 1 ELSE 0 END 'c_f',
r.r_i
FROM
t_s_r_d srd
INNER JOIN t_s s WITH (NOLOCK)
ON s.s_i = srd.s_i
INNER JOIN i_s_r(12345) r
ON r.r_i = srd.r_i
INNER JOIN i_s_s_f() sf
ON (sf.s_i = srd.s_i)
INNER JOIN t_s_r_s srst WITH (NOLOCK)
ON (srst.s_i = srd.s_i AND srst.r_i = srd.r_i )
LEFT OUTER JOIN t_s_r_s srs WITH (NOLOCK)
ON (srs.s_i = srd.s_i AND srs.r_i = srd.r_i)
WHERE
srst.d_f = 0
AND ((srd.m_s_d_l >= someval AND srd.m_s_d_l < someotherval)
OR
(srd.m_s_d_l <= someval AND srd.m_e_d_l > someotherval))
AND r.o_f = 0
AND r.i_f = 0
AND r.v_f = 1
AND r.g_i = 180
AND NOT EXISTS(SELECT * FROM t_c_r cdr WITH (NOLOCK) WHERE cdr.r_i = r.r_i)
UNION
SELECT
'XXX' l_t,
s.p,
srst.c_b,
srd.a_d_f,
s.s_c,
sf.c_s,
srst.p_f,
s.s_r_i,
s.s_i,
s.s_d,
srd.m_s_d_l s_d,
srd.m_e_d_l e_d,
CASE WHEN (srs.s_s_t IS NOT NULL AND srs.s_s_t <> srs.s_e_t)
THEN 1 ELSE 0 END 's_f',
CASE WHEN (srs.c_s_t IS NOT NULL AND srs.c_s_t <> srs.c_e_t)
THEN 1 ELSE 0 END 'c_f',
c.c_i
FROM
(t_s_r_d srd
INNER JOIN t_s s WITH (NOLOCK)
ON s.s_i = srd.s_i
INNER JOIN i_s_s_f() sf
ON (sf.s_i = srd.s_i)
LEFT OUTER JOIN t_s_r_s srs WITH (NOLOCK)
ON (srs.s_i = srd.s_i AND srs.r_i = srd.r_i)
INNER JOIN t_s_r_s srst WITH (NOLOCK)
ON (srst.s_i = srd.s_i AND srst.r_i = srd.r_i)),
i_s_c(12345) c
WHERE
srst.d_f = 0
AND ((srd.m_s_d_l >= someval AND srd.m_s_d_l < someotherval)
OR
(srd.m_s_d_l <= someval AND srd.m_e_d_l > someotherval))
AND c.o_f = 0
AND c.i_f = 0
AND c.v_f = 1
AND c.g_i = 180
AND EXISTS(SELECT * FROM t_c_r cr WITH (NOLOCK) WHERE cr.r_i = srd.r_i and
cr.c_i = c.c_i)
ORDER BY s_d
Because UNION removes duplicates it has to sort the whole data set first...try to use UNION ALL instead....it is much faster since it doesn't need to remove the dups
Turns out it was the old style ansii style joins in conjunction with the newer style explicit joins that were causing the lengthy return. Quite interesting, if anyone could provide a reason why that would be fantastic!