Getting incorrect value from SQL Math Operation? - mysql

I am performing an operation to work out a percentage based on 2 values.
I have a single table that tracks an overall value against months and years.
Table Name - donation_tracker
I am comparing across all months across current and previous year then performing a calculation such as:
Current Value (Month- January, Year- 2014, CityID- 1) / Previous Value (Month- January, Year- 2013, CityID- 1) = Division Value * 100 = New Percentage Value.
Some of the math operations appear to be correct but some are incorrect for example the image below is showing 140 when it should be 130.
The values I am quering are as follows:
The column donation_amount is set as
Type = Decimal
Length = 10,2.
Sum should be 130...
SQL CODE-
SELECT city_markers.City_ID, city_markers.City_Name, city_markers.City_Lng, city_markers.City_Lat, SUM( d2.Donation_Amount ) , d1.Month, d1.Year, round( (
D2.Donation_amount / D1.Donation_amount
) *100, 2 )
FROM `city_markers`
INNER JOIN donation_tracker d1 ON city_markers.City_ID = d1.City_ID
INNER JOIN donation_tracker D2 ON d1.month = D2.month
AND d1.month = 'January'
AND d2.month = 'January'
AND d2.year = '2014'
AND D1.year = D2.year -1
AND D1.Location_ID = D2.Location_ID
GROUP BY city_markers.City_ID
Thank you.

You do not sum up the amounts here:
round( (D2.Donation_amount / D1.Donation_amount) *100, 2 )
So the result is calculated by the values of each first row:
round( (70 / 50) *100, 2 ) ==> 140
Use sum() to get the intended result:
round( (sum(D2.Donation_amount) / sum(D1.Donation_amount)) *100, 2 )

Related

Return results where no values exist for a specific column

Any guidance would be greatly appreciated please.
Aim of the statement is to read the East (x) and West (y) in the same row for the same year and month.
Not sure if I am over complicating things. I am stuck with the y as part of my statement. Currently there are no records for statement WHERE Dam_Name = 15 AND [Survey_Date] > DATEADD(yyyy,-1,getdate()) so my final results return no records.
How do I get it to still give me the list with the x values and blank for the y values?
Here is what I built.
;WITH x AS
(
SELECT [Survey_Year], [Water_Volume], DateName(month,DateAdd(month,Survey_Month,-1)) as Month
FROM Dam_Water_Levels
WHERE Dam_Name = 14 AND [Survey_Date] > DATEADD(yyyy,-1,getdate())
)
, y AS
(
SELECT
Case when count (1) >0
then (select [Survey_Year], [Water_Volume], DateName(month,DateAdd(month,Survey_Month,-1)))
else ''
END
as Month
FROM Dam_Water_Levels
WHERE Dam_Name = 15 AND [Survey_Date] > DATEADD(yyyy,-1,getdate())
Group by Survey_Year,Survey_Month, Water_Volume
)
SELECT Month = x.Month, East = x.Water_Volume, West = y.Water_Volume
FROM x
INNER JOIN y ON x.Month = y.Month
inner join Months mon on mon.Month=y.Month
order by y.Survey_Year, mon.ID;
Hope it makes sense
Regards,
Tanya
Another way I looked at it was to use this query. The down side was I had to include a column I didn't want (Intended_Month) because I needed it in my order by statement.
SELECT
Distinct a.Intended_Month as ' ', DateName(month,DateAdd(month,a.Intended_Month,-1)) as Month,
( SELECT Water_Volume from Dam_Water_Levels as y WHERE a.Survey_Year=y.Survey_Year and a.Survey_Month=y.Intended_Month and y.Dam_Name = 14) as 'East' ,
(SELECT Water_Volume from Dam_Water_Levels as x WHERE a.Survey_Year=x.Survey_Year and a.Survey_Month=x.Intended_Month and x.Dam_Name = 15) as' West'
FROM Dam_Water_Levels as a
where a.[Survey_Date] > DATEADD(yyyy,-1,getdate())
order by a.Intended_Month

Calculating datetime difference from separate rows and add them to get total

Click link below to see image.
I need to get the time diff highlighted in red and the time diff in blue. Then add the time together to get the total.
Below is the query that will show all the records.
The first 2 records i will need to get the time difference, which will be around 3 days 4 hours and the last 2 records should only be 2 mins. So in total it should be AROUND 3 days 4 hours and 2 mins.
query image
select so
, createDate
, o
, n
from userTrans
where ( n = 10 OR o = 10 ) and so = 'g220'
Below will show that i grouped the records to get the total time. This is not what i want because it is getting the min and max time diff. The result turns out to be 32 hours.
select so
, min(createDate) minDate
, max(createDate) maxDate
, TIMESTAMPDIFF(MINUTE, min(createDate), max(createDate)) diff
, CONCAT(
FLOOR(HOUR(TIMEDIFF(min(createDate), max(createDate))) / 24), ' days ',
MOD(HOUR(TIMEDIFF(min(createDate), max(createDate))), 24), ' hours ',
MINUTE(TIMEDIFF(min(createDate), max(createDate))), ' minutes') diff1
, count(*) hits
from userTrans
where ( n = 10 OR o = 10 ) and so = 'g220'
group by so
order by TIMESTAMPDIFF(MINUTE, min(createDate), max(createDate)) DESC
Will this be possible to achieve? I hope I was clear.
Thank you
You can use a correlated subquery to align the 2 date/time references for a calculation.
SELECT
*
, TIME_TO_SEC(TIMEDIFF(createDate,nextcreatedate))
FROM (
SELECT
so
, createDate
, (
SELECT
createDate
FROM table1 AS t2
WHERE t2.so = t1.so
AND t2.createDate > t1.createDate
ORDER BY
CreaDate
LIMIT 1
)
AS nextcreatedate
, n
, test
FROM table1 AS t1
WHERE o = 0
) d
In other dbms systems using lead() might provide a more convenient of getting the next needed createdate value.

MySql - Selecting MAX & MIN and returning the corresponding rows

I trying to get the last 6 months of the min and max of prices in my table and display them as a group by months. My query is not returning the corresponding rows values, such as the date time for when the max price was or min..
I want to select the min & max prices and the date time they both occurred and the rest of the data for that row...
(the reason why i have concat for report_term, as i need to print this with the dataset when displaying results. e.g. February 2018 -> ...., January 2018 -> ...)
SELECT metal_price_id, CONCAT(MONTHNAME(metal_price_datetime), ' ', YEAR(metal_price_datetime)) AS report_term, max(metal_price) as highest_gold_price, metal_price_datetime FROM metal_prices_v2
WHERE metal_id = 1
AND DATEDIFF(NOW(), metal_price_datetime) BETWEEN 0 AND 180
GROUP BY report_term
ORDER BY metal_price_datetime DESC
I have made an example, extract from my DB:
http://sqlfiddle.com/#!9/617bcb2/4/0
My desired result would be to see the min and max prices grouped by month, date of min, date of max.. and all in the last 6 months.
thanks
UPDATE.
The below code works, but it returns back rows from beyond the 180 days specified. I have just checked, and it is because it joining by the price which may be duplicated a number of times during the years.... see: http://sqlfiddle.com/#!9/5f501b/1
You could use twice inner join on the subselect for min and max
select a.metal_price_datetime
, t1.highest_gold_price
, t1.report_term
, t2.lowest_gold_price
,t2.metal_price_datetime
from metal_prices_v2 a
inner join (
SELECT CONCAT(MONTHNAME(metal_price_datetime), ' ', YEAR(metal_price_datetime)) AS report_term
, max(metal_price) as highest_gold_price
from metal_prices_v2
WHERE metal_id = 1
AND DATEDIFF(NOW(), metal_price_datetime) BETWEEN 0 AND 180
GROUP BY report_term
) t1 on t1.highest_gold_price = a.metal_price
inner join (
select a.metal_price_datetime
, t.lowest_gold_price
, t.report_term
from metal_prices_v2 a
inner join (
SELECT CONCAT(MONTHNAME(metal_price_datetime), ' ', YEAR(metal_price_datetime)) AS report_term
, min(metal_price) as lowest_gold_price
from metal_prices_v2
WHERE metal_id = 1
AND DATEDIFF(NOW(), metal_price_datetime) BETWEEN 0 AND 180
GROUP BY report_term
) t on t.lowest_gold_price = a.metal_price
) t2 on t2.report_term = t1.report_term
simplified version of what you should do so you can learn the working process.
You need calculate the min() max() of the periods you need. That is your first brick on this building.
you have tableA, you calculate min() lets call it R1
SELECT group_field, min() as min_value
FROM TableA
GROUP BY group_field
same for max() call it R2
SELECT group_field, max() as max_value
FROM TableA
GROUP BY group_field
Now you need to bring all the data from original fields so you join each result with your original table
We call those T1 and T2:
SELECT tableA.group_field, tableA.value, tableA.date
FROM tableA
JOIN ( ... .. ) as R1
ON tableA.group_field = R1.group_field
AND tableA.value = R1.min_value
SELECT tableA.group_field, tableA.value, tableA.date
FROM tableA
JOIN ( ... .. ) as R2
ON tableA.group_field = R2.group_field
AND tableA.value = R2.max_value
Now we join T1 and T2.
SELECT *
FROM ( .... ) as T1
JOIN ( .... ) as T2
ON t1.group_field = t2.group_field
So the idea is if you can do a brick, you do the next one. Then you also can add filters like last 6 months or something else you need.
In this case the group_field is the CONCAT() value

SSRS Matrix percentages

Here's the report:
This is how I got the percentages for column the '%Change of most recent year".
=((Last(Fields!Quantity.Value,"Child") - First(Fields!Quantity.Value)) / First(Fields!Quantity.Value))`
= ((54675 - 55968)/55968 ) = -2.31%'
= ((54675 - 57849)/57849) = -5.49%'
It will always take the first year '2012' in this case and get the percentages against each other year. If I enter the years 2005,2004,2003,2002,2001 it will always take the first year and do a percentages against each additional year. 2005 to 2004, 2005 to 2003, 2005 to 2002 and so on. I can have as many as 2 column (year) to many columns.
I need to do it for the Total and Subtotal but it won't work because it's in a different scope.
data is = row Child group
Sub Total: = row Parent group
Total: = row Total group
Year = Column Period group
Query use to get result.
SELECT MEMBERSHIP_CODE
, PERIOD, COUNT(DISTINCT ID) AS Distinct_ID
, SUM(QUANTITY) AS Quantity
, '01-Personal' AS Child
, '01-Overall' AS Parent
, 'Total' as Total
FROM vf_Sshot AS vfs
INNER JOIN vProd AS vP ON vfs.PRODUCT_CODE = vP.PRODUCT_CODE
INNER JOIN vMem_Type vMT on vMT.Member_Type = vfs.Member_Type
WHERE (PERIOD IN ( (SELECT Val from dbo.fn_String_To_Table(#Periods,',',1))))
AND (vMT.MEMBER_TYPE NOT IN ('a','b','c'))
AND (vfs.STATUS IN ( 'A', 'D', 'C'))
AND (MEMBERSHIP_CODE NOT IN ('ABC', 'DEF' ))
and vP.PROD_TYPE in ('DUE','MC','SC')
and vMT.Member_Record = '1'
GROUP BY MEMBERSHIP_CODE, PERIOD
Any ideas?
How would I produce this output?
TOTAL: 57,573 58,941 57,573 61,188 57,573 61,175 57,175
This is the easiest way of solving your problem. In your query, identify the sum for the latest period on a separate column (you can transform your query into a CTE, so that you don't have to change your base query a lot):
WITH query AS (
SELECT MEMBERSHIP_CODE
, PERIOD, COUNT(DISTINCT ID) AS Distinct_ID
, SUM(QUANTITY) AS Quantity
, '01-Personal' AS Child
, '01-Overall' AS Parent
, 'Total' as Total
...
UNION
SELECT
...
)
SELECT
A.MEMBERSHIP_CODE,
A.PERIOD,
A.Distinct_ID,
A.Child,
A.Parent,
A.Total,
A.Quantity,
B.Quantity AS LastPeriodQuantity
FROM
query A INNER JOIN
(SELECT *, ROW_NUMBER() OVER(PARTITION BY MEMBERSHIP_CODE, Distinct_ID, Child, Parent ORDER BY PERIOD DESC) as periodOrder FROM query) B ON
A.MEMBERSHIP_CODE = B.MEMBERSHIP_CODE AND
A.DISTINCT_ID = B.DISTINCT_ID AND
A.Parent = B.Parent AND
A.Child = B.Child AND
A.Total = B.Total AND
B.PeriodOrder = 1
And then on all your totals/subtotals/columns you will be accessing a column that is being grouped/filtered by the same rules than your denominator. Your expression can remain, for all cells, something like this:
=(Fields!LastPeriodQuantity.Value - Fields!Quantity.Value) / Fields!Quantity.Value

MySQL Join Two Queries Horizontally

I have a query that works correctly to pull a series of targets and total hours worked for company A. I would like to run the exact same query for company B and join them on a common date, which happens to be grouped by week. My current query:
SELECT * FROM (
SELECT org, date,
( SELECT SUM( target ) FROM target WHERE org = "companyA" ) AS companyA_target,
SUM( hours ) AS companyA_actual
FROM time_management_system
WHERE org = "companyA"
GROUP BY WEEK( date )
ORDER BY DATE
) q1
LEFT JOIN (
SELECT org, date,
( SELECT SUM( target ) FROM target WHERE org = "companyB" ) AS companyB_target,
SUM( hours ) AS companyB_actual
FROM time_management_system
WHERE org = "companyB"
GROUP BY WEEK( date )
ORDER BY DATE
) q2
ON q1.date = q2.date
The results show all of the dates / information of companyA, however companyB only shows sporadic data. Separately, the two queries will show the exact same set of dates, just with different information in the 'target' and 'actual' columns.
companyA 2012-01-28 105.00 39.00 NULL NULL NULL NULL
companyA 2012-02-05 105.00 15.00 NULL NULL NULL NULL
companyA 2012-02-13 105.00 60.50 companyB 2012-02-13 97.50 117.50
Any idea why I'm not getting all the information for companyB?
As a side note, would anybody be able to point in the direction of converting each row's week value into a column? With companyA and companyB as the only two rows?
I appreciate all the help! Thanks.
WITH no date apparent in the target table, the summation will be constant across all weeks. So, I have performed a pre-query for only those "org" values of company A and B with a group by. This will ensure only 1 record per "org" so you don't get a Cartesian result.
Then, I am querying the time_management_system ONCE for BOTH companies. Within the field computations, I am applying an IF() to test the company value and apply when correct. The WEEK activity is the same for both in the final result, so I don't have to do separately and join. This also prevents the need of having the date column appear twice. I also don't need to explicitly add the org column names as the final column names reflect that.
SELECT
WEEK( tms.date ) as GrpWeek,
IF( tms.org = "companyA", TargetSum.CompTarget, 00000.00 )) as CompanyATarget,
SUM( IF( tms.org = "companyA", tms.hours, 0000.00 )) as CompanyAHours,
IF( tms.org = "companyB", TargetSum.CompTarget, 00000.00 )) as CompanyBTarget,
SUM( IF( tms.org = "companyB", tms.hours, 000.00 )) as CompanyBHours
from
Time_Management_System tms
JOIN ( select
t.org,
SUM( t.target ) as CompTarget
from
Target T
where
t.org in ( "companyA", "companyB" )
group by
t.org ) as TargetSums
ON tms.org = TargetSums.org
where
tms.org in ( "companyA", "companyB" )
group by
WEEK( tms.date )
order by
WEEK( tms.date )
Both of your subqueries are wrong.
Either you want this:
SELECT
org,
WEEK(date),
( SELECT SUM( target ) FROM target WHERE org = "companyB" ) AS companyB_target,
SUM( hours ) AS companyB_actual
FROM time_management_system
WHERE org = "companyB"
GROUP BY WEEK( date )
Or else you want this:
SELECT
org,
date,
( SELECT SUM( target ) FROM target WHERE org = "companyB" ) AS companyB_target,
SUM( hours ) AS companyB_actual
FROM time_management_system
WHERE org = "companyB"
GROUP BY date
The way you are doing it now is not correctly formed SQL. In pretty much any other database your query would fail immediately with an error. MySQL is more lax and runs the query but gives indeterminate results.
GROUP BY and HAVING with Hidden Columns