Which SQL-Datatype should I use for storing numbers and strings and to allow comparisons? - sql

There is a table in SQL Server which should contain one column discovery_timewith following values:
1998
1700
Middle Age
1493
Stone Age
I want to make querys like
'SELECT ... WHERE discovery_time > 1800'
Which data type is recommendable?

Use with VarChar():
Create Table #tbl
(
val VarChar(15)
)
Insert into #tbl Values
('1998'),
('1700'),
('Middle Age'),
('1493'),
('Stone Age')
Query
With cte As
(
Select
*,
CASE WHEN TRY_CONVERT(Int, val) IS Not NULL Then val End As yr
From #tbl
)
Select val From cte Where yr > 1800
UPDATE: This will also work..
Select * From #tbl
Where Try_Convert(Int, val) > 1800
Result
val
1998

Well, obviously you cannot store string values in a numeric column, so a string is needed.
One method is to change the logic, more like:
WHERE discovery_time > '1800' and discovery_time < '9999'
Then be sure that the strings are all zero padded. So, '0500' rather than '500'. This will not work for BC.
An alternative is to hide the variables. Here is one method:
create table . . . (
discovery_year int, -- a year
discovery_period varchar(255), -- a period
discovery_time as (coalesce(discovery_period, cast(discovery_year as varchar(255))
);
Then do the logic on discovery_year.
Actually, another method is try_convert():
WHERE TRY_CONVERT(int, discovery_time) > 1800
You can also add this in as a computed column:
alter table t add discovery_year as (try_convert(int, discovery_time));
You just need to remember to use discovery_year in the comparisons.

Related

How to convert or cast int to string in SQL Server

Looking at a column that holds last 4 of someone's SSN and the column was originally created as an int datatype. Now SSN that begin with 0 get registered as 0 on the database.
How can I convert the column and it's information from an int into a string for future proof?
You should convert. CONVERT(VARCHAR(4), your_col)
If you specifically want zero-padded numbers, then the simplest solution is format():
select format(123, '0000')
If you want to fix the table, then do:
alter table t alter column ssn4 char(4); -- there are always four digits
Then update the value to get the leading zeros:
update t
ssn4 = format(convert(int, ssn4), '0000');
Or, if you just want downstream users to have the string, you can use a computed column:
alter table t
add ssn4_str as (format(ssn4, '0000'));
If you want to add leading zeros, use:
SELECT RIGHT('0000'+ISNULL(SSN,''),4)
First thing never store SSN or Zip Code as any numeric type.
Second you should fix the underlying table structure not rely on a conversion...but if you're in a jam this is an example of a case statement that will help you.
IF OBJECT_ID('tempdb..#t') IS NOT NULL
BEGIN
DROP TABLE #t
END
GO
CREATE TABLE #t(
LastFourSSN INT
)
INSERT INTO #t(LastFourSSN)
VALUES('0123'),('1234')
SELECT LastFourSSN --strips leading zero
FROM #t
SELECT -- adds leading zero to anything less than four charaters
CASE
WHEN LEN(LastFourSSN) < 4
THEN '0' + CAST(LastFourSSN AS VARCHAR(3))
ELSE CAST(LastFourSSN AS VARCHAR(4))
END LastFourSSN
FROM #t
If you are looking for converting values in the column for your purpose to use in application, you can use this following-
SELECT CAST(your_column AS VARCHAR(100))
--VARCHAR length based on your data
But if you are looking for change data type of your database column directly, you can try this-
ALTER TABLE TableName
ALTER COLUMN your_column VARCHAR(200) NULL
--NULL or NOT NULL based on the data already stored in database

Separating UNIQUEIDENTFIER values from possible varchars in datamapping

I'm having hard time to differentiate between 'UNIQUEIDENTIFIER' & 'varchar' in a data mapping script. Its a mismatch of data.
I tried this so far:
--my_field is a UNIQUEIDENTFIER
SELECT distinct my_field, len(my_field), count(my_field)
FROM my_table_
WHERE len(my_field) < 36 OR len(my_field) > 36
group by my_field
SELECT my_field, len(my_field)
FROM my_table_
WHERE my_field LIKE '%[!#%&+,./:;<=>#`{|}~"()*\\\_\^\?\[\]\'']%' {ESCAPE '\'}
Any help would be much appreciated.
Assuming your SQL Server version is 2012 or higher (and if not, you really should upgrade, since 2012 is the earliest version still supported by Microsoft) - you can use try_cast - it will return null if the cast fails:
Sample data:
DECLARE #T AS TABLE
(
my_field varchar(100)
)
INSERT INTO #T (my_field) VALUES
('608DC3C3-1B2E-47AA-9362-33CD76DA3F6A'), ('A3871EA9-172D-49DD-8874-A850857F01FB'),
('A3871EA9-THIS-IS-WRONG-A850857F01FB'), ('And so is this')
Query:
SELECT my_field
FROM #T
WHERE TRY_CAST(my_Field AS UNIQUEIDENTIFIER) IS NULL
Result:
my_field
A3871EA9-THIS-IS-WRONG-A850857F01FB
And so is this

'LIKE' issues with FLOAT: SQL query needed to find values >= 4 decimal places

I have a conundrum....
There is a table with one NVARCHAR(50) Float column that has many rows with many numbers of various decimal lengths:
'3304.063'
'3304.0625'
'39.53'
'39.2'
I need to write a query to find only numbers with decimal places >= 4
First the query I wrote was:
SELECT
Column
FROM Tablename
WHERE Column LIKE '%.[0-9][0-9]%'
The above code finds all numbers with decimal places >= 2:
'3304.063'
'3304.0625'
'39.53'
Perfect! Now, I just need to increase the [0-9] by 2...
SELECT
Column
FROM Tablename
WHERE Column LIKE '%.[0-9][0-9][0-9][0-9]%'
this returned nothing! What?
Does anyone have an explanation as to what went wrong as well and/or a possible solution? I'm kind of stumped and my hunch is that it is some sort of 'LIKE' limitation..
Any help would be appreciated!
Thanks.
After your edit, you stated you are using FLOAT which is an approximate value stored as 4 or 8 bytes, or 7 or 15 digits of precision. The documents explicitly state that not all values in the data type range can be represented exactly. It also states you can use the STR() function when converting it which you'll need to get your formatting right. Here is how:
declare #table table (columnName float)
insert into #table
values
('3304.063'),
('3304.0625'),
('39.53'),
('39.2')
--see the conversion
select * , str(columnName,20,4)
from #table
--now use it in a where clause.
--Return all values where the last digit isn't 0 from STR() the conversion
select *
from #table
where right(str(columnName,20,4),1) != 0
OLD ANSWER
Your LIKE statement would do it, and here is another way just to show they both work.
declare #table table (columnName varchar(64))
insert into #table
values
('3304.063'),
('3304.0625'),
('39.53'),
('39.2')
select *
from #table
where len(right(columnName,len(columnName) - charindex('.',columnName))) >= 4
select *
from #table
where columnName like '%.[0-9][0-9][0-9][0-9]%'
One thing that could be causing this is a space in the number somewhere... since you said the column type was VARCHAR this is a possibility, and could be avoided by storing the value as DECIMAL
declare #table table (columnName varchar(64))
insert into #table
values
('3304.063'),
('3304. 0625'), --notice the space here
('39.53'),
('39.2')
--this would return nothing
select *
from #table
where columnName like '%.[0-9][0-9][0-9][0-9]%'
How to find out if this is the case?
select *
from #table
where columnName like '% %'
Or, anything but numbers and decimals:
select *
from #table
where columnName like '%[^.0-9]%'
The following is working fine for me:
declare #tab table (val varchar(50))
insert into #tab
select '3304.063'
union select '3304.0625'
union select '39.53'
union select '39.2'
select * from #tab
where val like '%.[0-9][0-9][0-9][0-9]%'
Assuming your table only has numerical data, you can cast them to decimal and then compare:
SELECT COLUMN
FROM tablename
WHERE CAST(COLUMN AS DECIMAL(19,4)) <> CAST(COLUMN AS DECIMAL(19,3))
You'd want to test the performance of this against using the character data type solutions that others have already suggested.
You can use REVERSE:
declare #vals table ([Val] nvarchar(50))
insert into #vals values ('3304.063'), ('3304.0625'), ('39.53'), ('39.2')
select [Val]
from #Vals
where charindex('.',reverse([Val]))>4

How to manipulate comma-separated list in SQL Server

I have a list of values such as
1,2,3,4...
that will be passed into my SQL query.
I need to have these values stored in a table variable. So essentially I need something like this:
declare #t (num int)
insert into #t values (1),(2),(3),(4)...
Is it possible to do that formatting in SQL Server? (turning 1,2,3,4... into (1),(2),(3),(4)...
Note: I can not change what those values look like before they get to my SQL script; I'm stuck with that list. also it may not always be 4 values; it could 1 or more.
Edit to show what values look like: under normal circumstances, this is how it would work:
select t.pk
from a_table t
where t.pk in (#place_holder#)
#placeholder# is just a literal place holder. when some one would run the report, #placeholder# is replaced with the literal values from the filter of that report:
select t.pk
from a_table t
where t.pk in (1,2,3,4) -- or whatever the user selects
t.pk is an int
note: doing
declare #t as table (
num int
)
insert into #t values (#Placeholder#)
does not work.
Your description is a bit ridicuolus, but you might give this a try:
Whatever you mean with this
I see what your trying to say; but if I type out '#placeholder#' in the script, I'll end up with '1','2','3','4' and not '1,2,3,4'
I assume this is a string with numbers, each number between single qoutes, separated with a comma:
DECLARE #passedIn VARCHAR(100)='''1'',''2'',''3'',''4'',''5'',''6'',''7''';
SELECT #passedIn; -->: '1','2','3','4','5','6','7'
Now the variable #passedIn holds exactly what you are talking about
I'll use a dynamic SQL-Statement to insert this in a temp-table (declared table variable would not work here...)
CREATE TABLE #tmpTable(ID INT);
DECLARE #cmd VARCHAR(MAX)=
'INSERT INTO #tmpTable(ID) VALUES (' + REPLACE(SUBSTRING(#passedIn,2,LEN(#passedIn)-2),''',''','),(') + ');';
EXEC (#cmd);
SELECT * FROM #tmpTable;
GO
DROP TABLE #tmpTable;
UPDATE 1: no dynamic SQL necessary, all ad-hoc...
You can get the list of numbers as derived table in a CTE easily.
This can be used in a following statement like WHERE SomeID IN(SELECT ID FROM MyIDs) (similar to this: dynamic IN section )
WITH MyIDs(ID) AS
(
SELECT A.B.value('.','int') AS ID
FROM
(
SELECT CAST('<x>' + REPLACE(SUBSTRING(#passedIn,2,LEN(#passedIn)-2),''',''','</x><x>') + '</x>' AS XML) AS AsXml
) as tbl
CROSS APPLY tbl.AsXml.nodes('/x') AS A(B)
)
SELECT * FROM MyIDs
UPDATE 2:
And to answer your question exactly:
With this following the CTE
insert into #t(num)
SELECT ID FROM MyIDs
... you would actually get your declared table variable filled - if you need it later...

A query that will search for the highest numeric value in a table where the column has an alphanumeric sequence

I have a column (XID) that contains a varchar(20) sequence in the following format: xxxzzzzzz Where X is any letter or a dash and zzzzz is a number.
I want to write a query that will strip the xxx and evaluate and return which is the highest number in the table column.
For example:
aaa1234
bac8123
g-2391
After, I would get the result of 8123
Thanks!
A bit painful in SQL Server, but possible. Here is one method that assumes that only digits appear after the first digit (which you actually specify as being the case):
select max(cast(stuff(col, 1, patindex('%[0-9]%', col) - 1, '') as float))
from t;
Note: if the last four characters are always the number you are looking for, this is probably easier to do with right():
select max(right(col, 4))
Using Numbers table
declare #string varchar(max)
set #string='abc1234'
select top 1 substring(#string,n,len(#string))
from
numbers
where n<=len(#string)
and isnumeric(substring(#string,n,1))=1
order by n
Output:1234
Using PATINDEX you can achieve it, like this -
DECLARE #test table
(
id INT,
player varchar(100)
)
INSERT #test
VALUES (1,'aaa1234'),
(2,'bac8123'),
(3,'g-2391')
SELECT
MAX(CONVERT(INT, LTRIM(SUBSTRING(player, PATINDEX('%[0-9]%', player), LEN(player)))))
FROM #test
Try:
Select MAX(RIGHT(XID,17))
from table
You can also use this method
CREATE TABLE #Tmp
(
XID VARCHAR(20)
)
INSERT INTO #Tmp(XID)
VALUES ('aaa1234'), ('bac8123'), ('g-2391')
SELECT MAX(RIGHT(XID, LEN(XID) - 3))
FROM #Tmp