I need to created domain in PostgreSQL for a price. The price must be NUMERIC(9,2) where 9 is precision and 2 - scale. When trying to create domain getting:
ERROR: operator does not exist: numeric ~* unknown
Hint: No operator matches the given name and argument type(s). You might need to add explicit type casts.
QUERY:
CREATE DOMAIN d_price AS NUMERIC(9, 2) NOT NULL
CONSTRAINT Product_price_can_contain_only_double_precision_value
CHECK(VALUE ~*'^(([[:digit:]])+\.([[:digit:]]){2})$');
You need your numeric value as a string before you can use the string operator, change your VALUE to: CAST(VALUE AS TEXT)
Your CHECK constraint is nonsensical, because it applies after the value has already been converted to NUMERIC by the database engine's number parser.
VALUE ~*'^(([[:digit:]])+\.([[:digit:]]){2})$')
appears to say "one or more leading digits, a period, and exactly two trailing digits". You can't do that check in any useful way once the number has already been parsed. Observe:
regress=> SELECT NUMERIC(18,2) '1', NUMERIC(18,2) '1.12345';
numeric | numeric
---------+---------
1.00 | 1.12
(1 row)
No matter what the input is, if it fits inside the NUMERIC you've specified, it'll be extended to fit. If it doesn't fit the NUMERIC size you've given it'll produce an error before your CHECK constraint ever runs.
Related
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
I need store exactly numeric data in database.
Let say have to save 123.200 or 123.1 exactly into database.
But result will come up 123.20 or 123.10 in database if column type set to decimal with fixed 2 digit.
What I can do if I just want 123.200 or 132.1 shown on database/report?
No need system auto convert to any other decimal.
You can store the value "as is" in the varchar type.
The problem with this approach is that database would allow to store any string there, even if it is not a number, say 10abc.xyz23
If you need to know how to present the number to the user, you need to store this information somehow. Since, each number in the column may be formatted differently, you need to store this formatting information for each row.
I'd store it as decimal type with large enough scale and precision to cover all possible ranges of your data and in addition to that have extra column DecimalPlaces, which would contain the number of decimal places your reporting engine should use when displaying the value.
If you must do this, then as others have suggested, you'll need to use a character data type to store it. I'd also add a computed column that makes the numeric value readily available also:
create table T (
Val varchar(39) not null,
Val_numeric as CONVERT(decimal(38,10),Val) persisted
)
go
insert into T(Val) values
('123.200'),
('123.1')
select * from T
Results:
Val Val_numeric
--------------------------------------- ---------------------------------------
123.200 123.2000000000
123.1 123.1000000000
When you need the "user entered" value, you use Val. When you need the real value, you use Val_Numeric. This also has the advantage that (without a complex check constraint), you cannot enter any invalid values into the Val column. E.g.:
insert into T(Val) values ('1.2.3')
Produces the error:
Msg 8114, Level 16, State 5, Line 12
Error converting data type varchar to numeric.
I'm trying to implement a check constraint on a key field. The key field is composed of a 3 character prefix, and then appended with numeric characters (which can be provided manually, but the default is to get an integer value from a sequence, which is then cast as nvarchar). The key field is defined as nvarhcar(9).
I'm doing this for multiple tables, but here is a specific example below to demonstrate:
Table name: Company
Key field: IDCompany
Key field prefix: CMP
Examples of valid keys -
CMP1
CMP01
CMP10000
CMP999999
Examples of invalid keys -
CMPdog1
steve
1CMP1
1
999999999
The check constraint I came up with was:
IDCompany LIKE 'CMP%[0-9]'
However, this is beaten by CMPdog1 etc.
What should I be using as a check constraint to enforce an unknown number of numeric characters?
I could do the following:
IDCompany LIKE 'CMP[0-9]' OR IDCompany LIKE 'CMP[0-9][0-9]' OR .... through to 6 characters
But, this seems like a clunky way of doing it, is there something smarter?
EDIT 2: This actually doesn't work, it does not exclude negative numbers:
EDIT 1:
This solution ended up working for me:
IDCompany nvarchar(9) NOT NULL CONSTRAINT DEF_Company_IDCompany DEFAULT 'CMP' + CAST((NEXT VALUE FOR dbo.sq_Company) AS nvarchar) CONSTRAINT CHK_Company_IDCompany CHECK (IDCompany LIKE 'CMP%[0-9]' AND ISNUMERIC(SUBSTRING(IDCompany,4,LEN(IDCompany)-3))=1)
EDIT 3: Solution -
As proposed in Szymon's post below.
Thanks all!
You could do something like that:
where LEFT(IDCompany, 3) = 'CMP'
and isnumeric(RIGHT(IDCompany, len(IDCompany) - 3)) = 1
and IDCompany not like '%[.,-]%'
The first part checks that it starts with CMP
The next part is to make sure that the rest is numeric but excluding negative and decimal numbers.
Well, I would reconsider the design of your table and create 3 columns:
prefix, CHAR(3), with a default as 'CMP' and a constraint to allow only 'CMP' combination
id, INTEGER
companyid, NVARCHAR(9), a computed, persisted column as sum of the first 2 columns. Most probably with an index on.
Unfortunately, SQL Server doesn't suppport regular expressions.
So there is only 2 ways to solve your problem:
Use CLR function for using regular expression. You may find more information here
Or whrite long WHERE clause like you suggested:
IDCompany LIKE 'CMP[0-9]' OR IDCompany LIKE 'CMP[0-9][0-9]' OR ....
Try this:
isnumeric(substring(IDCompany,4,len(IDCompany)))=1 and IDCompany not like '%[.,-]%'
How this works: The first three characters are fixed, so we only need to check from the 4th character onwards. So we get the required substring. Then, we use isNumeric to check if the substring is entirely numeric. Example here
EDIT: As pointed out in comments by Allan, we need an extra check to ensure that characters used in numeric strings like commas or dots are not part of the input string.
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.
I have a piece of my query in Oracle that generates discrete percentile:
...
PERCENTILE_DISC(0.9999) WITHIN GROUP(ORDER BY DURATION_COUNT) as PERCENTILE_9999_QTY,
...
The data type of PERCENTILE_9999_QTY is Number(8).
It works fine except in some cases I get this error:
ORA-01438: value larger than specified precision allowed for this
column
I prefer not to change the Number(8) data type. Is there a way to insure that the value fits into the Number(8) precision?
If the result cannot fit in your N(8) column then there's not much you can do except (a) raise an exception, or (b) put something else instead:
SELECT ...
CASE
WHEN PERCENTILE_DISC(0.9999)
WITHIN GROUP(ORDER BY DURATION_COUNT)
< 100000000
THEN PERCENTILE_DISC(0.9999)
WITHIN GROUP(ORDER BY DURATION_COUNT)
ELSE NULL
END as PERCENTILE_9999_QTY,
...
From the documentation:
This function takes as an argument any
numeric datatype or any nonnumeric
datatype that can be implicitly
converted to a numeric datatype. The
function returns the same datatype as
the numeric datatype of the argument.
If you want Number(8) as output you have to execute this function on a dataset that fits in NUMBER(8).