MDX: What is the val() function in MDX - ssas

MDX: What is the val() function in MDX
WITH MEMBER [Measures].[Store Size] AS
VAL([DimMangedPopulation].[Managed Population Key].CURRENTMEMBER.PROPERTIES("Program Name"))
SELECT
{[Measures].[Store Size]} ON COLUMNS,
[DimManagedPopulation].[Program Name].&[BHI] ON ROWS
FROM Sales
Please see the below image for output

Looking back at the image, it seems you used a wrong choice of words:
"What did the val() function in MDX"
Your question should have been, "Why it returns 0"
Assuming, my deduction is correct, the answer is : Val() would return a value of 0 for a character string argument. You can check this with a simple example.
with member abc as val("abc")
select abc on 0
from [AdvWorks]
In your case, you selecting the property "Program Name", which seems to be a character string rather than a number, and hence you get a 0.
HTH

Related

How to fix the “divide by zero” error in SQL? [duplicate]

I have this error message:
Msg 8134, Level 16, State 1, Line 1 Divide by zero error encountered.
What is the best way to write SQL code so that I will never see this error message again?
I could do either of the following:
Add a where clause so that my divisor is never zero
Or
I could add a case statement, so that there is a special treatment for zero.
Is the best way to use a NULLIF clause?
Is there better way, or how can this be enforced?
In order to avoid a "Division by zero" error we have programmed it like this:
Select Case when divisor=0 then null
Else dividend / divisor
End ,,,
But here is a much nicer way of doing it:
Select dividend / NULLIF(divisor, 0) ...
Now the only problem is to remember the NullIf bit, if I use the "/" key.
In case you want to return zero, in case a zero devision would happen, you can use:
SELECT COALESCE(dividend / NULLIF(divisor,0), 0) FROM sometable
For every divisor that is zero, you will get a zero in the result set.
This seemed to be the best fix for my situation when trying to address dividing by zero, which does happen in my data.
Suppose you want to calculate the male–female ratios for various school clubs, but you discover that the following query fails and issues a divide-by-zero error when it tries to calculate ratio for the Lord of the Rings Club, which has no women:
SELECT club_id, males, females, males/females AS ratio
FROM school_clubs;
You can use the function NULLIF to avoid division by zero. NULLIF compares two expressions and returns null if they are equal or the first expression otherwise.
Rewrite the query as:
SELECT club_id, males, females, males/NULLIF(females, 0) AS ratio
FROM school_clubs;
Any number divided by NULL gives NULL, and no error is generated.
You can also do this at the beginning of the query:
SET ARITHABORT OFF
SET ANSI_WARNINGS OFF
So if you have something like 100/0 it will return NULL. I've only done this for simple queries, so I don't know how it will affect longer/complex ones.
You can at least stop the query from breaking with an error and return NULL if there is a division by zero:
SELECT a / NULLIF(b, 0) FROM t
However, I would NEVER convert this to Zero with coalesce like it is shown in that other answer which got many upvotes. This is completely wrong in a mathematical sense, and it is even dangerous as your application will likely return wrong and misleading results.
SELECT Dividend / ISNULL(NULLIF(Divisor,0), 1) AS Result from table
By catching the zero with a nullif(), then the resulting null with an isnull() you can circumvent your divide by zero error.
EDIT:
I'm getting a lot of downvotes on this recently...so I thought I'd just add a note that this answer was written before the question underwent it's most recent edit, where returning null was highlighted as an option...which seems very acceptable. Some of my answer was addressed to concerns like that of Edwardo, in the comments, who seemed to be advocating returning a 0. This is the case I was railing against.
ANSWER:
I think there's an underlying issue here, which is that division by 0 is not legal. It's an indication that something is fundementally wrong. If you're dividing by zero, you're trying to do something that doesn't make sense mathematically, so no numeric answer you can get will be valid. (Use of null in this case is reasonable, as it is not a value that will be used in later mathematical calculations).
So Edwardo asks in the comments "what if the user puts in a 0?", and he advocates that it should be okay to get a 0 in return. If the user puts zero in the amount, and you want 0 returned when they do that, then you should put in code at the business rules level to catch that value and return 0...not have some special case where division by 0 = 0.
That's a subtle difference, but it's important...because the next time someone calls your function and expects it to do the right thing, and it does something funky that isn't mathematically correct, but just handles the particular edge case it's got a good chance of biting someone later. You're not really dividing by 0...you're just returning an bad answer to a bad question.
Imagine I'm coding something, and I screw it up. I should be reading in a radiation measurement scaling value, but in a strange edge case I didn't anticipate, I read in 0. I then drop my value into your function...you return me a 0! Hurray, no radiation! Except it's really there and it's just that I was passing in a bad value...but I have no idea. I want division to throw the error because it's the flag that something is wrong.
Replacing "divide by zero" with zero is controversial - but it's also not the only option. In some cases replacing with 1 is (reasonably) appropriate. I often find myself using
ISNULL(Numerator/NULLIF(Divisor,0),1)
when I'm looking at shifts in scores/counts, and want to default to 1 if I don't have data. For example
NewScore = OldScore * ISNULL(NewSampleScore/NULLIF(OldSampleScore,0),1)
More often than not, I've actually calculated this ratio somewhere else (not least because it can throw some very large adjustment factors for low denominators. In this case I'd normally control for OldSampleScore is greater than a threshold; which then precludes zero. But sometimes the 'hack' is appropriate.
I wrote a function a while back to handle it for my stored procedures:
print 'Creating safeDivide Stored Proc ...'
go
if exists (select * from dbo.sysobjects where name = 'safeDivide') drop function safeDivide;
go
create function dbo.safeDivide( #Numerator decimal(38,19), #divisor decimal(39,19))
returns decimal(38,19)
begin
-- **************************************************************************
-- Procedure: safeDivide()
-- Author: Ron Savage, Central, ex: 1282
-- Date: 06/22/2004
--
-- Description:
-- This function divides the first argument by the second argument after
-- checking for NULL or 0 divisors to avoid "divide by zero" errors.
-- Change History:
--
-- Date Init. Description
-- 05/14/2009 RS Updated to handle really freaking big numbers, just in
-- case. :-)
-- 05/14/2009 RS Updated to handle negative divisors.
-- **************************************************************************
declare #p_product decimal(38,19);
select #p_product = null;
if ( #divisor is not null and #divisor <> 0 and #Numerator is not null )
select #p_product = #Numerator / #divisor;
return(#p_product)
end
go
Add a CHECK constraint that forces Divisor to be non-zero
Add a validator to the form so that the user cannot enter zero values into this field.
For update SQLs:
update Table1 set Col1 = Col2 / ISNULL(NULLIF(Col3,0),1)
There is no magic global setting 'turn division by 0 exceptions off'. The operation has to to throw, since the mathematical meaning of x/0 is different from the NULL meaning, so it cannot return NULL.
I assume you are taking care of the obvious and your queries have conditions that should eliminate the records with the 0 divisor and never evaluate the division. The usual 'gotcha' is than most developers expect SQL to behave like procedural languages and offer logical operator short-circuit, but it does NOT. I recommend you read this article: http://www.sqlmag.com/Articles/ArticleID/9148/pg/2/2.html
Here is a situation where you can divide by zero. The business rule is that to calculate inventory turns, you take cost of goods sold for a period, annualize it. After you have the annualized number, you divide by the average inventory for the period.
I'm looking at calculating the number of inventory turns that occur in a three month period. I have calculated that I have Cost of Goods sold during the three month period of $1,000. The annual rate of sales is $4,000 ($1,000/3)*12. The beginning inventory is 0. The ending inventory is 0. My average inventory is now 0. I have sales of $4000 per year, and no inventory. This yields an infinite number of turns. This means that all my inventory is being converted and purchased by customers.
This is a business rule of how to calculate inventory turns.
CREATE FUNCTION dbo.Divide(#Numerator Real, #Denominator Real)
RETURNS Real AS
/*
Purpose: Handle Division by Zero errors
Description: User Defined Scalar Function
Parameter(s): #Numerator and #Denominator
Test it:
SELECT 'Numerator = 0' Division, dbo.fn_CORP_Divide(0,16) Results
UNION ALL
SELECT 'Denominator = 0', dbo.fn_CORP_Divide(16,0)
UNION ALL
SELECT 'Numerator is NULL', dbo.fn_CORP_Divide(NULL,16)
UNION ALL
SELECT 'Denominator is NULL', dbo.fn_CORP_Divide(16,NULL)
UNION ALL
SELECT 'Numerator & Denominator is NULL', dbo.fn_CORP_Divide(NULL,NULL)
UNION ALL
SELECT 'Numerator & Denominator = 0', dbo.fn_CORP_Divide(0,0)
UNION ALL
SELECT '16 / 4', dbo.fn_CORP_Divide(16,4)
UNION ALL
SELECT '16 / 3', dbo.fn_CORP_Divide(16,3)
*/
BEGIN
RETURN
CASE WHEN #Denominator = 0 THEN
NULL
ELSE
#Numerator / #Denominator
END
END
GO
Filter out data in using a where clause so that you don't get 0 values.
Sometimes, 0 might not be appropriate, but sometimes 1 is also not appropriate. Sometimes a jump from 0 to 100,000,000 described as 1 or 100-percent change might also be misleading. 100,000,000 percent might be appropriate in that scenario. It depends on what kind of conclusions you intend to draw based on the percentages or ratios.
For example, a very small-selling item moving from 2-4 sold and a very large-selling item changing from 1,000,000 to 2,000,000 sold might mean very different things to an analyst or to management, but would both come through as 100% or 1 change.
It might be easier to isolate NULL values than to scour over a bunch of 0% or 100% rows mixed with legitimate data. Often, a 0 in the denominator can indicate an error or missing value, and you might not want to just fill in an arbitrary value just to make your dataset look tidy.
CASE
WHEN [Denominator] = 0
THEN NULL --or any value or sub case
ELSE [Numerator]/[Denominator]
END as DivisionProblem
This is how I fixed it:
IIF(ValueA != 0, Total / ValueA, 0)
It can be wrapped in an update:
SET Pct = IIF(ValueA != 0, Total / ValueA, 0)
Or in a select:
SELECT IIF(ValueA != 0, Total / ValueA, 0) AS Pct FROM Tablename;
Thoughts?
You can handle the error appropriately when it propagates back to the calling program (or ignore it if that's what you want). In C# any errors that occur in SQL will throw an exception that I can catch and then handle in my code, just like any other error.
I agree with Beska in that you do not want to hide the error. You may not be dealing with a nuclear reactor but hiding errors in general is bad programming practice. This is one of the reasons most modern programming languages implement structured exception handling to decouple the actual return value with an error / status code. This is especially true when you are doing math. The biggest problem is that you cannot distinguish between a correctly computed 0 being returned or a 0 as the result of an error. Instead any value returned is the computed value and if anything goes wrong an exception is thrown. This will of course differ depending on how you are accessing the database and what language you are using but you should always be able to get an error message that you can deal with.
try
{
Database.ComputePercentage();
}
catch (SqlException e)
{
// now you can handle the exception or at least log that the exception was thrown if you choose not to handle it
// Exception Details: System.Data.SqlClient.SqlException: Divide by zero error encountered.
}
Use NULLIF(exp,0) but in this way - NULLIF(ISNULL(exp,0),0)
NULLIF(exp,0) breaks if exp is null but NULLIF(ISNULL(exp,0),0) will not break

Returning a varchar value from a coalesced int calculation

I'm a newbie learning my way around T-SQL using the AdventureWorks2012 database. I'm using SQL Server 2014, though a solution that would also work with 2008 would be great. I've been given the below exercise:
Write a query using the Sales.SpecialOffer table. Display the difference between the MinQty and MaxQty columns along with the SpecialOfferID and Description columns.
Thing is, MaxQty allows for null values, so I'm trying to come up with a real world solution for an output that doesn't involve leaving nulls in there. However, when I try to use coalesce to return 'No Max' (yes, I get that I could just leave NULL in there but I'm trying to see if I can figure this out), I get the message that the varchar value 'No Max' couldn't be converted to data type int. I'm assuming this is because MaxQty - MinQty as an int takes precedence?
select
specialofferid
, description
, coalesce((maxqty - minqty),'No Max') 'Qty_Difference'
from
sales.specialoffer;
Error:
Msg 245, Level 16, State 1, Line 135
Conversion failed when converting the varchar value 'No max' to data type int.
I thought about just returning a nonsense integer (0 or a negative) but that doesn't seem perfect - if return 0 I'm obscuring situations where the result is actually zero, etc.
Thoughts?
You just need to make sure that all the parameters of the COALESCE function call have consistent data types. Because you can't get around the fact No Max is a string, then you have to make sure that the maxqty - minqty part is also treated as a string by casting the expression.
select specialofferid
, description
, coalesce(cast(maxqty - minqty as varchar),'No Max') 'Qty_Difference'
from sales.specialoffer;
EDIT: A few more details on the cause of the error
Without the explicit cast, the reason why the COALESCE function attempts to convert the No Max string to an int can be explained by the following documented rule:
Data type determination of the resulting expression is different. ISNULL uses the data type of the first parameter, COALESCE follows the CASE expression rules and returns the data type of value with the highest precedence.
And if you check the precedence of the different types, as documented here, then you will see that int has higher precedence than varchar.
So as soon as you have a mix of data types in the call to COALESCE, SQL Server will try to convert all mismatching parameters to the data type with highest precedence, in this case int. To override that default behavior, explicit type casting is required.
I would use a case statement to so you can do stuff you want.
select specialofferid
, description
, CASE
WHEN maxqty is null THEN 'No Max'
ELSE (maxqty - minqty) 'Qty_Difference'
END
from sales.specialoffer;

SQL and iReport - Aggregate functions in the Where clause and Parameters

Well basically I would like to be able to use a parameter on iReport with an aggregate function.
If you type "yes" it will show you the values greater than 0, if you type "no" it will show the values that are less than 0. However, the aggregate function first adds up all the values related to an id and then it subtracts the result from another value, the result of that is the one I want to show.
How would I be able to do this? I'm clueless as I don't know how to use it with HAVING.
I don't understand what 'it' refers to in "I don't know how to use it with HAVING." The question will be much clearer with some SQL. But I guess you're looking for this:
SELECT id, sum(values) as the_agg
FROM table1
GROUP BY id
HAVING sum(values) $P!{BiggerOrSmaller} 0
The default value for the parameter BiggerOrSmaller should be like this:
$P{MyParam}.equals("yes") ? ">" : "<"
This assumes you have a parmeter called MyParam which can take the value "yes". Based on that value it sets the parameter BiggerOrSmaller appropriately.

SQL function Instr()

I am having a column named DP as shown:
07-APR-2011
12-APR-2011
26-APR-2011
Now to retrieve the query for selecting the payments made in the month of april i came across a query
select * from payments where instr(dp,'APR')<>0
Okay , i am well acquainted with INSTR function and > sign , but cant interpret the logic with<> sign here !
[UPDATE]
i am also aware that <> is equivalent of != .
But my point is we could have used
instr(dp,'APR') instead of doing instr(dp,'APR')<>0
<> means "is not equal to". You can also write !=, if you prefer.
instr(dp,'APR') returns zero if 'APR' is not a substring of dp, so instr(dp,'APR')<>0 means "'APR' is a substring of dp". It could also be written as dp LIKE '%APR%'.
Update for updated question:
But my point is we could have used instr(dp,'APR') instead of doing instr(dp,'APR')<>0
No, you couldn't have. Some dialects of SQL treat zero as "false" and other integers as "true", but Oracle does not do this. It treats integers and Booleans as separate types, and does not implicitly convert between them. WHERE 0 is not a valid WHERE-clause.
<> is Not Equals - basically it's checking that a substring of 'APR' appears in the string.
If that function returned 0 then it would indicate 'APR' does not appear anywhere in the string to be searched.

SQL DB2 null calculation causing problems

I have the following SQL:
Select dmvndn "Vendor Number", IFNULL(sum(dmsls) / sum(dmprc), 0) "Calculation"
From MyFile
Group By dmvndn
However, when i run this, i am still getting null records in my "Calculation" field.
I have also tried the COALESCE function, which returns the same results. I get some records as 0, and some records are blank (or, null).
Both fields are of type P, which i am told is packed numeric.
any ideas or suggestions?
Edit 1
It seems that the problem is not with either of these fields being NULL, it is that one or both fields are 0. And when i divide by zero, i get the empty / blank result.
Try
Sum(IFNULL(dmsls,0)) / Sum(IFNULL(dmprc,0))
A trick of this kind helps me in MS SQL Server:
Select
dmvndn "Vendor Number",
IFNULL(sum(dmsls) / NULLIF(sum(dmprc), 0), 0) "Calculation"
From MyFile
Group By dmvndn
I wonder if it can't help you in DB2.
UPDATE: an explanation.
Basically, it replaces the divisor with NULL if it's 0. And you may probably know that when at least one of the operands is NULL, the result of the operation becomes NULL as well.
To account for the result being NULL you already had your IFNULL on the result. It didn't make much difference then, because none of the operands was likely to be NULL. However, now using IFNULL makes perfect sense.