ROW Prior to the MAX row, not the MIN - mysql

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.

Related

SQL query performance issues NOT EXISTS

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)

how can i solve this question? using self inner join and some conditions?

the first picture is the table . second picture is the expected output.
conditions are 1. refids should be same. 2. for all the same ref ids (a.start,a.end &b.start,b.end) in the current and previous row. 3. should calculate the time difference which is greater than or equal to one day.
You want pairs of rows that match certain condition. You can perform a join to identify the pairs.
You don't say which version of MySQL you are using but in MySQL 8.x you can do:
with
x as (
select a.id
from my_table a
join my_table b on b.id = a.id + 1
and b.refid = a.refid
and (a.detail = 'a.end' and b.detail = 'a.start'
or a.detail = 'b.end' and b.detail = 'b.start')
)
select t.*
from my_table t
join x on t.id = x.id or t.id = x.id + 1
For MySQL 5.x you can do:
select t.*
from my_table t
join (
select a.id
from my_table a
join my_table b on b.id = a.id + 1
and b.refid = a.refid
and (a.detail = 'a.end' and b.detail = 'a.start'
or a.detail = 'b.end' and b.detail = 'b.start')
) x on t.id = x.id or t.id = x.id + 1

SQL SELECT to get company status based on working hours

I'm trying to execute a SQL SELECT query to get all the stores within a city and also have a field to inform if the store is within working hours or not.
This is the SQL I have so far:
$day = date('w');
$hour = date('H:i:s');
SELECT a.id, a.rating, a.name, a.image, b.cityName
(
SELECT 1 FROM tbStore a, workingHour b
WHERE a.id = b.id_store
AND b.weekDay = '$day'
AND b.hourOpen <= '$hour'
AND b.hourClose >= '$hour')
) as 'open'
FROM tbStore a, tbCity b
WHERE b.url = '$url'
AND a.id_city = b.id
ORDER BY open
The way I'm checking this value is as a boolean, so the 'open' field needs to be 0 (closed) or 1 (open).
It's kind of working... But the problem is, if just 1 store is within working hours, all of the others will be 'open' as well, instead of just that specific store.
I also saw some sql statements where people use CASE instead of another select, so any other type of code can be used, as long as the final result is correct.
You're redefining the tbStore a and losing the restriction by $url. You probably just need to change:
SELECT 1 FROM tbStore a, workingHour b
To:
SELECT 1 FROM workingHour b
I have just rewrite you query using explict join way
SELECT
a.id
, a.rating
, a.name
, a.image
, b.cityName
, when( c.id_store is not null then 1 else 0 end) as open
FROM tbStore a
inner join tbCity b on a.id_city = b.id
LEFT join workingHour c on a.id = c.id_store
WHERE b.url = '$url'
AND c.weekDay = '$day'
AND c.hourOpen <= '$hour'
AND c.hourClose >= '$hour'
ORDER BY open

Join between sub-queries in SQLAlchemy

In relation to the answer I accepted for this post, SQL Group By and Limit issue, I need to figure out how to create that query using SQLAlchemy. For reference, the query I need to run is:
SELECT t.id, t.creation_time, c.id, c.creation_time
FROM (SELECT id, creation_time
FROM thread
ORDER BY creation_time DESC
LIMIT 5
) t
LEFT OUTER JOIN comment c ON c.thread_id = t.id
WHERE 3 >= (SELECT COUNT(1)
FROM comment c2
WHERE c.thread_id = c2.thread_id
AND c.creation_time <= c2.creation_time
)
I have the first half of the query, but I am struggling with the syntax for the WHERE clause and how to combine it with the JOIN. Any one have any suggestions?
Thanks!
EDIT: First attempt seems to mess up around the .filter() call:
c = aliased(Comment)
c2 = aliased(Comment)
subq = db.session.query(Thread.id).filter_by(topic_id=122098).order_by(Thread.creation_time.desc()).limit(2).offset(2).subquery('t')
subq2 = db.session.query(func.count(1).label("count")).filter(c.id==c2.id).subquery('z')
q = db.session.query(subq.c.id, c.id).outerjoin(c, c.thread_id==subq.c.id).filter(3 >= subq2.c.count)
this generates the following SQL:
SELECT t.id AS t_id, comment_1.id AS comment_1_id
FROM (SELECT count(1) AS count
FROM comment AS comment_1, comment AS comment_2
WHERE comment_1.id = comment_2.id) AS z, (SELECT thread.id AS id
FROM thread
WHERE thread.topic_id = :topic_id ORDER BY thread.creation_time DESC
LIMIT 2 OFFSET 2) AS t LEFT OUTER JOIN comment AS comment_1 ON comment_1.thread_id = t.id
WHERE z.count <= 3
Notice the sub-query ordering is incorrect, and subq2 somehow is selecting from comment twice. Manually fixing that gives the right results, I am just unsure of how to get SQLAlchemy to get it right.
Try this:
c = db.aliased(Comment, name='c')
c2 = db.aliased(Comment, name='c2')
sq = (db.session
.query(Thread.id, Thread.creation_time)
.order_by(Thread.creation_time.desc())
.limit(5)
).subquery(name='t')
sq2 = (
db.session.query(db.func.count(1))
.select_from(c2)
.filter(c.thread_id == c2.thread_id)
.filter(c.creation_time <= c2.creation_time)
.correlate(c)
.as_scalar()
)
q = (db.session
.query(
sq.c.id, sq.c.creation_time,
c.id, c.creation_time,
)
.outerjoin(c, c.thread_id == sq.c.id)
.filter(3 >= sq2)
)

Calculations using 2 tables and reporting to a 3rd

So I've got 3 tables and I need to calculate a value from the first 2 and then update a field in the third table with that value, and I'm coming across an error.
UPDATE Characters AS c
SET c.Total_DKP = (
(SELECT c.Initial_DKP
FROM c
WHERE c.Name='harrian')-
(SELECT SUM(a.DKP_Change)
FROM Attendance AS a
WHERE a.Name = 'harrian')
)
WHERE c.Name = 'harrian' ;
it gives the error
Table 'harrian.c' doesn't exist
but when I run
UPDATE Characters AS c
SET c.Total_DKP = ( SELECT SUM(a.DKP_Change)
FROM Attendance AS a
WHERE a.Name = 'harrian'
)
WHERE c.Name = 'harrian'
I have no issues, can anyone tell me whats wrong with the first block?
Here is the full query-
UPDATE Characters AS c
SET c.Total_DKP = (
(SELECT c.Inital_DKP FROM Characters AS c WHERE c.Name = 'harrian')
+(SELECT SUM(a.DKP_Change) FROM Attendance AS a WHERE a.Name = 'harrian')
+(SELECT SUM(b.Cost) FROM Raid_Drops AS b WHERE b.Player_Name = 'harrian')
)
WHERE c.Name = 'harrian' ;
I'm now getting this error
You can't specify target table 'c' for update in FROM clause
Your first subquery is:
SELECT c.Initial_DKP FROM c WHERE c.Name='harrian'
Instead of FROM c I think you mean FROM Characters:
SELECT Initial_DKP FROM Characters WHERE Name='harrian'
But if that is the case, the whole UPDATE is better written as
UPDATE Characters AS c
SET c.Total_DKP = c.Initial_DKP - (
SELECT SUM(a.DKP_Change)
FROM Attendance AS a
WHERE a.Name = 'harrian'
)
WHERE c.Name = 'harrian';
Because an update statement cannot use the table it's updating in a subquery.
If there can be no more than one row with Name = 'harrian' in table Characters (e.g if Name is UNIQUE), you can use:
UPDATE Characters AS c
SET c.Total_DKP = (
c.Inital_DKP
+ (SELECT SUM(a.DKP_Change) FROM Attendance AS a WHERE a.Name = 'harrian')
+ (SELECT SUM(b.Cost) FROM Raid_Drops AS b WHERE b.Player_Name = 'harrian')
)
WHERE c.Name = 'harrian' ;
Some parenthesis above are not needed. Also, to avoid the cases where Attendance or Raid_Drops have no rows with Name = 'harrian' and the NULL that will be produced by the SUM() over the empty tables, you need to convert the Nulls to zeros:
UPDATE Characters AS c
SET c.Total_DKP =
c.Inital_DKP
+ COALESCE( (SELECT SUM(a.DKP_Change) FROM Attendance AS a
WHERE a.Name = 'harrian')
), 0)
+ COALESCE( (SELECT SUM(b.Cost) FROM Raid_Drops AS b
WHERE b.Player_Name = 'harrian')
), 0)
WHERE c.Name = 'harrian' ;