CASTING to NUMERIC in SQL - sql

I am trying to understand the ARPU calculation in SQL from the following code, however I don't understand why the author has used NUMERIC with revenue in the 2nd query? Won't revenue (meal_price * order quantity) be numeric anyway?

The issue is probably the following. NUMERIC is a specific data type. However, it is not clear that meal_price and order_quantity are specifically NUMERIC -- and not some other type such as INT.
Many databases do integer division for INT, so 1 / 2 is 0 rather than 0.5.
The conversion to NUMERIC is a simple way to avoid integer division.

Of course if a and b are numeric types , a * b will be numeric type
But there are many different numeric types, see
https://www.postgresql.org/docs/13/datatype-numeric.html
NUMERIC is a KEYWORK to specify numeric type of arbitrary précision, see previous link, it's often used to do exact calculations (accouinting) that cannoy be done in foating type.
In your case the author choosed to define the type he wants to use and not let the system/db choose for him. (try to figure out if a and b are integer what shoult be the type of the result 2 * 4 / 3 ?). It's a good practice.

Related

Shouldn't binary_double store a higher value than number in Oracle?

Considering the following test code :
CREATE TABLE binary_test (bin_float BINARY_FLOAT, bin_double BINARY_DOUBLE, NUM NUMBER);
INSERT INTO binary_test VALUES (4356267548.32345E+100, 4356267548.32345E+2+300, 4356267548.32345E+100);
SELECT CASE WHEN bin_double>to_binary_double(num) THEN 'Greater'
WHEN bin_double=to_binary_double(num) THEN 'Equal'
WHEN bin_double<to_binary_double(num) THEN 'Lower'
ELSE 'Unknown' END comparison,
A.*
FROM binary_test A;
I've tried to see which one stores higher values. If I try to add E+300 for the number and binary_float columns, it returns numeric overflow error. So, I thought I could store a greater value with the binary_float.
However, when I tried to check it, it shows a lower value, and with the case comparison it says it is lower too. Could you please elaborate this situation?
You are inserting the value 4356267548.32345E+2+300 into the binary double column. That evaluates to 4356267548.32345E+2, which is 435626754832.345, plus 300 - which is 435626755132.345 (or 4.35626755132345E+011, which becomes 4.3562675513234497E+011 when converted to binary double). That is clearly lower than 4356267548.32345E+100 (or 4.35626754832345E+109, which becomes 4.3562675483234496E+109 when converted to binary double).
Not directly relevant, but you should also be aware that you're providing a decimal number literal, which will be implicitly converted to binary double during insert. So you can't use 4356267548.32345E+300, as that is too large for the number data type. If you want to specify a binary double literal then you need to append a d to it, i.e. 4356267548.32345E+300d; but that is still too large.
The highest you can go with that numeric part is 4356267548.32345E+298d, which evaluates to 4.3562675483234498E+307 - just below the data type limit of 1.79769313486231E+308; and note the loss of precision.
db<>fiddle

Placeholder for Decimal Column in SQL

I am new to SQL so this question is likely simple and easy to answer.
I am creating a temp table in which I want a blank column to be filled later with decimal values.
Can I use as a placeholder in my SELECT statement to indicate that I want decimal values to fill the column?
For columns that I will fill with integer values, I am using the following code:
SELECT 0 AS ColumnName
I do not believe this will work for the column that I want filled with decimal values, as I believe the 0 indicates integer values instead. Is there something that I can use instead of the 0?
Any help would be appreciated!
SQL will perform implicit conversion of some types when it knows there is no risk of data loss. In your case, int can safely convert to decimal, because there's no way to corrupt your data. 0 is 0.0 as far as SQL is concerned.
The opposite would not be true, as casting from decimal to int would lose the decimal part. Therefore SQL would not implicitly cast the opposite.
Your query is good as is.
Using a 0 in a float field will not cause truncation on the record since it contains less precision than a float, so it is safe to do this and implies a 0.0
You could also DEFAULT the field to 0.0 in the table declaration so it will be 0.0 until updated. But not required.
Ex: CREATE TABLE table
( col1 FLOAT(size,d) DEFAULT 0.0 )

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;

Value of real type incorrectly compares

I have field of REAL type in db. I use PostgreSQL. And the query
SELECT * FROM my_table WHERE my_field = 0.15
does not return rows in which the value of my_field is 0.15.
But for instance the query
SELECT * FROM my_table WHERE my_field > 0.15
works properly.
How can I solve this problem and get the rows with my_field = 0.15 ?
To solve your problem use the data type numeric instead, which is not a floating point type, but an arbitrary precision type.
If you enter the numeric literal 0.15 into a numeric (same word, different meaning) column, the exact amount is stored - unlike with a real or float8 column, where the value is coerced to next possible binary approximation. This may or may not be exact, depending on the number and implementation details. The decimal number 0.15 happens to fall between possible binary representations and is stored with a tiny error.
Note that the result of a calculation can be inexact itself, so be still wary of the = operator in such cases.
It also depends how you test. When comparing, Postgres coerces diverging numeric types to a type that can best hold the result.
Consider this demo:
CREATE TABLE t(num_r real, num_n numeric);
INSERT INTO t VALUES (0.15, 0.15);
SELECT num_r, num_n
, num_r = num_n AS test1 --> FALSE
, num_r = num_n::real AS test2 --> TRUE
, num_r - num_n AS result_nonzero --> float8
, num_r - num_n::real AS result_zero --> real
FROM t;
db<>fiddle here
Old sqlfiddle
Therefore, if you have entered 0.15 as numeric literal into your column of data type real, you can find all such rows with:
SELECT * FROM my_table WHERE my_field = real '0.15'
Use numeric columns if you need to store fractional digits exactly.
Your problem originates from IEEE 754.
0.15 is not 0.15, but 0.15000000596046448 (assuming double precision), as it can not be exactly represented as a binary floating point number.
(check this calculator)
Why is this a problem? In this case, most likely because the other side of the comparison uses the exact value 0.15 - through an exact representation, like a numeric type. (Cleared up on suggestion by Eric)
So there are two ways:
use a format that actually stores the numbers in decimal format - as Erwin suggested
(or at least use the same type across the board)
use rounding as Jack suggested - which has to be used carefully (by the way this uses a numeric type too, to exactly represent 0.15...)
Recommended reading:
What Every Computer Scientist Should Know About Floating-Point Arithmetic
(Sorry for the terse answer...)
Well, I can't see your data, but I'm guessing that my_field doesn't exactly equal 0.15. Try:
select * from my_table where round(my_field::numeric,2) = 0.15;
Considering both PPTerka's and Jack's answer.
Approximate numeric data types do not store the exact values specified for many numbers;
Look here for MS' decription of real values.
http://technet.microsoft.com/en-us/library/ms187912(v=sql.105).aspx

Multiplication with NULL and empty column values in SQL

This was my Interview Question
there are two columns called Length and Breadth in Area table
Length Breadth Length*Breadth
20 NULL ?
30 ?
21.2 1 ?
I tried running the same question on MYSQL while inserting,To insert an empty value I tried the below query . Am I missing anything while inserting empty values in MYSQL.
insert into test.new_table values (30,);
Answers: With Null,Result is Null.
With float and int multiplication result is float
As per your question the expected results would be as below.
SELECT LENGTH,BREADTH,LENGTH*BREADTH AS CALC_AREA FROM AREA;
LENGTH BREADTH CALC_AREA
20
30 0 0
21.2 1 21.2
For any(first) record in SQL SERVER if you do computation with NULL the answer would be NULL.
For any(second) record in SQL SERVER, if you do product computation between a non-empty value and an empty value the result would be zero as empty value is treated as zero.
For any(third) record in SQL SERVER, if you do computation between two non-empty data type values the answer would be a NON-EMPTY value.
Check SQL Fiddle for reference - http://sqlfiddle.com/#!3/f250a/1
That blank Breath (second row) cannot happen unless Breath is VARCHAR. Assuming that, the answers will be:
NULL (since NULL times anything is NULL)
Throws error (since an empty string is not a number. In Sql Server, the error is "Error converting data type varchar to numeric.")
21.20 (since in Sql Server, for example, conversion to a numeric type is automatic, so SELECT 21.2 * '1' returns 21.20).
Assuming that Length and Breadth are numerical types of some kind the second record does not contain possible values — Breadth must be either 0 or NULL.
In any event, any mathematical operation in SQL involving a NULL value will return the value NULL, indicating that the expression cannot be evaluated. The answer are NULL, impossible, and 21.2.
The product of any value and NULL is NULL. This is called "NULL propagation" if you want to Google it. To score points in an interview, you might want to mention that NULL isn't a value; it's a special marker.
The fact that the column Breadth has one entry "NULL" and one entry that's blank (on the second row) is misleading. A numeric column that doesn't have a value in a particular row means that row is NULL. So the second column should also show "NULL".
The answer to the third row, 21.2 * 1, depends on the data type of the column "Length*Breadth". If it's a data type like float, double, or numberic(16,2), the answer is 21.2. If it's an integer column (integer, long, etc.), the answer is 21.
A more snarky answer might be "There's no answer. The string "Length*Breadth" isn't a legal SQL column name."
In standard SQL they would all generate errors because you are comparing values (or nulls) of different types:
CAST ( 20 AS FLOAT ) * CAST ( NULL AS INTEGER ) -- mismatched types error
CAST ( '' AS INTEGER ) -- type conversion error
CAST ( AS INTEGER ) -- type conversion error
CAST ( 21.2 AS FLOAT ) * CAST ( 2 AS INTEGER ) -- mismatched types error
On the other hand, most SQL product would implicitly cast values when comparing values (or nulls) of different types according to type precedence e.g. comparing float value to an integer value would in effect cast the integer to float and result in a float. At the product level, the most interesting question is what happens when you compare a null of type integer with a value (or even a null) of type float...
...but, frankly, not terribly interesting. In an interview you are presented with a framework (in the form of questions asked of you) on which to present your knowledge, skills and experience. The 'answer' here is to discuss nulls (e.g. point out that nulls are tricky to define and behave in unintuitive ways, which leads to frequent bugs and a desire to avoid nulls entirely, etc) and whether implicit casting is a good thing.