How To Check Numerical Format in SQL Server 2008 - sql

I am converting some existing Oracle queries to MSSQL Server (2008) and can't figure out how to replicate the following Regex check:
SELECT SomeField
FROM SomeTable
WHERE NOT REGEXP_LIKE(TO_CHAR(SomeField), '^[0-9]{2}[.][0-9]{7}$');
That finds all results where the format of the number starts with 2 positive digits, followed by a decimal point, and 7 decimal places of data: 12.3456789
I've tried using STR, CAST, CONVERT, but they all seem to truncate the decimal to 4 decimal places for some reason. The truncating has prevented me from getting reliable results using LEN and CHARINDEX. Manually adding size parameters to STR gets slightly closer, but I still don't know how to compare the original numerical representation to the converted value.
SELECT SomeField
, STR(SomeField, 10, 7)
, CAST(SomeField AS VARCHAR)
, LEN(SomeField )
, CHARINDEX(STR(SomeField ), '.')
FROM SomeTable
+------------------+------------+---------+-----+-----------+
| Orig | STR | Cast | LEN | CHARINDEX |
+------------------+------------+---------+-----+-----------+
| 31.44650944 | 31.4465094 | 31.4465 | 7 | 0 |
| 35.85609 | 35.8560900 | 35.8561 | 7 | 0 |
| 54.589623 | 54.5896230 | 54.5896 | 7 | 0 |
| 31.92653899 | 31.9265390 | 31.9265 | 7 | 0 |
| 31.4523333333333 | 31.4523333 | 31.4523 | 7 | 0 |
| 31.40208955 | 31.4020895 | 31.4021 | 7 | 0 |
| 51.3047869443893 | 51.3047869 | 51.3048 | 7 | 0 |
| 51 | 51.0000000 | 51 | 2 | 0 |
| 32.220633 | 32.2206330 | 32.2206 | 7 | 0 |
| 35.769247 | 35.7692470 | 35.7692 | 7 | 0 |
| 35.071022 | 35.0710220 | 35.071 | 6 | 0 |
+------------------+------------+---------+-----+-----------+

What you want to do does not make sense in SQL Server.
Oracle supports a number data type that has a variable precision:
if a precision is not specified, the column stores values as given.
There is no corresponding data type in SQL Server. You have have a variable number (float/real) or a fixed number (decimal/numeric). However, both apply to ALL values in a column, not to individual values within a row.
The closest you could do is:
where somefield >= 0 and somefield < 100
Or if you wanted to insist that there is a decimal component:
where somefield >= 0 and somefield < 100 and floor(somefield) <> somefield
However, you might have valid integer values that this would filter out.

This answer gave me an option that works in conjunction with checking the decimal position first.
SELECT SomeField
FROM SomeTable
WHERE SomeField IS NOT NULL
AND CHARINDEX('.', SomeField ) = 3
AND LEN(CAST(CAST(REVERSE(CONVERT(VARCHAR(50), SomeField , 128)) AS FLOAT) AS BIGINT)) = 7
While I understand this is terrible by nearly all metrics, it satisfies the requirements.
The basis of checking formatting on this data type in inherently flawed as pointed out by several posters, however for this very isolated use case I wanted to document the workaround.

Related

Length of SQL CHAR column is always at maximum regardless of content [duplicate]

This question already has an answer here:
Getting the length of a string in SQL
(1 answer)
Closed 2 years ago.
I'm looking for the equivalent of Select LEN(1234) from x Return 4 for FlameRobin for my Char field.
All I can find is char_length which returns the fields max length not the contents of the field.
Because that is the difference between SQL datatypes CHAR (fixed length, always right-padded with spaces, like in DBF and other tabular formats of that age) and VARCHAR (variable-length, may be shorter than max length).
And your query is NOT a query you are really using!
The query you suggest DOES return exactly 4 in Firebird.
db<>fiddle here
select rdb$get_context('SYSTEM', 'ENGINE_VERSION') as version
, rdb$character_set_name
from rdb$database;
VERSION | RDB$CHARACTER_SET_NAME
:------ | :---------------------------------------------------------------------------------------------------------------------------
3.0.5 | UTF8
Select char_LENgth(1234) from rdb$database
| CHAR_LENGTH |
| ----------: |
| 4 |
create table T (
i integer,
c char(20),
v varchar(20)
)
✓
insert into T values (1234, 1234, 1234)
1 rows affected
select * from T
I | C | V
---: | :------------------------------------------------------------------------------- | :---
1234 | 1234 | 1234
Select
char_length(1234) as const
, char_length(i) as int_to_char
, char_length(c) as fixed_char
, char_length(v) as var_char
, char_length(trim(c)) as char_t
, char_length(cast(trim(c) as varchar(20))) as char_t_v
, char_length(trim(cast(c as varchar(20)))) as char_v_t
from T
CONST | INT_TO_CHAR | FIXED_CHAR | VAR_CHAR | CHAR_T | CHAR_T_V | CHAR_V_T
----: | ----------: | ---------: | -------: | -----: | -------: | -------:
4 | 4 | 20 | 4 | 4 | 4 | 4
This is exactly what should happen. If you store "HELLO" in a CHAR(20) field, you will get a 20 character string on output (it might be trimmed somewhere along the path, so you don't realize that the initial size is always padded to, or truncated to, 20).
Either use VARCHAR type, or you'll have to do something like CHAR_LENGTH(TRIM(FieldName)) to get the "perceived length" of the string.

Filtering records not containing numbers

I have a table that has numbers in string format. Ideally the table should contain 10 digit number in string format, but it has many junk values. I wanted to filter out the records that are not ideal in nature.
Below is the sample table that I have:
+---------------+--------+----------------------------------+
| ID_UID | Length | ##Comment |
+---------------+--------+----------------------------------+
| +112323456705 | 13 | Contains special character |
| 4323456432 | 11 | Contains blank |
| 3423122334 | 10 | As expected, 10 character number |
| 6758439239 | 10 | As expected, 10 character number |
| 58_4323129 | 10 | Contains special character |
| 4567$%6790 | 10 | Contains special character |
| 45684938901 | 11 | Is 11 characters |
| 4568 38901 | 10 | Contains blank |
+---------------+--------+----------------------------------+
Expected Output:
+---------------+--------+----------------------------+
| ID_UID | Length | ##Comment |
+---------------+--------+----------------------------+
| +112323456705 | 13 | Contains special character |
| 4323456432 | 11 | Contains blank |
| 58_4323129 | 10 | Contains special character |
| 4567$%6790 | 10 | Contains special character |
| 45684938901 | 11 | Is 11 characters |
| 4568 38901 | 10 | Contains blank |
+---------------+--------+----------------------------+
Basically I want all the records that dont have 10 digit numbers in them.
I have tried out below query:
SELECT *
FROM t1
WHERE ID_UID LIKE '%[^0-9]%'
But this does not returns any records.
Have created a fiddle for the same.
P.S. The columns length and ##Comment are illustrative in nature.
You want RLIKE not LIKE:
SELECT *
FROM t1
WHERE ID_UID RLIKE '[^0-9]'
Note that % is a LIKE wildcard, not a regular expression wildcard. Also, regular expressions match the pattern anywhere it occurs, so no wildcards are needed for the beginning and end of the string.
If you want to find values that are not ten digits, then be explicit:
SELECT *
FROM t1
WHERE ID_UID NOT RLIKE '^[0-9]{10}$'

Select Value based on Multiple Value Range in SQL

I am having multiple criteria to give incentive to my employees. For example as shown in below image
Grid Table is dynamic in nature. It keeps on changing based on business conditions.
I have a table where I have emp Ids whose Resolution % I have calculated and also calculated their Normalization %. Now, I need to give them % Incentives based on the above Grid using SQL Query.
Output Table in which i need to update the incentives
I assume the grid table is also stored as a database table (so you can update it):
+-----------------+---------------+--------------------+------------------+-----------+
| INCENTIVES |
+-----------------+---------------+--------------------+------------------+-----------+
| from_resulution | to_resolution | from_normalization | to_normalization | incentive |
+-----------------+---------------+--------------------+------------------+-----------+
| 0 | 70 | 0 | 5 | 9 |
| 0 | 70 | 5 | 10 | 11 |
| 0 | 70 | 10 | 100 | 13 |
| 71 | 75 | 0 | 5 | 10 |
... I hope you get the idea
+-----------------+---------------+--------------------+------------------+-----------+
And the update query can be:
update employee E
set E.incentive = (select I.incentive
from incentives I
where e.resolution >= I.from_resolution
and e.resolution < I.to_resolution
and e.normalization >= I.from_normalization
and e.normalization < I.to_normalization)
UPDATE: the TO values are not in the scope of the range. By using the TO value equal to the FROM value of the next range we assure to cover all values (including floating point). Thanks to Gordon

SQL LIKE (Reverse)

I'm kinda confused on how this thing can be done.
I have a database table having these values:
| id | file_type | code | position |
-------------------------------------
| 1 | Order | SO | 1 |
| 2 | Order | 1-SO | 7 |
| 3 | Order | 1_SO | 7 |
Now, I want to get the position and the file type of my filename so I come up with this query:
SET #FileName = '1-SO1234567890.pdf'
SELECT *
FROM tbl_FileTypes
WHERE CHARINDEX(code,#FileName)> 0
Sadly, I'm getting two results here, I only need the data with the "1-SO" result.
| id | file_type | code | position |
-------------------------------------
| 1 | Order | SO | 1 |
| 2 | Order | 1-SO | 7 |
I believe that my WHERE Clause causes this to happen.
Is there any better way for me to get my desired results?
Thank you very much in advance.
You might want to use SUBSTRING instead (assuming T-SQL):
WHERE code = SUBSTRING(#FileName,1, LEN(code));
Which checks if the first n-chars of FileName equal a given code.
DEMO
well
SET #FileName = '1-SO1234567890.pdf'
SELECT *
FROM tbl_FileTypes
WHERE CHARINDEX(code,#FileName) = 1
would work in this case but if you had a code '1-SOS' it's going to fail again.
Since you are cheking left most characters, you could also use LEFT() function as below. Also use TOP (1) to get a single record and consider ordering as needed.
SELECT TOP (1) *
FROM tbl_FileTypes
WHERE LEFT(#FileName,LEN(code)) = code
--ORDER BY yourCol

Decimal(3,2) values in MySQL are always 9.99

I have a field, justsomenum, of type decimal(3,2) in MySQL that seems to always have values of 9.99 when I insert something like 78.3. Why?
This is what my table looks like:
mysql> describe testtable;
+---------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| firstname | varchar(20) | YES | | NULL | |
| lastname | varchar(20) | YES | | NULL | |
| justsomenum | decimal(3,2) | YES | | NULL | |
+---------------+--------------+------+-----+---------+----------------+
When I insert something like this and the select:
mysql> insert into testtable (firstname, lastname, justsomenum) values ("Lloyd", "Christmas", 83.5);
I get 9.99 when I select.
mysql> select * from testtable;
+----+-----------+-----------+---------------+
| id | firstname | lastname | justsomenum |
+----+-----------+-----------+---------------+
| 1 | Shooter | McGavin | 9.99 |
| 2 | Lloyd | Christmas | 9.99 |
| 3 | Lloyd | Christmas | 9.99 |
| 4 | Lloyd | Christmas | 9.99 |
+----+-----------+-----------+---------------+
4 rows in set (0.00 sec)
This is MySQL 5.0.86 on Mac OS X 10.5.8.
Any ideas? Thanks.
The maximum value for decimal(3, 2) is 9.99, so when you try to insert something larger than that, it is capped to 9.99. Try decimal(5, 2) or something else if you want to store larger numbers.
The first argument is the total number of digits of precision, and the second argument is the number of digits after the decimal point.
In older versions MySQL DECIMAL(3,2) meant 3 integers to the left of the DP and 2 to the right.
The MySQL devs have since changed it so the first property (in this case '3') is the complete number of integers in the decimal (9.99 being three numbers), and the second property (in this case '2') stays the same — the number of decimal places.
It's a little confusing. Basically, for DECIMAL fields, whatever number of integers you want before the DP needs to be added to whatever number of integers you want after the DP and set as your first option.
Then as has already been said, if you try to enter a number greater than the maximum value for the field, MySQL trims it for you. The problem here is your MySQL configuration. I go into more detail about this on my blog.