Mysql Query performance issue - mysql

We run our systems on a dedicated server with the following spec
Dual Xeon 2.8Ghz
8GB RAM
2 x 250GB Storage
1TB Monthly Data Transfer
All my code is in php and databases are mysql
Currently we have around 500k records in our database; this is likely to increase by at least 500k every year
I am facing an issue of a very high load time of one of our main pages.
I have noticed that on their local server the load time is 2 secs and on our server it is 18 seconds.Using same query and an exact copy of server database on the local server.
What steps would you take to find out why there is such a large difference in load times?
Each call that comes through our system is loaded into database. Currently there are 370k records
Each call is associated with a campaign, tariff and pricing model.
Our queries calculate:
a) Total number of calls per campaign,
b) Total call charges per campaign
The problem is that much of the data, (call charges) can changed in the past by updating tariffs, pricing, campaign start dates, end dates etc. This has led to some instability as the table includes the call charges
The alternative is to calculate call charges on the fly. So to calculate it in the code every time there is a query. However there are 400k records, and this is likely to increase by a million every year
What is the best way to do this?
Please see the query strings below:
a) Platform Campaign Lead Data Query
SELECT c.tariff, c.campaign_name, c.ad_status, c.ad_control, cd.company, cd.customer_name, pm.pricing_model_name, c.start_date, c.end_date,
(
SELECT p.payment_date
FROM payments p
WHERE p.campaign_id = c.campaign_id
ORDER BY p.payment_date DESC
LIMIT 1) AS LastPaidOn,
(
SELECT p.payment_amount
FROM payments p
WHERE p.payment_status = 'Complete' AND p.campaign_id = c.campaign_id
ORDER BY p.payment_date DESC
LIMIT 1) AS LastPaidAmount,
(CASE WHEN NOW() BETWEEN c.start_date AND c.end_date THEN 'Live' ELSE 'Dead' END) AS STATUS,
(CASE WHEN c.group_name = c.campaign_id THEN '' ELSE c.group_name END)
AS group_name_processed,
(
SELECT IFNULL(SUM(call_leads_purchased),0)
FROM payments
WHERE campaign_id = c.campaign_id AND payment_status = 'Complete') AS CallLeadsPurchased,
(
SELECT IFNULL(SUM(cd.call_leads_used),0)
FROM call_data cd
WHERE cd.campaign_id = c.campaign_id) AS CallLeadsUsed
FROM campaign_data c
JOIN customer_details cd ON cd.customer_id = c.customer_id
JOIN pricing_model pm ON pm.pricing_model_id = c.pricing_model_id
HAVING STATUS = 'Live'
ORDER BY c.campaign_name
b) Dashboard Queries
/For Calls Summary/
SELECT
(
SELECT SUM(call_leads_used)
FROM call_data cd
WHERE 1 = 1 AND cd.call_date >= '2010-03-06' AND campaign_id IN(6)) AS SummaryCallLeadsUsed,
(
SELECT COUNT(*)
FROM call_data cd
WHERE 1 = 1 AND cd.call_date >= '2010-03-06' AND campaign_id IN(6)) AS SummaryReceivedCalls,
(
SELECT COUNT(*)
FROM call_data cd
WHERE 1 = 1 AND cd.call_date >= '2010-03-06' AND campaign_id IN(6) AND cd.call_status = 'Unanswered') AS SummaryUnanswered,
(
SELECT SUM(call_leads_used)
FROM call_data cd
WHERE campaign_id IN(6)) AS TotalCallLeadsUsed,
(
SELECT SUM(call_leads_purchased)
FROM payments
WHERE campaign_id IN(6) AND payment_status = 'Complete') AS TotalLeadsPurchased
/For Pie Chart/
SELECT
'Free Calls' AS TYPE, COUNT(*) AS calls
FROM call_data cd,
campaign_data cad
WHERE cd.campaign_id = cad.campaign_id AND cd.call_date >= '2010-03-06' AND cd.is_processed = 1 AND cd.applied_charges = 0 AND cad.customer_id = 6
GROUP BY cad.customer_id UNION
SELECT
'Charged Calls' AS TYPE, COUNT(*) AS calls
FROM call_data cd,
campaign_data cad
WHERE cd.campaign_id = cad.campaign_id AND cd.call_date >= '2010-03-06' AND cd.is_processed = 1 AND cd.applied_charges > 0 AND cad.customer_id = 6
GROUP BY cad.customer_id
/*For Calls per day chart*/
SELECT COUNT(*) AS calls, DATE_FORMAT(call_date, '%Y-%m-%d') AS call_day
FROM call_data cd,
campaign_data cad
WHERE cd.campaign_id = cad.campaign_id AND cd.call_date >= '2010-03-06' AND cd.is_processed = 1 AND cad.customer_id = 6
GROUP BY call_day
ORDER BY call_day
/*For total pages */
SELECT COUNT(*) AS expr
FROM call_data cd,
campaign_data cad
WHERE cd.campaign_id = cad.campaign_id AND cd.is_processed = 1 AND cd.call_date >= '2010-03-06' AND cad.customer_id = 6
/*Calls data*/
SELECT
cad.campaign_name,
cd.call_date,
cd.caller_id,
cd.call_duration,
cd.call_status,
cd.call_duplication,
cd.applied_charges
FROM call_data cd,
campaign_data cad
WHERE cd.campaign_id = cad.campaign_id AND cd.is_processed = 1 AND cd.call_date >= '2010-03-06' AND cad.customer_id = 6
ORDER BY cd.call_date DESC
LIMIT 0, 20

Related

SQL how to create aggregate of two aggregated columns for each row

I'm working on a big query in SQL (MySQL 5.7) to calculate aggregated columns based on raw values in my table. I've created several aggregated columns (see attached screenshot and SQL) and I now need to create a conversion_percent column for each tlp_aff_id in my query.
This conversion_percent should be a division of the aggregated JoinedSessions.total_sessions and COUNT(Report.tlp_aff_id) as leads_total.
My current SQL:
SELECT
# Application details
Report.tlp_aff_id,
# Revenue
JoinedRevenue.total_revenue,
# Commission
JoinedCommission.total_commission,
# Profit
JoinedProfit.total_profit,
# Sessions
JoinedSessions.total_sessions,
# Submits
COUNT(Report.tlp_aff_id) as total_submits,
# Leads
COUNT(Report.tlp_aff_id) as leads_total,
SUM(case when Report.application_result = 'Accepted' then 1 else 0 end) leads_accepted,
SUM(case when Report.application_result = 'Rejected' then 1 else 0 end) leads_rejected
# Conversion percent
# JoinedConversion.conversion_percent
FROM
tlp_payout_report_minute AS Report
INNER JOIN
(
SELECT
JoinedRevenue.tlp_aff_id,
JoinedRevenue.minute_rounded_timestamp,
SUM(commission) AS total_revenue
FROM
tlp_payout_report_minute AS JoinedRevenue
WHERE
JoinedRevenue.minute_rounded_timestamp >= 1664841600
AND
JoinedRevenue.minute_rounded_timestamp <= 1664927999
GROUP BY
JoinedRevenue.tlp_aff_id
) AS JoinedRevenue
ON JoinedRevenue.tlp_aff_id = Report.tlp_aff_id
INNER JOIN
(
SELECT
ReportCommission.tlp_aff_id,
ReportCommission.seller_code,
ReportCommission.minute_rounded_timestamp,
SUM(commission) AS total_commission
FROM
tlp_payout_report_minute AS ReportCommission
WHERE
ReportCommission.minute_rounded_timestamp >= 1664841600
AND
ReportCommission.minute_rounded_timestamp <= 1664927999
AND
ReportCommission.seller_code != 44
GROUP BY
ReportCommission.tlp_aff_id
) AS JoinedCommission
ON JoinedCommission.tlp_aff_id = Report.tlp_aff_id
INNER JOIN
(
SELECT
ReportProfit.tlp_aff_id,
ReportProfit.seller_code,
ReportProfit.application_result,
ReportProfit.minute_rounded_timestamp,
SUM(commission) AS total_profit
FROM
tlp_payout_report_minute AS ReportProfit
WHERE
ReportProfit.minute_rounded_timestamp >= 1664841600
AND
ReportProfit.minute_rounded_timestamp <= 1664927999
AND
ReportProfit.application_result = 'Accepted'
AND
ReportProfit.seller_code = 44
GROUP BY
ReportProfit.tlp_aff_id
) AS JoinedProfit
ON JoinedProfit.tlp_aff_id = Report.tlp_aff_id
INNER JOIN
(
SELECT
Conversion.aff_id,
Conversion.conversion_type,
COUNT(Conversion.ip_address) as total_sessions
FROM
tlp_conversions AS Conversion
WHERE
Conversion.conversion_time >= '2022-10-04 00:00:00'
AND
Conversion.conversion_time <= '2022-10-04 23:59:59'
AND
Conversion.aff_id IS NOT NULL
AND
Conversion.conversion_type = 2
GROUP BY
Conversion.aff_id
) AS JoinedSessions
ON JoinedSessions.aff_id = Report.tlp_aff_id
WHERE
Report.minute_rounded_timestamp >= 1664841600
AND
Report.minute_rounded_timestamp <= 1664927999
GROUP BY
Report.tlp_aff_id
ORDER BY
JoinedRevenue.total_revenue DESC
I'm thinking something along the lines of:
INNER JOIN
(
...
) AS JoinedConversion
ON JoinedConversion.aff_id = Report.tlp_aff_id
But I don't think this is necessary for conversion_percent.
What's the right approach here?

Get total cost for a customer call

I have 2 tables = customer and their call history
Now I want to charge them based on call duration and that too for a specific month say Jan 2015.
Here is the criteria to calculate the cost for calls -
A) For incoming calls, the charge is 1 unit per second. Example if the duration is 250 seconds then cost is 250
B) For outgoing calls, for the first 2mins, the cost is fixed at 500 units. for subsequent seconds the cost is 2 units per second.
Example if the outgoing duration is 5mins then cost is 500 units + 2*3*60 units = 860 units
Below are the tables:
customer table with columns id, name, phone
history table with columns id, incoming_phone, outgoing_phone, duration, dialed_on (YYYY-MM-DD)
I have come up with below queries for my conditions:
For incoming call cost:
select c.name, c.phone, h.duration as cost
from customer c join history h on c.phone = h.incoming_phone
When I run the above query I did not get any syntax errors.
For outgoing call cost:
select c.name, c.phone, CASE
WHEN h.duration > 120 THEN 500 + 2*(h.duration-120)
ELSE 2*(h.duration-120)
END; as cost
from customer c join history h on c.phone = h.outgoing_phone
When I run the above query I got syntax error like "ERROR 1109 (42S02) at line 1: Unknown table 'c' in field list"
I want to join these two queries and get the total cost and display the fields as name, phone, cost
I still need to add a condition for a specific month to restrict data for Jan 2015, but got stuck with the approach.
The error is due to the extra semicolon ; after END.
Sounds like your final query will be this:
SELECT c.name,
c.phone,
SUM(CASE WHEN h.direction = 'in' THEN h.duration END) as IncomingCost,
SUM(CASE WHEN h.direction = 'out' AND h.duration > 120 THEN 500 + 2*(h.duration-120)
WHEN h.direction = 'out' AND h.duration <= 120 THEN 500
END) as OutgoingCost,
SUM(CASE WHEN h.direction = 'in' THEN h.duration END +
CASE WHEN h.direction = 'out' AND h.duration > 120 THEN 500 + 2*(h.duration-120)
WHEN h.direction = 'out' AND h.duration <= 120 THEN 500
END) as TotalCost
FROM customer c
JOIN (SELECT 'out' as directon, duration, dialed_on, outgoing_phone as phone
FROM history
WHERE YEAR(dialed_on) = 1995
AND MONTH(dialed_on) = 1
UNION ALL
SELECT 'in' as direction, duration, dialed_on, incoming_phone as phone
FROM history
WHERE YEAR(dialed_on) = 1995
AND MONTH(dialed_on) = 1
) h ON c.phone = h.phone
GROUP BY c.name,
c.phone

Combining two mysql query returns ok instead of rows

I have a query in which I return some information regarding an invoice, I take that invoice and compare it to another table "payment" to see if that invoice (fullamount -fullpaid) exists in the other table and if it does some function should not run in my backend code.
SELECT a.status, a.rf_reference, a.payor_orig_id , a.gross_amount + a.type_related_amount as fullamount,
a.gross_paid + a.type_related_paid as fullpaid
FROM invoice a
where a.type = 3 and
a.status in (10, 30) and
a.UPDATE_DT is null
having fullamount > fullpaid
order by a.ORIG_ID;
The above query returns
status| rf_reference | payor_orig_id | fullamount | fullpaid
30 RF123456 212 1000 200
So now I take the above information and pass it onto another query to see if a row field matches.
I pass it on like this
select *
from payment
where
payor_orig_id = 212 and
rf_reference = RF123456 and
payment_amount = (1000-200) and
status = 10 and
INSERT_DT BETWEEN DATE_SUB(NOW(), INTERVAL 185 DAY) AND NOW() and
UPDATE_DT IS NULL;
So now the above code will return a row by which basically I do not run my backend function.
Since this are two separate query I would like to combine them to one where I make sure that I add a having statement and check that ONLY rows are returned where there is no match between the invoice and payment table.
SELECT a.status, a.rf_reference, a.payor_orig_id , a.gross_amount + a.type_related_amount as fullamount,
a.gross_paid + a.type_related_paid as fullpaid,
(select b.payment_amount
from payment b
where
b.payor_orig_id = a.payor_orig_id and
b.rf_reference = a.rf_reference and
b.status = 10 and
b.INSERT_DT BETWEEN DATE_SUB(NOW(), INTERVAL 185 DAY) AND NOW() and
b.UPDATE_DT IS NULL) as payment_amount
FROM invoice a
where a.type = 3 and
a.status in (10, 30) and
a.UPDATE_DT is null
having fullamount > fullpaid and
(fullamount - fullpaid ) <> payment_amount
order by a.ORIG_ID;
The above query returns "OK" which is odd since I am not sure how to debug it.
Try seeing if the other table exists or not using NOT EXIST
SELECT a.* ,
a.gross_amount + a.type_related_amount as fullamount,
a.gross_paid + a.type_related_paid as fullpaid
FROM invoice a
where a.type = 3 and
a.status in (10, 30) and
a.UPDATE_DT is null and
NOT EXISTS ( select *
from payment
where
payor_orig_id = a.payor_orig_id and
rf_reference = a.rf_reference and
payment_amount = ((a.gross_amount + a.type_related_amount) - (a.gross_paid + a.type_related_paid)) and
status = 10 and
INSERT_DT BETWEEN DATE_SUB(NOW(), INTERVAL 185 DAY) AND NOW() and
UPDATE_DT IS NULL )
having fullamount > fullpaid
order by a.ORIG_ID;

Alternatives to joins in mysql

I have a query as follows that retrieves the status for different stores in a table and displays it as different columns.
SELECT a.Store_ID,b.total as order_completed,c.total as order_cancelled,d.total as order_processed,e.total as order_failed FROM ORDER_HISTORY a
-> LEFT OUTER JOIN(select Store_ID,count(*) as total from ORDER_HISTORY where Status = 57 group by Store_ID)b on a.Store_ID = b.Store_ID
-> LEFT OUTER JOIN(select Store_ID,count(*) as total from ORDER_HISTORY where Status = 53 group by Store_ID)c on a.Store_ID = c.Store_ID
-> LEFT OUTER JOIN(select Store_ID,count(*) as total from ORDER_HISTORY where Status = 52 group by Store_ID)d on a.Store_ID = d.Store_ID
-> LEFT OUTER JOIN(select Store_ID,count(*) as total from ORDER_HISTORY where Status = 62 group by Store_ID)e on a.Store_ID = e.Store_ID
-> group by a.Store_ID;
Can anybody suggest an alternative to using joins as it affects the performance of db operations.
Create an index on ORDER_HISTORY over (Store_ID, Status), then this should be plenty fast.
SELECT
Store_ID,
status,
COUNT(*) as total
FROM
ORDER_HISTORY
GROUP BY
Store_ID,
status;
Then use your application to display the few resulting rows data in columns. Should not be hard to implement.
Another approach would be (same index as above):
SELECT
Store_ID,
SUM(CASE WHEN Status = 57 THEN 1 ELSE 0 END) AS order_completed,
SUM(CASE WHEN Status = 53 THEN 1 ELSE 0 END) AS order_cancelled,
SUM(CASE WHEN Status = 52 THEN 1 ELSE 0 END) AS order_processed,
SUM(CASE WHEN Status = 62 THEN 1 ELSE 0 END) AS order_processed
FROM
ORDER_HISTORY
GROUP BY
Store_ID;
Replace NULL values as appropriate.
Try using a trigger. Same as a stored procedure that executes when an event occurs within the database. maybe it will help you.

Expressing formula within a SELECT query

I have this existing query:
SELECT
extension
, Total_Outbound+Total_Missed+Total_Received AS Total_Calls
, Total_Missed
, Total_Talk_Time_minutes
FROM (
SELECT
, extension
, sum(if(Answered = 1,0,1)) AS Total_Missed
, sum(CASE WHEN LEGTYPE1 = 2 AND ANSWERED = 1 THEN 1 ELSE 0 END) AS Total_Received
, sum(if(LEGTYPE1 = 1,1,0)) AS Total_Outbound
, round(sum(Duration) / 60,2) AS Total_Talk_Time_minutes
FROM session a
GROUP BY extension
) x;
It works great but I need to add a metric/formula to it called missed_call_score right under Total_Talk_Time_Minutes.
The formula for the missed call score is this:
(missed calls/total talk time) * (average calls per CSR/total calls) * 100 but one thing to note is that the average calls per csr needs to ignore the MAX and MIN, so the lowest and highest number of calls taken.
I'm not sure how I could construct this score within a single select variable or the syntax I would use for this given the fact that it has to throw out the max and min.
Here is an example of my needed output and the formulas it should be using:
extension | Total calls | missed calls | total talk time | missed call score
----------------------------------------------------------------------------
1234 8 4 15.5 5.7
4321 4 0 9.42 0.0
5678 5 2 6.78 6.5
9876 13 6 18.3 7.2
Total call sum = 30
Total call sum without high and low = 13
average calls per CSR = (13/2) = 6.5
extension 1 = (4/15.5) * (6.5/30) * 100 = 5.7
extension 2 = (0/9.42) * (6.5/30) * 100 = 0.0
extension 3 = (2/6.78) * (6.5/30) * 100 = 6.5
extension 4 = (6/18.3) * (6.5/30) * 100 = 7.2
The data above for extension, total calls, missed calls and talk time are taken from my sql fiddle, linked below. I simply added the score column to give example of my expected output.
The fiddle linked below shows my create and inserts so hopefully that gives everything needed to assist me with this.
**sql fiddle
**
http://sqlfiddle.com/#!9/aa1f9/1
UPDATE
Full production query with joins
SELECT firstn ,
lastn ,
extension ,
Total_Outbound+Total_Missed+Total_Received AS Total_Calls ,
Total_Missed ,
Total_Talk_Time_minutes ,
Total_All_Calls ,
Max_Calls ,
Min_Calls ,
CSR_Count ,
((Total_Missed/Total_Talk_Time_minutes) *
(((Total_All_Calls-Max_Calls-Min_Calls)/CSR_Count)/Total_All_Calls)) * 100
FROM ( SELECT u.firstn ,
u.lastn ,
c.extension ,
sum(if(Answered = 1,0,1)) AS Total_Missed ,
sum(CASE WHEN LEGTYPE1 = 2 AND ANSWERED = 1 THEN 1 ELSE 0 END) AS Total_Received ,
sum(CASE WHEN LEGTYPE1 = 1 THEN 1 ELSE 0 END) AS Total_Outbound ,
round(sum(Duration) / 60,2) AS Total_Talk_Time_minutes ,
(SELECT COUNT(1) FROM ambition.session a INNER JOIN ambition.callsummary b ON a.NOTABLECALLID = b.NOTABLECALLID
INNER join ambition.mxuser c ON a.RESPONSIBLEUSEREXTENSIONID = c.EXTENSIONID
INNER join jackson_id.users u ON c.extension = u.extension
WHERE b.ts between curdate() - interval 5 day and now()
AND c.extension IN (7276,7314,7295,7306,7357,7200,7218,7247,7331,7255,7330,7000,7215,7240,7358,7312)) Total_All_Calls ,
(SELECT MAX(CNT) FROM (SELECT COUNT(1) CNT, c.extension
FROM ambition.SESSION a INNER JOIN ambition.callsummary b ON a.NOTABLECALLID = b.NOTABLECALLID
INNER join ambition.mxuser c ON a.RESPONSIBLEUSEREXTENSIONID = c.EXTENSIONID
INNER join jackson_id.users u ON c.extension = u.extension
WHERE b.ts between curdate() - interval 5 day and now()
AND c.extension IN (7276,7314,7295,7306,7357,7200,7218,7247,7331,7255,7330,7000,7215,7240,7358,7312) GROUP BY responsibleuserextensionid) y) Max_Calls ,
(SELECT MIN(CNT) FROM (SELECT COUNT(1) CNT, c.extension
FROM ambition.SESSION a
INNER JOIN ambition.callsummary b ON a.NOTABLECALLID = b.NOTABLECALLID
INNER join ambition.mxuser c ON a.RESPONSIBLEUSEREXTENSIONID = c.EXTENSIONID
INNER join jackson_id.users u ON c.extension = u.extension
WHERE b.ts between curdate() - interval 5 day and now()
AND c.extension IN (7276,7314,7295,7306,7357,7200,7218,7247,7331,7255,7330,7000,7215,7240,7358,7312)GROUP BY responsibleuserextensionid) y) Min_Calls ,
(SELECT COUNT(DISTINCT c.extension)-2
FROM ambition.SESSION a INNER JOIN ambition.callsummary b ON a.NOTABLECALLID = b.NOTABLECALLID
INNER join ambition.mxuser c ON a.RESPONSIBLEUSEREXTENSIONID = c.EXTENSIONID
INNER join jackson_id.users u ON c.extension = u.extension
WHERE b.ts between curdate() - interval 5 day and now()
AND c.extension IN (7276,7314,7295,7306,7357,7200,7218,7247,7331,7255,7330,7000,7215,7240,7358,7312)) CSR_Count
FROM ambition.session a
INNER JOIN ambition.callsummary b ON a.NOTABLECALLID = b.NOTABLECALLID
INNER join ambition.mxuser c ON a.RESPONSIBLEUSEREXTENSIONID = c.EXTENSIONID
INNER join jackson_id.users u ON c.extension = u.extension
LEFT JOIN ambition.knownnumbers k ON a.callingpartyno = k.phone_number
WHERE b.ts between curdate() - interval 5 day and now()
AND c.extension IN (7276,7314,7295,7306,7357,7200,7218,7247,7331,7255,7330,7000,7215,7240,7358,7312)
GROUP BY c.extension, u.firstn, u.lastn ) x
This should work for you:
SELECT
extension
, Total_Outbound+Total_Missed+Total_Received AS Total_Calls
, Total_Missed
, Total_Talk_Time_minutes
, Total_All_Calls
, Max_Calls
, Min_Calls
, CSR_Count
, ((Total_Missed/Total_Talk_Time_minutes) *
(((Total_All_Calls-Max_Calls-Min_Calls)/CSR_Count)/Total_All_Calls)) * 100
FROM (
SELECT
extension
, sum(if(Answered = 1,0,1)) AS Total_Missed
, sum(CASE WHEN LEGTYPE1 = 2 AND ANSWERED = 1 THEN 1 ELSE 0 END) AS Total_Received
, sum(CASE WHEN ANSWERED = 1 AND LEGTYPE1 = 1 THEN 1 ELSE 0 END) AS Total_Outbound
, round(sum(Duration) / 60,2) AS Total_Talk_Time_minutes
, (SELECT COUNT(1) FROM session) Total_All_Calls
, (SELECT MAX(CNT) FROM (SELECT COUNT(1) CNT, EXTENSION FROM SESSION GROUP BY EXTENSION) y) Max_Calls
, (SELECT MIN(CNT) FROM (SELECT COUNT(1) CNT, EXTENSION FROM SESSION GROUP BY EXTENSION) y) Min_Calls
, (SELECT COUNT(DISTINCT EXTENSION)-2 FROM SESSION) CSR_Count
FROM session a
GROUP BY extension
) x;
Here is the fiddle.
Basically I used sub-counts in your derived table x to get each of the variables needed for missed_call_score. One major thing worth noting is that the logic was off for Total_Outbound, so I tweaked that to a CASE statement instead of an IF(). I selected the count columns in the outer query just so you can see what is going on, you can remove those.
I've done something similar in the past and extracted this snippet from my code.
I think/hope that this might help you getting started (I left out most of the columns from your query and you'd have to adjust avg(amount) to match your formula.
select extension, avg(amount) from
(
select t.*,
min(amount) over (partition by extension) as min_amt,
max(amount) over (partition by extension) as max_amt
from your_table t
) t
where amount > min_amt and amount < max_amt group by extension;