MySQL CASE statement returning null - mysql

I have two 'identical' operations
(1)
SELECT ticket_type_name
, amount as 'original_amount'
, CASE ticket_type_name
WHEN ticket_type_name = 'General Admission - Friday'
and amount > 0 THEN amount / 400
END AS 'NewAmount'
from analytics.deleteme
(2)
select distinct ticket_type_name, amount, amount/400 AS NewAmount
from analytics.deleteme
where amount > 0
and ticket_type_name = 'General Admission - Friday'
The first statement generates a value of NULL
The second statement generates a correct value of 1 (400/400)
I have added an ELSE statement at the end:
SELECT DISTINCT ticket_type_name
, amount as 'original_amount'
, CASE ticket_type_name
WHEN ticket_type_name = 'General Admission - Friday' and amount > 0 THEN amount / 400
else amount/575
END AS 'NewAmount'
from analytics.deleteme
where amount > 0
Results are the reverse of what they should be!
GA Friday should be 400/400 = 1
Everything else, eg GA Weekend should be divided by 575 (=0.82, not 1.18!)
ticket_type_name, amount, NewAmount
General Admission - Friday 400.0 0.69
General Admission - Weekend 475.0 1.18
General Admission - Saturday 375.0 0.93
Children (12 to 14 Years) - Weekend 100.0 0.25
Children (12 to 14 Years) - Saturday 50.0 0.125

I could replicate your bug.
Just remove ticket_type_name after CASE and it works:
SELECT
DISTINCT ticket_type_name,
amount AS 'original_amount',
CASE
WHEN
ticket_type_name = 'General Admission - Friday'
AND amount > 0
THEN
amount / 400
ELSE
amount / 575
END AS 'NewAmount'
FROM analytics.deleteme
WHERE amount > 0;
You don't need to put anything between CASE and WHEN but it's funny how this broke the query. The first comment below perfectly explains why.
As an alternative, you may move ticket_type_name after CASE and put the comparison value after WHEN. Like this:
SELECT
DISTINCT ticket_type_name,
amount AS 'original_amount',
CASE ticket_type_name
WHEN
'General Admission - Friday'
THEN
amount / 400
ELSE
amount / 575
END AS 'NewAmount'
FROM analytics.deleteme
WHERE amount > 0;
Long story short: either use CASE field WHEN value… or CASE WHEN condition…. You don't want to use CASE field WHEN condition….

Related

Calculate sum when value changes

I am trying to build a system that will track vehicle fuelings, and have run into a problem with one report; determining fuel efficiency in distance/fuel. Sample data is:
odometer
fuel
partial_fillup
61290
10.3370
0
61542
6.4300
0
61735
4.3600
0
61994
7.5000
0
62242
5.4070
0
62452
8.1100
0
62713
5.7410
1
62876
9.4850
0
63243
6.1370
1
63499
10.7660
0
Where odometer is the total distance the vehicle has traveled, fuel is the number of gallons or liters put in, and partial_fillup is a boolean meaning the fuel tank was not completely filled if non-zero.
If the user fills the tank each time the query I can use is:
set #a = null;
select
odometer,
odometer-previousOdometer distance,
fuel,
(odometer-previousOdometer)/fuel mpg,
partial_fillup
from
(
select
#a as previousOdometer,
#a:=odometer,
odometer,
fuel/1000 fuel,
partial_fillup
from fuel
where
vehicle_id =1
and odometer >= 61290
order by odometer
) as readings
where readings.previousOdometer is not null;
However, when the user only partially fills the tank, the correct procedure would be to subtract the last full fueling from current odometer reading, then divide by the sum of all fuel since the previous odometer reading, so at odometer 63499, the calculate would be (63499-62876)/(10.7660+6.1370)
This will get the average used on the last ride:
select
odometer,
odometer-lag(odometer) over (order by odometer) as distance,
fuel,
(odometer-lag(odometer) over (order by odometer))/fuel as mpg
from fuel
output:
odometer
distance
fuel
mpg
61290
10.3370
61542
252
6.4300
39.1913
61735
193
4.3600
44.2661
61994
259
7.5000
34.5333
62242
248
5.4070
45.8665
62452
210
8.1100
25.8940
62713
261
5.7410
45.4625
62876
163
9.4850
17.1850
63243
367
6.1370
59.8012
63499
256
10.7660
23.7786
Or you can calculate the total drive distance, and the total amount of fuel used:
select
distance,
sum_fuel,
distance/sum_fuel as mpg
from (
select
f.odometer,
f.odometer-(select min(odometer) from fuel) as distance,
fuel,
sum_fuel
from fuel f
inner join (
select
odometer,
sum(fuel) over (order by R) as sum_fuel
from (
select
odometer,
fuel,
row_number() over (order by odometer) R
from fuel) x
) x on x.odometer = f.odometer
) x2
which will get next output, which will get closer to an average after a longer time of measurement:
distance
sum_fuel
mpg
0
10.3370
0.0000
252
16.7670
15.0295
445
21.1270
21.0631
704
28.6270
24.5922
952
34.0340
27.9720
1162
42.1440
27.5721
1423
47.8850
29.7170
1586
57.3700
27.6451
1953
63.5070
30.7525
2209
74.2730
29.7416
DBFIDDLE
I was able to figure it out after studying Luuk's answer. I'm sure there is a more efficient way to do this; I am not used to using variables in SQL. But, the answers are correct in the test data.
set #oldOdometer = null;
set #totalFuel = 0;
select
s.odometer,
format(fuel, 3) fuel,
s.distance,
format( distance / fuel, 2) as mpg
from (
select
partial_fillup as partial,
odometer,
(fuel+#totalFuel) as fuel,
#totalFuel as totalFuel,
#oldOdometer oldOdometer,
if ( partial_fillup, null,odometer - #oldOdometer ) as distance,
#totalFuel := if ( partial_fillup, #totalFuel + fuel, 0) as pastFuel,
#oldOdometer := if (partial_fillup,#oldOdometer,odometer ) as runningOdometer
from
fuel
order by
odometer ) s
where s.distance is not null
order by s.odometer
limit 1,999;
limit 1,999 simply there to skip the first row returned, since there is not enough data to calculate distance or mpg. On my copy of MySQL, doing this means you do not need to initialize the two variables (you don't have to include the set commands at the beginning), so it works with my reporting tool very well. If you do initialize them, you do not need the limit statement. Works assuming you don't have more than 999 rows returned.

How to use group by and case when

There is six games and I want to write query to output the net sales amount for each games and total sales based on Top 10 Retailers. Also, I will be great if you can output the percent of total sales for each retailer. Here is MySQL query:
SELECT Concat(css.retailerid,'-',rmd.retailer) AS retailer,
rmd.city,
CASE
WHEN css.gameid= 1 THEN Sum(css.netsales)
END AS 'Meqa 5/36',
CASE
WHEN css.gameid= 2 THEN Sum(css.netsales)
END AS '6/40',
CASE
WHEN css.gameid= 3 THEN Sum(css.netsales)
END AS '4+4',
CASE
WHEN css.gameid= 6 THEN Sum(css.netsales)
END AS 'Meqa 5',
CASE
WHEN css.gameid= 9 THEN Sum(css.netsales)
END AS 'Super Keno',
Sum(css.netsales) AS netsalesamount,
-- sum(css.NetSales)*100/(select sum(ss.NetSales) from reporting.core_sales_statistics AS ss) AS PERCENT,
-- sum(css.netsalesqty) AS netsalesqty,
-- Sum(css.NetSalesQty)*100/(select sum(ss.NetSalesQty) from reporting.core_sales_statistics AS ss) AS PERCENT
FROM reporting.core_sales_statistics AS css
LEFT JOIN reporting.retailer_master_data AS rmd ON css.retailerid=rmd.retailerid
WHERE css.datastatus=1
AND rmd.reportinglevel IN ('Exclusive Agents', 'Distributors' )
AND rmd.reportinglevel3 IN ('Exclusive Agents', 'Distributor Agents')
AND css.sourcesystem ='AEGIS'
AND css.transactiondate = curdate() -1
AND css.gamecategoryid=4
GROUP BY css.retailerid,
rmd.retailer
ORDER BY netsalesamount DESC limit 10
;

from and to ranges in mysql

Currently I have this kind of query
SELECT
part_number,
part_name,
attenuation_low_end,
attenuation_high_end,
optimum_fermentation_temp_f_low,
optimum_fermentation_temp_f_high
FROM
yeast_module
WHERE
category = 3
AND
(
( `attenuation_low_end` > '31' OR `attenuation_low_end` = '31' )
AND
( `attenuation_high_end` < '40' OR `attenuation_high_end` = '40' )
)
Where I'm trying to get the records with the range of low to high end from 31 and maximum of 40
But it returns me something like this
As you can notice it seems doesn't return the data between 31 to 40
Am I doing this right?
UPDATE
I'm expecting no return since, there's no data between 31-40
You're comparing strings, which performs lexicographic comparisons rather than numeric comparisons. You need to convert to numbers. Adding 0 to a numeric string is a simple way to convert it to a number.
WHERE 0+attenuation_low_end >= 31 AND 0+attenuation_high_end <= 40
If you want ranges contained in the 31-40 range:
where attenuation_low_end >= 31 and attenuation_high_end <= 40
If you want ranges that overlap the 31-40 range:
where attenuation_low_end <= 40 and attenuation_high_end >= 31
If your data is of a string datatype, then you need to convert the values to integers so they can be compared as such.
Containment:
where attenuation_low_end + 0 >= 31 and attenuation_high_end + 0 <= 40
Overlap:
where attenuation_low_end + 0 <= 40 and attenuation_high_end + 0 >= 31

Using Previous Record Function in Formula and then creating a Group Summary

I have been trying to figure this out and I am stumped. I am finding the difference in minutes between two times by using the following formula:
This formula is called {#CALCULATIONS}
if {Location.Name} = previous ({Location.Name}) then
( hour({DownloadData.Date}) * 60 + minute({DownloadData.Date}) )-
( hour(previous({DownloadData.Date})) * 60 + minute(previous({DownloadData.Date})) )
else
0
I then uses the answer it comes up with in a formula that determines if it meets a criteria. If it does I assign it a 1 if not I assign it a zero. Here are the 5 different formulas I have for time ranges.
{#1 to 30} if {#Calculations} in 1 to 30 then 1 else 0
{#31 to 45} if {#Calculations} in 31 to 45 then 1 else 0
{#46 to 60} if {#Calculations} in 46 to 60 then 1 else 0
{#61 to 90} if {#Calculations} in 61 to 90 then 1 else 0
{#Morethan90} if {#Calculations} >90 then 1 else 0
For a while I have been trying to figure out how to get it to total the 1s that meet the formula criteria in the group.
Any suggestions?

Why is this IIF function giving an #Error?

One of my tables has a field named Cost and a field named Extra Cost. To come up with the Total Cost, I add them together with the following field, which works just fine:
Total Cost: (Val(nz([Cost],"")))/100 + (Val(nz([Extra Cost],"")))/100
(I divide by 100 because Cost and Extra Cost are stored without a decimal point)
Now it's possible that a record will have Cost = 0, and Extra Cost > 0. But if Cost = 0, I want Total Cost to also = 0. I came up with the following, but it results in #Error if Cost = 0. It works fine if Cost > 0:
Total Cost: IIf([Cost]>0,((Val(nz([Cost],"")))/100+(Val(nz([Extra Cost],""))))/100,0)
Basically I'm looking for:
If Cost = 0, Then Total Cost = 0
Else
If Cost > 0, Then Total Cost = Cost + Extra Cost
What is wrong with the 'true' portion?
Here's a few examples of the data:
Cost Extra Cost
100 2.5
250 1.5
150 2.5
null 2.75
Based on your description, I think you can divide by 100 after you add the 2 values instead of dividing each of them by 100 before you add them. That shouldn't affect the logic, but should give you a simpler IIf expression ... which will hopefully be easier to diagnose.
IIf
(
Val(Nz([Cost], "0")) > 0,
(Val([Cost]) + Val(Nz([Extra Cost], "0"))) / 100,
0
)
Using your sample data in Access 2007, I get this result set from the following query:
Cost Extra Cost Total Cost
100 2.5 1.025
250 1.5 2.515
150 2.5 1.525
2.75 0
SELECT
y.Cost,
y.[Extra Cost],
IIf
(
Val(Nz([Cost], "0")) > 0,
(Val([Cost]) + Val(Nz([Extra Cost], "0"))) / 100,
0
) AS [Total Cost]
FROM YourTable AS y;
If the issue is that [Total Cost] requires a text value, you can use CStr() to cast the IIf numerical value to string.
CStr(
IIf
(
Val(Nz([Cost], "0")) > 0,
(Val([Cost]) + Val(Nz([Extra Cost], "0"))) / 100,
0
)
)