I'm trying to make a SQL query on some flights (called legs). The goal is to know how many flights are on time, how many have between 5 and 15 mins of delay and how many more than 15.
Using subqueries, I managed to get a first result :
SELECT
count(*) as 'leg total',
leg2.l2count as 'less than 15min delay',
round(leg2.l2count / count(*) * 100, 2) as 'less than 15min delay percentage',
leg3.l3count as 'more than 15min delay',
round(leg3.l3count / count(*) * 100, 2) as 'more than 15min delay percentage',
count(*) - leg2.l2count - leg3.l3count as 'on time',
round((count(*) - leg2.l2count - leg3.l3count) / count(*) * 100, 2) as 'on time percentage'
FROM TBL_leg leg -- previsional dates
JOIN TBL_flight_reports fr ON leg.id = fr.leg_ops_id, -- realized dates
(
SELECT count(l2.id) as l2count
FROM TBL_leg l2
JOIN TBL_flight_reports fr2 ON l2.id = fr2.leg_ops_id
WHERE (
timediff(fr2.arrival_date_block, l2.to_date_time) > SEC_TO_TIME(5*60) AND
timediff(fr2.arrival_date_block, l2.to_date_time) < SEC_TO_TIME(15*60)
)
OR (
timediff(fr2.departure_date_block, l2.start_date_time) > SEC_TO_TIME(5*60) AND
timediff(fr2.departure_date_block, l2.start_date_time) < SEC_TO_TIME(15*60)
)
) leg2, -- delays between threshold and 15min
(
SELECT count(l3.id) as l3count
FROM TBL_leg l3
JOIN TBL_flight_reports fr3 ON l3.id = fr3.leg_ops_id
WHERE (
timediff(fr3.arrival_date_block, l3.to_date_time) > SEC_TO_TIME(15*60)
)
OR (
timediff(fr3.departure_date_block, l3.start_date_time) > SEC_TO_TIME(15*60)
)
) leg3 -- delays of more than 15min
;
This query seems to be fine.
Now, i'm trying to sort my results : in my TBL_leg table, there is an airplane_id column and I want to group my results by it. The goal being to have my delays by airplane.
The issue is getting my grouping in my subqueries. I'm missing some logic right there.
Also, if you have some better solution for my main issue (my current query), feel free :)
Regards
Make sure each sub query is grouped by the airplane id and add the airplane id to the select of the sub query and then join by the airplane id.
e.g. 2nd leg:
left join (
SELECT l2.airplane_id, count(l2.id) as l2count
FROM TBL_leg l2
JOIN TBL_flight_reports fr2 ON l2.id = fr2.leg_ops_id
WHERE (
timediff(fr2.arrival_date_block, l2.to_date_time) > SEC_TO_TIME(5*60) AND
timediff(fr2.arrival_date_block, l2.to_date_time) < SEC_TO_TIME(15*60)
)
OR (
timediff(fr2.departure_date_block, l2.start_date_time) > SEC_TO_TIME(5*60) AND
timediff(fr2.departure_date_block, l2.start_date_time) < SEC_TO_TIME(15*60)
)
GROUP BY l2.airplane_id
) leg2 ON leg.airplane_id = leg2.airplane_id
Group the total query on airplane_id as well.
Related
I am trying to get through a problem where there are multiple accounts of same scheme on same customer id. On a given txn date I want to retrieve the total Sanctioned Limit and total utilized amount from these accounts. Below is the SQL query I have constructed.
SELECT
cust_id,
tran_date,
rollover_date,
next_rollover,
(
SELECT
acc_num as kcc_ac
FROM
dbzsubvention.acc_disb_amt a
WHERE
(a.tran_date <= AB.tran_date)
AND a.sch_code = 'xxx'
AND a.cust_id = AB.cust_id
ORDER BY
a.tran_date desc
LIMIT
1
) KCC_ACC,
(
SELECT
SUM(kcc_prod)
FROM
(
SELECT
prod_limit as kcc_prod,
acc_num,
s.acc_status
FROM
dbzsubvention.acc_disb_amt a
inner join dbzsubvention.acc_rollover_all_sub_status s using (acc_num)
left join dbzsubvention.acc_close_date c using (acc_num)
WHERE
a.cust_id = AB.cust_id
AND a.tran_date <= AB.tran_date
AND (
ac_close > AB.tran_date || ac_close is null
)
AND a.sch_code = 'xxx'
AND s.acc_status = 'R'
AND s.rollover_date <= AB.tran_date
AND (
AB.tran_date < s.next_rollover || s.next_rollover is null
)
GROUP BY
acc_num
order by
a.tran_date
) t
) kcc_prod,
(
SELECT
sum(disb_amt)
FROM
(
SELECT
disb_amt,
acc_num,
tran_date
FROM
(
SELECT
disb_amt,
a.acc_num,
a.tran_date
FROM
dbzsubvention.acc_disb_amt a
inner join dbzsubvention.acc_rollover_all_sub_status s using (acc_num)
left join dbzsubvention.acc_close_date c using (acc_num)
WHERE
a.tran_date <= AB.tran_date
AND (
c.ac_close > AB.tran_date || c.ac_close is null
)
AND a.sch_code = 'xxx'
AND a.cust_id = AB.cust_id
AND s.acc_status = 'R'
AND s.rollover_date <= AB.tran_date
AND (
AB.tran_date < s.next_rollover || s.next_rollover is null
)
GROUP BY
acc_num,
a.tran_date
order by
a.tran_date desc
) t
GROUP BY
acc_num
) tt
) kcc_disb
FROM
dbzsubvention.acc_disb_amt AB
WHERE
AB.cust_id = 'abcdef'
group by
cust_id,
tran_date
order by
tran_date asc;
This query isn't working. Upon research I have found that correlated subquery works only till 1 level down. However I couldn't get a workaround to this problem.
I have tried searching the solution around this problem but couldn't find the desired one. Using the SUM function at the inner query will not give desired results as
In the second subquery that will sum all the values in column before applying the group by clause.
In third subquery the sorting has to be done first then the grouping and finally the sum.
Therefore I am reaching out to the community for help to suggest a workaround to the issue.
You're correct - external column cannot be transferred through the nesting level immediately.
Try this workaround:
SELECT ... -- outer query
( -- correlated subquery nesting level 1
SELECT ...
( -- correlated subquery nesting level 2
SELECT ...
...
WHERE table0_level1.column0_1 ... -- moved value
)
FROM table1
-- move through nesting level making it a source of current level
CROSS JOIN ( SELECT table0.column0 AS column0_1 ) AS table0_level1
) AS ...,
...
FROM table0
...
I am trying to optimize this query. Now it takes 28 seconds.
AS used to be missing in my query. After adding, query time dropped 20%
SELECT
g.id,
g.adresid,
g.senaryoid,
g.olayid,
g.gonderilecegitarih
FROM
(
SELECT
adresid
FROM
expose2.800_emsenaryolar_emgidenbulten
WHERE
olayid = '3320'
) AS s
RIGHT JOIN expose2.800_emsenaryolar_emgidenbulten AS g ON s.adresid = g.adresid
WHERE
s.adresid IS NULL
AND g.olayid = '2784'
AND g.durum = '1'
AND g.gonderilecegitarih < DATE_SUB(
'2015-05-13 15:40:15',
INTERVAL 1 DAY
)
If you use s.adresid IS NULL condition in subquery it will join faster then more rows ...
SELECT
g.id,
g.adresid,
g.senaryoid,
g.olayid,
g.gonderilecegitarih
FROM (
SELECT adresid FROM expose2.800_emsenaryolar_emgidenbulten WHERE olayid = '3320' and s.adresid IS NULL
) AS s
RIGHT JOIN expose2.800_emsenaryolar_emgidenbulten AS g ON s.adresid = g.adresid
AND g.olayid = '2784'
AND g.durum = '1'
AND g.gonderilecegitarih < DATE_SUB(
'2015-05-13 15:40:15',
INTERVAL 1 DAY
)
still this query optimized using self join.
For added speed, add this composite index to g:
INDEX(olayid, durum, gonderilecegitarih)
Please provide SHOW CREATE TABLE 800_emsenaryolar_emgidenbulten; I want to verify that you also have an index on adresid.
It is possible that this has been answered somewhere already but I couldn't find it.
So would appreciate if someone could help me with this sql statement again.
This is the sql statement which I have so far:
SELECT * , Round( (Rate * TIME_TO_SEC( Total ) /3600 ) , 2) AS revenue
FROM (SELECT event.eventID, event.staffID, event.role, TIMEDIFF( Time, Pause )
AS Total,
CASE WHEN Position = 'Teamleader'
THEN (Teamleader)
WHEN Position = 'Waiter'
THEN (Waiter)
ELSE '0'
END AS Rate
FROM event, rates, eventoverview
WHERE Storno =0
AND event.eventID= eventoverview.eventID
AND event.clientid = rates.clientid
GROUP BY event.eventID, event.clientID)q1
GROUP BY q1.staffID
The table I am getting is now giving me a total rate per staff and event.
But what I would like to achieve is a sum of those rates per staff.
So basically a sum of the revenue.
Hope someone can help me. Thanks in advance
You can enclose your query in a subquery and do that in the outer query like this:
SELECT *,
SUM(revenue)
FROM
(
SELECT * ,
Round( (Rate * TIME_TO_SEC( Total ) /3600 ) , 2) AS revenue
FROM
(
SELECT
event.eventID,
event.staffID,
event.role,
TIMEDIFF( Time, Pause ) AS Total,
CASE WHEN Position = 'Teamleader' THEN (Teamleader)
WHEN Position = 'Waiter' THEN (Waiter)
ELSE '0'
END AS Rate
FROM event
INNER JOIN rates ON event.clientid = rates.clientid
INNER JOIN eventoverview ON event.eventID = eventoverview.eventID
WHERE Storno =0
GROUP BY event.eventID, event.clientID
)q1
GROUP BY q1.staffID
) AS t
GROUP BY staffID;
Note that: You might get inconsistent data, due to the use of SELECT * with GROUP BY staffID only, the columns that are not in the GROUP BY clause need to be enclosed with an aggregate function otherwise mysql will get an arbitrary value for it. This is not recommended and it it is not the standard way to do so.
I have a MySQL table with the structure:
beverages_log(id, users_id, beverages_id, timestamp)
I'm trying to compute the maximum streak of consecutive days during which a user (with id 1) logs a beverage (with id 1) at least 5 times each day. I'm pretty sure that this can be done using views as follows:
CREATE or REPLACE VIEW daycounts AS
SELECT count(*) AS n, DATE(timestamp) AS d FROM beverages_log
WHERE users_id = '1' AND beverages_id = 1 GROUP BY d;
CREATE or REPLACE VIEW t AS SELECT * FROM daycounts WHERE n >= 5;
SELECT MAX(streak) AS current FROM ( SELECT DATEDIFF(MIN(c.d), a.d)+1 AS streak
FROM t AS a LEFT JOIN t AS b ON a.d = ADDDATE(b.d,1)
LEFT JOIN t AS c ON a.d <= c.d
LEFT JOIN t AS d ON c.d = ADDDATE(d.d,-1)
WHERE b.d IS NULL AND c.d IS NOT NULL AND d.d IS NULL GROUP BY a.d) allstreaks;
However, repeatedly creating views for different users every time I run this check seems pretty inefficient. Is there a way in MySQL to perform this computation in a single query, without creating views or repeatedly calling the same subqueries a bunch of times?
This solution seems to perform quite well as long as there is a composite index on users_id and beverages_id -
SELECT *
FROM (
SELECT t.*, IF(#prev + INTERVAL 1 DAY = t.d, #c := #c + 1, #c := 1) AS streak, #prev := t.d
FROM (
SELECT DATE(timestamp) AS d, COUNT(*) AS n
FROM beverages_log
WHERE users_id = 1
AND beverages_id = 1
GROUP BY DATE(timestamp)
HAVING COUNT(*) >= 5
) AS t
INNER JOIN (SELECT #prev := NULL, #c := 1) AS vars
) AS t
ORDER BY streak DESC LIMIT 1;
Why not include user_id in they daycounts view and group by user_id and date.
Also include user_id in view t.
Then when you are queering against t add the user_id to the where clause.
Then you don't have to recreate your views for every single user you just need to remember to include in your where clause.
That's a little tricky. I'd start with a view to summarize events by day:
CREATE VIEW BView AS
SELECT UserID, BevID, CAST(EventDateTime AS DATE) AS EventDate, COUNT(*) AS NumEvents
FROM beverages_log
GROUP BY UserID, BevID, CAST(EventDateTime AS DATE)
I'd then use a Dates table (just a table with one row per day; very handy to have) to examine all possible date ranges and throw out any with a gap. This will probably be slow as hell, but it's a start:
SELECT
UserID, BevID, MAX(StreakLength) AS StreakLength
FROM
(
SELECT
B1.UserID, B1.BevID, B1.EventDate AS StreakStart, DATEDIFF(DD, StartDate.Date, EndDate.Date) AS StreakLength
FROM
BView AS B1
INNER JOIN Dates AS StartDate ON B1.EventDate = StartDate.Date
INNER JOIN Dates AS EndDate ON EndDate.Date > StartDate.Date
WHERE
B1.NumEvents >= 5
-- Exclude this potential streak if there's a day with no activity
AND NOT EXISTS (SELECT * FROM Dates AS MissedDay WHERE MissedDay.Date > StartDate.Date AND MissedDay.Date <= EndDate.Date AND NOT EXISTS (SELECT * FROM BView AS B2 WHERE B1.UserID = B2.UserID AND B1.BevID = B2.BevID AND MissedDay.Date = B2.EventDate))
-- Exclude this potential streak if there's a day with less than five events
AND NOT EXISTS (SELECT * FROM BView AS B2 WHERE B1.UserID = B2.UserID AND B1.BevID = B2.BevID AND B2.EventDate > StartDate.Date AND B2.EventDate <= EndDate.Date AND B2.NumEvents < 5)
) AS X
GROUP BY
UserID, BevID
Suppose equity has a column called TickerID. I would like to replace the 111's with equity.TickerID. MySQL can't seem to resolve the scope and returns an unknown column when I try that. This SQL statement works but I need to run it for each ticker. Would be nice if I could get a full table.
SELECT Ticker,
IF(tbl_m200.MA200_Count = 200,tbl_m200.MA200,-1) AS MA200,
IF(tbl_m50.MA50_Count = 50,tbl_m50.MA50,-1) AS MA50,
IF(tbl_m20.MA20_Count = 20,tbl_m20.MA20,-1) AS MA20
FROM equity
INNER JOIN
(SELECT TickerID,AVG(Y.Close) AS MA200,COUNT(Y.Close) AS MA200_Count FROM
(
SELECT Close,TickerID FROM equity_pricehistory_daily
WHERE TickerID = 111
ORDER BY Timestamp DESC LIMIT 0,200
) AS Y
) AS tbl_m200
USING(TickerID)
INNER JOIN
(SELECT TickerID,AVG(Y.Close) AS MA50,COUNT(Y.Close) AS MA50_Count FROM
(
SELECT Close,TickerID FROM equity_pricehistory_daily
WHERE TickerID = 111
ORDER BY Timestamp DESC LIMIT 50
) AS Y
) AS tbl_m50
USING(TickerID)
INNER JOIN
(SELECT TickerID,AVG(Y.Close) AS MA20,COUNT(Y.Close) AS MA20_Count FROM
(
SELECT Close,TickerID FROM equity_pricehistory_daily
WHERE TickerID = 111
ORDER BY Timestamp DESC LIMIT 0,20
) AS Y
) AS tbl_m20
USING(TickerID)
This seems to be some bug or "feature" of MySQL. Many persons seems to have the same problem with outer tables being out of scope.
Anyway... You could create functions that retrieve the information you want:
DROP FUNCTION IF EXISTS AveragePriceHistory_20;
CREATE FUNCTION AveragePriceHistory_20(MyTickerID INT)
RETURNS DECIMAL(9,2) DETERMINISTIC
RETURN (
SELECT AVG(Y.Close)
FROM (
SELECT Z.Close
FROM equity_pricehistory_daily Z
WHERE Z.TickerID = MyTickerID
ORDER BY Timestamp DESC
LIMIT 20
) Y
HAVING COUNT(*) = 20
);
SELECT
E.TickerID,
E.Ticker,
AveragePriceHistory_20(E.TickerID) AS MA20
FROM equity E;
You would get NULL instead of -1. If this is undesirable, you could wrap the function-call with IFNULL(...,-1).
Another way of solving this, would be to select for the time-frame, instead of using LIMIT.
SELECT
E.TickerID,
E.Ticker,
(
SELECT AVG(Y.Close)
FROM equity_pricehistory_daily Y
WHERE Y.TickerID = E.TickerID
AND Y.Timestamp > ADDDATE(CURRENT_TIMESTAMP, INTERVAL -20 DAY)
) AS MA20
FROM equity E;