mysql query takes too long - mysql

im working on a project and I created this sql query but it seems to be taking too long if I select a bigger date range, any idea how to make it faster, I tryed with union all and its much faster, but data is not the same, need help with code:
SELECT SUM(ROUND(total, 2)) as Enero from (
SELECT
FORMAT (
COALESCE(
CONCAT(ROUND(pro.precio / 1.14 , 2) + ROUND(p.recargo / 1.14 , 2) + (SELECT SUM(ROUND(precio / 1.14 , 2)) AS precio FROM acompanante_orden WHERE id_orden=p.id_orden) - p.descuento),
CONCAT(ROUND(pro.precio / 1.14 , 2) + ROUND(p.recargo / 1.14 , 2) - p.descuento)
)+ COALESCE(
ROUND(CONCAT(ROUND(pro.precio / 1.14 , 2) + (SELECT SUM(ROUND(precio / 1.14 , 2)) AS precio FROM acompanante_orden WHERE id_orden=p.id_orden) + ROUND(p.recargo / 1.14 , 2) - p.descuento) * 0.14, 2),
ROUND(CONCAT(ROUND(pro.precio / 1.14 , 2) + ROUND(p.recargo / 1.14 , 2) - p.descuento) * 0.14, 2)
), 2) as total
FROM orden p
LEFT JOIN producto_orden AS pro ON p.id_orden = pro.id_orden
LEFT JOIN acompanante_orden AS pa ON p.id_orden = pa.id_orden
WHERE p.actual_date BETWEEN '2017-04-01' AND '2017-04-01' and p.status=4
) sum_1
UNION ALL IDEA
select ROUND(( sum(precio)) * 1.14,2) as total from (
SELECT a.id_orden, ROUND(b.precio / 1.14 , 2) + ROUND(a.recargo / 1.14 , 2) - a.descuento as precio FROM orden a, producto_orden b WHERE a.id_orden=b.id_orden and a.actual_date BETWEEN '2017-04-01' AND '2017-04-01' and a.status=4 union all
SELECT c.id_orden, ROUND(d.precio / 1.14 , 2) as precio FROM orden c, acompanante_orden d WHERE c.id_orden=d.id_orden and c.actual_date BETWEEN '2017-04-01' AND '2017-04-01' and c.status=4
) sum_1
UPDATE:
SO here is my table schema: http://sqlfiddle.com/#!9/4fe12/2
WHAT I NEED:
I need to get TOTAL of monthly SELLS, but values on DB are with TAXES so before I get total I need to do some math:
DB INFO
PRODUCT= $50.00
ADDONS= $8.00
SHIPPING= $6.00
DISCOUNT= $2.00
RESULTS I NEED:
PRODUCT= $50.00 / 1.14 = $43.86
ADDONS= $8.00 / 1.14 = $7.02
SHIPPING= $6.00 / 1.14 =$5.26
SUBTOTAL= $56.14 - DISCOUNT($2)= $54.14
SUBTOTAL= $54.14 * 1.14 = $61.72

The first subquery is basically:
SELECT . . .
FROM orden p LEFT JOIN
producto_orden pro
ON p.id_orden = pro.id_orden LEFT JOIN
acompanante_orden pa
ON p.id_orden = pa.id_orden
WHERE p.actual_date BETWEEN '2017-04-01' AND '2017-04-01' and
p.status = 4
For this query, you want indexes on orden(status, actual_date, id_id_orden), producto_orden(id_orden), andacompanante_orden(id_orden)`.
I'm not sure what the rest of the query is.

Related

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;

mysql matching multiple and's and or's

I am trying to get rows from a table where there are matches on multiple other tables.
This is the query I am running.
SELECT qt.*, DATEDIFF(CURRENT_DATE, STR_TO_DATE(up.DOB, '%m-%d-%Y')) / 365 AS Age
FROM UserProfile AS up, Game AS g, QuestionTable AS qt
WHERE NOT EXISTS(SELECT * FROM Options WHERE UserID = 75 AND QID = qt.QID AND DateTime >= CURDATE())
AND g.Active = 1 AND g.QID = qt.QID
AND (((g.Gender = up.Gender OR g.Gender = 'B') AND ((g.City = up.City AND g.Zip = up.Zip AND g.Country = up.Country) OR g.Home = 0) AND ((Age BETWEEN g.Maximum AND g.Minimum) OR g.Age = 0) AND ((SQRT( POW( 69.1 * ( g.Latitude - -93.5746359 ) , 2 ) + POW( 69.1 * ( 44.9737707 - g.Longitude ) * COS( g.Latitude / 57.3 ) , 2 ) ) > g.Distance) OR g.Geo = 0)) OR g.Special = 0)
GROUP BY qt.QID
I have ran this expression through C# and it returns true, yet it is only matching on the 'g.Special = 0' part through MySql.
Any help on this would be much appreciated!
Resolved the issue by fixing some mistakes I made and changed the query to this,
SELECT *
FROM QuestionTable AS qt
WHERE NOT EXISTS
(SELECT * FROM Options WHERE UserID = 75 AND QID = qt.QID AND DateTime >= CURDATE())
AND EXISTS(SELECT g.* FROM UserProfile AS up, Game AS g WHERE g.Active = 1 AND g.QID = qt.QID AND(g.Special = 0 OR(
(g.Gender = up.Gender OR g.Gender = 'B') AND
((g.City = up.City AND g.zip = up.Zip AND g.Country = up.Country) OR g.Home = 0) AND
((SQRT( POW( 69.1 * ( g.Latitude - 44.9737707 ) , 2 ) + POW( 69.1 * ( -93.5746359 - g.Longitude ) * COS( g.Latitude / 57.3 ) , 2 ) ) < g.Distance) OR g.Geo = 0) AND
((DATEDIFF(CURRENT_DATE, STR_TO_DATE(DOB, '%m/%d/%Y'))/365.25 BETWEEN g.Minimum AND g.Maximum) OR g.Age = 0))))

SUM from two tables, sort by country and publisher

I have two different sales tables and i want to SUM the quantity and FEEs for storeX and storeY from book_sales(isa), with quantity from all sales in the financial_report(xo). Im having trouble with both, and i dont know which JOIN i should use. The quantity calculation does not give any results and the fee calculation gives a number that is way to high. What is wrong here?
Please check my procedure if you have time:
ALTER PROCEDURE [dbo].[Report] #startDate VARCHAR(10),
#endDate VARCHAR(10)
AS
SELECT BB.name AS 'Publisher',
Sum(CASE
WHEN isa.report_source = 'storeX'
or isa.report_source = 'storeY' THEN
isa.quantity * xo.quantity
END) AS 'Total quantity',
Sum(CASE
WHEN isa.sales_price > 69
AND isa.report_source = 'storeX' THEN
6 * 1.25 * isa.quantity
WHEN isa.sales_price <= 69
AND isa.report_source = 'storeX' THEN
3 * 1.25 * isa.quantity
WHEN isa.sales_price <= 19
AND isa.report_source = 'storeX' THEN
1 * 1.25 * isa.quantity
WHEN isa.sales_price > 69
AND isa.report_source = 'storeY' THEN
6 * 1.25 * isa.quantity
WHEN isa.sales_price <= 69
AND isa.report_source = 'storeY' THEN
3 * 1.25 * isa.quantity
WHEN isa.sales_price <= 19
AND isa.report_source = 'storeY' THEN
1 * 1.25 * isa.quantity
WHEN xo.sales_price > 69
AND bb.country = 'NOR' THEN 6 * 1.25 * xo.quantity
WHEN xo.sales_price <= 69
AND bb.country = 'NOR' THEN 3 * 1.25 * xo.quantity
WHEN xo.sales_price > 69
AND bb.country <> 'NOR' THEN 6 * xo.quantity
WHEN xo.sales_price <= 69
AND bb.country <> 'NOR' THEN 3 * xo.quantity
END) AS 'Fee inc VAT(tot)'
FROM book_sales AS isa
INNER JOIN store AS BV
ON bv.store_id = isa.store_id
INNER JOIN financial_report AS xo
ON xo.identifiers = isa.identifiers
LEFT OUTER JOIN publisher AS BB
ON bb.publisher_id = bk.publisher_id
WHERE isa.sales_date >= CONVERT(DATETIME, #startDate, 20)
AND isa.sales_date < Dateadd(day, 1, ( CONVERT(DATETIME, #endDate, 20
) ))
GROUP BY bb.name,
bb.country
There are a few problems to sort out :
1- The total might be affected by NULL values, I suggest:
Sum(CASE
WHEN isa.report_source = 'storeX'
or isa.report_source = 'storeY' THEN
isnull(isa.quantity,0) * isnull(xo.quantity,0)
ELSE 0 END) AS 'Total quantity',
2- The case evaluation should be in order. For example:
WHEN isa.sales_price <= 69
AND isa.report_source = 'storeX' THEN
3 * 1.25 * isa.quantity
WHEN isa.sales_price <= 19
AND isa.report_source = 'storeX' THEN
1 * 1.25 * isa.quantity
<= 19 will never happen because it is also <= 69 and therefore the <=69 will evaluate true.
3- Cardinality between tables might be 1 to many so the values are summed multiple times when they shouldn't. You may need to select distinct or aggregate or flatten out some of the tables in the join to avoid this issue.

SQL SUM Query with subqueries

I am trying to get the total 'capacity' for each station but I keep getting the error message "#1630 - FUNCTION tflBikes.COUNT does not exist":
SELECT Count(ts.capacity),
tbu.stationid,
ts.name,
ts.easting,
ts.northing,
tbu.t,
Round(Avg(availablebikes), 1) AS Average,
Round(Avg(availablebikes) / capacity * 100, 1) AS Percentage,
ts.postcode
FROM tflbikeusage tbu,
tflstations ts
WHERE tbu.stationid = ts.usageid
AND ts.easting = Abs(easting -
(SELECT easting
FROM tflstations
WHERE name = 'Hatton Garden, Holborn')) < 750 * 0.5
AND ts.northing = Abs(northing -
(SELECT northing
FROM tflstations
WHERE name = 'Hatton Garden, Holborn')) < 750 * 0.5
AND Hour(t) BETWEEN 10 AND 22
GROUP BY ts.capacity HAVING COUNT (ts.capacity) =
(SELECT Sum(capacityno)
FROM
(SELECT COUNT(ts.capacity) AS capacityNo,
tbu.stationid,
ts.name,
ts.easting,
ts.northing,
tbu.t,
Round(Avg(availablebikes), 1) AS Average,
Round(Avg(availablebikes) / capacity * 100 , 1) AS Percentage,
ts.postcode
FROM tflbikeusage tbu,
tflstations ts
WHERE tbu.stationid = ts.usageid
AND ts.easting = Abs(easting -
(SELECT easting
FROM tflstations
WHERE name = 'Hatton Garden, Holborn')) < 750 * 0.5
AND ts.northing = Abs(northing -
(SELECT northing
FROM tflstations
WHERE name = 'Hatton Garden, Holborn')) < 750 * 0.5
AND Hour(t) BETWEEN 10 AND 22
GROUP BY ts.capacity
ORDER BY percentage DESC)derivedTable)
You are aliasing tflbikesUsage to "tbu", so you should be using that going forward.

how to select row values as columns of another table in mysql [duplicate]

I have three tables.
tax_master
item_master
item_tax
The values in it are like this.
*tax_master*
tax_id tax_name tax_value
--------------------------
1 Vat 5
2 LBT 8
*item_master*
item_id Prise
---------------
1 30
2 100
*item_tax*
item_tax_id item_id tax_id
------------------------------
1 1 1
2 2 2
3 1 2
Now i want output like this.
item_id prise VAT LBT Total_prise
---------------------------------------
1 30 1.5 2.4 33.9
2 100 - 8 108
VAT value is calculated like 5/30*100 like 5% on 30=1.5
select item_id, price,
(min(case when tax_name = 'VAT' then tax end)) vat,
(min(case when tax_name = 'LBT' then tax end)) lbt,
coalesce(min(case when tax_name = 'VAT' then tax end),0) +
coalesce(min(case when tax_name = 'LBT' then tax end),0) +
price total
from
(select a.item_id item_id,
c.tax_name tax_name,
(c.tax_value * b.price / 100) tax,
b.price price
from item_tax a inner join item_master b on a.item_id = b.item_id
inner join tax_master c on a.tax_id = c.tax_id) as calc
group by item_id, price;
Demo here.
The following statement should return the data the way you want it:
SELECT DISTINCT
item_master.item_id,
(SELECT
tax_master.tax_value * item_master.prise / 100
FROM
item_tax, tax_master
WHERE
item_tax.item_id = item_master.item_id and
tax_master.tax_id = item_tax.tax_id and
tax_master.tax_name = 'VAT') as VAT,
(SELECT
tax_master.tax_value * item_master.prise / 100
FROM
item_tax, tax_master
WHERE
item_tax.item_id = item_master.item_id and
tax_master.tax_id = item_tax.tax_id and
tax_master.tax_name = 'LBT') as LBT
FROM
item_tax, item_master, tax_master
WHERE
item_master.item_id = item_tax.item_id and
tax_master.tax_id = item_tax.tax_id
bwt: I think prise should be meant to be price ;)
EDIT: I tried it out with a fiddle and you need a MAX (or MIN) to group them) on top of the left joins:
SELECT item_master.item_id,
MAX(item_master.price * tax_master_vat.tax_value / 100) as VAT,
MAX(item_master.price * tax_master_lbt.tax_value / 100) as LBT
FROM item_master
LEFT JOIN item_tax ON item_master.item_id = item_tax.item_id
LEFT JOIN tax_master tax_master_vat ON item_tax.tax_id = tax_master_vat.tax_id
AND tax_master_vat.tax_name = 'VAT'
LEFT JOIN tax_master tax_master_lbt ON item_tax.tax_id = tax_master_lbt.tax_id
AND tax_master_lbt.tax_name = 'LBT'
GROUP BY item_master.item_id
If there's no tax_value linked for a certain item, it should return NULL (multiplying with NULL is NULL). If you want to show something different from 'null' (or nothing) you can use COALESCE. If you want to include the total price:
If you want to add the price or the total you can left join this on the item_master table:
SELECT item_master.item_id,
tax.VAT,
tax.LBT,
item_master.price + COALESCE(tax.VAT, 0) + COALESCE(tax.LBT, 0) AS Total
FROM item_master
LEFT JOIN ( SELECT item_master.item_id,
MAX(item_master.price * tax_master_vat.tax_value / 100) as VAT,
MAX(item_master.price * tax_master_lbt.tax_value / 100) as LBT
FROM item_master
LEFT JOIN item_tax ON item_master.item_id = item_tax.item_id
LEFT JOIN tax_master tax_master_vat ON item_tax.tax_id = tax_master_vat.tax_id
AND tax_master_vat.tax_name = 'VAT'
LEFT JOIN tax_master tax_master_lbt ON item_tax.tax_id = tax_master_lbt.tax_id
AND tax_master_lbt.tax_name = 'LBT'
GROUP BY item_master.item_id ) tax ON item_master.item_id = tax.item_id
Or, you can use the OVER PARTITION BY clause (lets you group on item_id only but still include the price field):
SELECT item_id,
price,
VAT,
LBT,
price + COALESCE(VAT, 0) + COALESCE(LBT, 0) AS Total
FROM ( SELECT DISTINCT
item_master.item_id,
item_master.price,
MAX(item_master.price * tax_master_vat.tax_value / 100) OVER (PARTITION BY item_master.item_id) as VAT,
MAX(item_master.price * tax_master_lbt.tax_value / 100) OVER (PARTITION BY item_master.item_id) as LBT
FROM item_master
LEFT JOIN item_tax ON item_master.item_id = item_tax.item_id
LEFT JOIN tax_master tax_master_vat ON item_tax.tax_id = tax_master_vat.tax_id
AND tax_master_vat.tax_name = 'VAT'
LEFT JOIN tax_master tax_master_lbt ON item_tax.tax_id = tax_master_lbt.tax_id
AND tax_master_lbt.tax_name = 'LBT'
) price_and_tax
SQL Fiddle Example