Convert signed int to string ip address in SQL Server - sql-server

I'm retrieving a signed int from a SQL Server database and need to convert it to a "normal" looking dotted string for display to users.
Googling, I found this code:
SELECT
dbo.IPADDRESS.IPADDRESS,
CAST(ROUND( (cast(dbo.IPADDRESS.IPADDRESS as bigint) / 16777216 ), 0, 1) AS varchar(4)) + '.' +
CAST((ROUND( (cast(dbo.IPADDRESS.IPADDRESS as bigint) / 65536 ), 0, 1) % 256) AS varchar(4)) + '.' +
CAST((ROUND( (cast(dbo.IPADDRESS.IPADDRESS as bigint) / 256 ), 0, 1) % 256) AS varchar(4)) + '.' +
CAST((cast(dbo.IPADDRESS.IPADDRESS as bigint) % 256 ) AS varchar(4)) as IPDottedNotation
FROM
dbo.IPADDRESS
which works some of the time, but produces wacky output other times. For example, converting this -1951276725 yields the result -116.-78.-30.-181.
Any suggestions? Thanks.

DECLARE #IPADDRESS TABLE (
IPADDRESS INT);
INSERT INTO #IPADDRESS
VALUES (-1139627840),
( 1);
SELECT
LTRIM(CAST(SUBSTRING(IP,4,1) AS TINYINT)) + '.' +
LTRIM(CAST(SUBSTRING(IP,3,1) AS TINYINT)) + '.' +
LTRIM(CAST(SUBSTRING(IP,2,1) AS TINYINT)) + '.' +
LTRIM(CAST(SUBSTRING(IP,1,1) AS TINYINT))
FROM #IPADDRESS
CROSS APPLY (SELECT CAST(IPADDRESS AS BINARY(4))) C(IP)

The code you have would work if IPADDRESS was a bigint (effectively storing the unsigned int representation in the database - i.e. all values > 0). Do you have the option of changing the datatype in the table?
To get what you have to work, you need to convert your signed int to the equivalent unsigned int before the conversion to bigint. I'm not sure what the most efficient way to do this in TSQL is, but it might be to cast it to binary:
SELECT dbo.IPADDRESS.IPADDRESS,
CAST(ROUND( (cast(cast(dbo.IPADDRESS.IPADDRESS as binary(4)) as bigint) / 16777216 ), 0, 1) AS varchar(4)) + '.' +
CAST((ROUND( (cast(cast(dbo.IPADDRESS.IPADDRESS as binary(4)) as bigint) / 65536 ), 0, 1) % 256) AS varchar(4)) + '.' +
CAST((ROUND( (cast(cast(dbo.IPADDRESS.IPADDRESS as binary(4)) as bigint) / 256 ), 0, 1) % 256) AS varchar(4)) + '.' +
CAST((cast(cast(dbo.IPADDRESS.IPADDRESS as binary(4)) as bigint) % 256 ) AS varchar(4)) as IPDottedNotation

Like #Ed Harper stated that the selected solution doesn't work for a signed int. Below is my solution which requires a little less casting and isn't inverted. Check out the following test scenario shown below where the converted string/varchar IP should be 192.168.18.188:
CREATE TABLE #data
(
ip NVARCHAR(45),
before NVARCHAR(45)
)
INSERT INTO #data
VALUES ('converted-ip','-1139627840')
update #data
set ip = cast((cast(before as int) & 255) as nvarchar) + '.' +
cast((cast(floor(convert(decimal, before)/256) as int) & 255) as nvarchar) + '.' +
cast((cast(floor(convert(decimal, before)/65536) as int) & 255) as nvarchar) + '.' +
cast((cast(floor(convert(decimal, before)/16777216) as int) & 255) as nvarchar)
select * from #data

Related

Convert HHH:MM:SS to seconds

From the source database, I am getting HH:MM:SS as 832:24:12
Currently I am using below statement which is working fine for most of the cases hh:mm:ss but it fails when hours are more than 99
ISNULL(LEFT(COLUMN,2) * 3600 + RIGHT(LEFT(COLUMN,5),2) * 60 + RIGHT(COLUMN, 2) ,0)
Just another option with a small tweak to your original
Example
Declare #V varchar(50) = '832:24:12'
Select (left(#V,charindex(':',#V)-1)*3600) + (left(right(#V,5),2)*60) + right(#v,2)
Returns
2996652
You can use a tricky solution using PARSENAME() function.
DECALRE #Hours INT = 0, #Minutes INT = 0 , #Seconds INT = 0
SELECT #Hours = PARSENAME(REPLACE('832:24:12'+':00', ':', '.'),4),
#Minutes = PARSENAME(REPLACE('832:24:12'+':00', ':', '.'),3),
#Seconds = PARSENAME(REPLACE('832:24:12'+':00', ':', '.'),2)
SELECT #Hours * 3600 + #Minutes * 60 + #Seconds as TotalSeconds
I am replacing ':' with '.' character after appending dummy sequence of characters ':00' for PARSENAME() function to work by splitting into delimitted data.
For table query
SELECT PARSENAME(REPLACE(ISNULL(ColumnName + ':00',0), ':', '.'),4) * 3600 +
PARSENAME(REPLACE(ISNULL(ColumnName + ':00',0), ':', '.'),3) * 60 +
PARSENAME(REPLACE(ISNULL(ColumnName + ':00',0), ':', '.'),2) As TotalSecs
FROM TableName
This of a guess, however...
CREATE TABLE #Test (TimeString varchar(10))
INSERT INTO #Test
VALUES ('832:24:12')
SELECT TimeString,
(LEFT(TimeString, H.CI - 1) * 3600) + (SUBSTRING(TimeString,H.CI +1, M.CI - H.CI -1) * 60) + (RIGHT(TimeString, LEN(TimeString) - M.CI))
FROM #Test T
CROSS APPLY (VALUES(CHARINDEX(':',TimeString))) H(CI)
CROSS APPLY (VALUES(CHARINDEX(':',TimeString, H.CI+1))) M(CI);
DROP TABLE #Test;
Hours can be the leftwards chars minus 6 positions to take into account the positions for minutes and seconds in the string (:##:##).
The minutes can accessed by taking the left 2, of the rightmost 5 chars.
The seconds are the right 2 chars.
Ex:
DECLARE #tempval varchar(100) = '832:24:12'
SELECT LEFT(#tempval, LEN(#tempval) - 6) * 3600
+LEFT(RIGHT(#tempval, 5), 2) * 60
+RIGHT(#tempval, 2)
Returns
2996652

Create a custom sequence with letter prefix

I have a sequence in SQL Server
CREATE SEQUENCE dbo.NextBusinessValue
START WITH 1
INCREMENT BY 1 ;
GO
And I'd like to use this to generate a 5 digit custom reference number that uses this sequence to create the number in the format A0000.
The rules for the reference number are that:
1-9999 would be A0001 - A9999
10000-19999 would be B0000 - B9999
20000-29999 would be C0000 - C9999 etc...
It won't ever get the the amount of data that requires going past Z.
I know I can get a letter by using:
SELECT CHAR(65)
So this would work for 1-9999:
declare #n int = 9999
SELECT CHAR(65) + right('0000' + convert(varchar(10), #n), 4)
But would fail when it reaches 10000.
What methods can be used to increment the letter each time the sequence hits the next block of 10000?
UPDATE AND WARNING
Having a primary key and a business key used for display, invoicing is very common. The business key has to be stored and indexed because business users will use it to search for records, documents etc. You shouldn't use the business key as the primary key though.
ORIGINAL
You already get the first digit with #n/10000. Add that to 65 to get the first letter.
To get the remainder you can perform a modulo operation, #n/10000 and format the result as a string:
select char(65 + #n/10000) + format(#n % 10000 ,'d')
Sequences and FORMAT were both introduced in SQL Server 2012, so you can be assured that FORMAT is always available.
9999 will return A9999, 19999 will return B9999 etc.
The scale can be a parameter itself
select char(65 + #n/#scale) + format(#n % #scale ,'d')
Personally I would handle this either in your display code or add it as a computed field either ti the table or view.
This would work upto Z:
declare #n int = 9999
-- Gives A9999
SELECT CHAR(#n / 10000 + 65 ) + right('0000' + convert(varchar(10), #n), 4)
SET #n = 10000
-- Gives B0000
SELECT CHAR(#n / 10000 + 65 ) + right('0000' + convert(varchar(10), #n), 4)
SET #n = 10001
-- Gives B0001
SELECT CHAR(#n / 10000 + 65 ) + right('0000' + convert(varchar(10), #n), 4)
SET #n = 20001
-- Gives C0001
SELECT CHAR(#n / 10000 + 65 ) + right('0000' + convert(varchar(10), #n), 4)
SET #n = 200001
-- Gives U0001
SELECT CHAR(#n / 10000 + 65 ) + right('0000' + convert(varchar(10), #n), 4)
SET #n = 300001
-- Gives _0001
SELECT CHAR(#n / 10000 + 65 ) + right('0000' + convert(varchar(10), #n), 4)
Something like this?
DECLARE #n INT = 9999;
WHILE #n < 26000
BEGIN
SELECT CHAR(65 + CONVERT(INT, #n / 10000)) + RIGHT('0000' + CONVERT(VARCHAR(10), #n), 4);
SELECT #n = #n + 1;
END;
(edited)
You should not use this as primary key, but rather calculate your format for the output on-the-fly. For a faster search I'd reccomend to use the following to calculate a persistant computed column, which you can use with an index.
DECLARE #mockingTbl TABLE(SomeSeqValue INT);
INSERT INTO #mockingTbl VALUES(0),(1),(999),(1000),(9999),(10000),(12345),(50000);
SELECT A.NumeralPart
,B.Rest
,C.StartLetter
,C.StartLetter+REPLACE(STR(A.NumeralPart,4),' ','0') AS YourCode
FROM #mockingTbl AS m
CROSS APPLY(SELECT m.SomeSeqValue % 10000 AS NumeralPart) AS A
CROSS APPLY(SELECT (m.SomeSeqValue-A.NumeralPart)/1000 AS Rest) AS B
CROSS APPLY(SELECT CHAR(B.Rest + ASCII('A'))) AS C(StartLetter)

Convert varchar (eg. 20151125175706+0800) to Datetime in SQL

How do I convert yyyymmddhhmmss+offset (20151125175706+0800) to datetime format in SQL Server? To something that can be used in datetime comparison to select records entered in table during last one minute.
Im getting this format from OBX segment of the message and I'm saving this as is in a varchar field in table.
I tried to do this(among other things):
select cast(convert(varchar, SUBSTRING(OBXDateTime, 1, 4) + '-' +
SUBSTRING(OBXDateTime, 5, 2) + '-' +
SUBSTRING(OBXDateTime, 7, 2) + ' ' +
SUBSTRING(OBXDateTime, 9, 2) + ':' +
SUBSTRING(OBXDateTime, 11, 2) + ':' +
SUBSTRING(OBXDateTime, 13, 2), 101) as datetime) from ObservationPatInfo
This query gives only 522 rows out of 1020 rows in the table and gives as error message saying
"The conversion of a varchar data type to a datetime data type resulted in an out-of-range value."
Kindly help me to solve this.
You can try this:
DECLARE #YourDt VARCHAR(100)='20151125175706+0800';
DECLARE #ISO8601_with_TimeZone VARCHAR(100);
--This will use multiple STUFFs to get the separators into your string
SELECT #ISO8601_with_TimeZone = STUFF(STUFF(STUFF(STUFF(STUFF(STUFF(#YourDt,18,0,':'),13,0,':'),11,0,':'),9,0,'T'),7,0,'-'),5,0,'-');
--This looks like a properly formatted datetime now: 2015-11-25T17:57:06+08:00
SELECT #ISO8601_with_TimeZone;
--Use DATETIME2 and code 127 to convert ISO8601 with TimeZone
SELECT CONVERT(datetime2,#ISO8601_with_TimeZone,127);
Use TRY_CONVERT to find the bad values:
select pi.*
from (select try_convert(ddatetime,
(SUBSTRING(OBXDateTime, 1, 4) + '-' +
SUBSTRING(OBXDateTime, 5, 2) + '-' +
SUBSTRING(OBXDateTime, 7, 2) + ' ' +
SUBSTRING(OBXDateTime, 9, 2) + ':' +
SUBSTRING(OBXDateTime, 11, 2) + ':' +
SUBSTRING(OBXDateTime, 13, 2)
), 101) as datetime) as dte, pi.*
from ObservationPatInfo pi
) pi
where dte is null;

Convert decimal time to hours and minutes

Been struggling with this and can't seem to find the right answer, although there are plenty of mentions for converting, but nothing specific is working.
I need to convert a time with data type of float into hours and minutes. So 13.50 as 13.30. The data type as fixed as float in DB so cannot change. DB is SQL Server 2008R2
Have tried:
cast(cast(floor(fdsViewTimesheet.perStandardHours) as
float(2))+':'+cast(floor(100*(
fdsViewTimesheet.perStandardHours - floor(fdsViewTimesheet.perStandardHours)))as
float(2)) as time) AS STANDARD_HOURS
But I get error message "Explicit conversion from data type real to time is not allowed" Have tried as char instead of as float but query hangs.
What am I doing wrong? I just want to convert a float value into hours and minutes.
Would be grateful if someone could point me in the right direction.
You can try:
DECLARE #HOURS decimal(7,4) = 20.5599
SELECT CAST(CONVERT(VARCHAR,DATEADD(SECOND, #HOURS * 3600, 0),108) AS TIME)
output : 20:33:35
But remember : Type Time in MSSQL only under 24hrs
If you want greater than 24hrs, try:
DECLARE #HOURS decimal(7,4) = 25.5599
SELECT
RIGHT('0' + CAST (FLOOR(#HOURS) AS VARCHAR), 2) + ':' +
RIGHT('0' + CAST(FLOOR((((#HOURS * 3600) % 3600) / 60)) AS VARCHAR), 2) + ':' +
RIGHT('0' + CAST (FLOOR((#HOURS * 3600) % 60) AS VARCHAR), 2)
output : 25:33:35
-- Update
Decimal minutes to more than 24hrs
DECLARE #MINUTES decimal(7,4) = 77.9
SELECT
RIGHT('0' + CAST (FLOOR(COALESCE (#MINUTES, 0) / 60) AS VARCHAR (8)), 2) + ':' +
RIGHT('0' + CAST (FLOOR(COALESCE (#MINUTES, 0) % 60) AS VARCHAR (2)), 2) + ':' +
RIGHT('0' + CAST (FLOOR((#MINUTES* 60) % 60) AS VARCHAR (2)), 2);
output: 01:17:54
This should work for you
DECLARE #f [real]
SET #f = 13.50
SELECT DATEADD(mi, (#f - FLOOR(#f)) * 60, DATEADD(hh, FLOOR(#f), CAST ('00:00:00' AS TIME)))
DECLARE #f FLOAT = 13.5;
SELECT CONVERT(TIME(0), DATEADD(MINUTE, 60*#f, 0));
Or if you just want hh:mm as a string:
SELECT CONVERT(CHAR(5), DATEADD(MINUTE, 60*#f, 0), 108);
Just be careful if you have values >= 24.
How about you convert to minutes and add to the 00:00 time like so:
DECLARE #c datetime
select #c = dateadd(mi,fdsViewTimesheet.perStandardHours*60,'00:00')
If you wanted to do it in the statement with Time only:
select CONVERT(TIME,dateadd(mi,fdsViewTimesheet.perStandardHours*60,'00:00') )
If you have values that are larger than 24 hours, then the standard datetime and time types in sql cannot hold these. They are limited to holding 24 hour ranges.
What you would need to do is store the time representation in a string for example like so:
select cast(floor(fdsViewTimesheet.perStandardHours) as varchar(10)) + ':' + cast(FLOOR( (fdsViewTimesheet.perStandardHours - floor(fdsViewTimesheet.perStandardHours))*60)as varchar(2))

convert varchar to int in select statement using patindex

My query is for convert varchar into string,
select top(5)'Insert into jobs(minexperience,maxexperience)values('+
cast(substring(Experience as varchar(50)),0,patindex('%to%',Experience))*365*24*60*60,
cast(substring(Experience as
varchar(50)),patindex('%to%',Experience)+2,patindex('%Years%',Experience)-patindex('%to%',Experience)-2)*365*24*60*60+')'
from requirementsdetailsfororganization
In my below query i have an error "Incorrect syntax near the keyword 'AS'."
I want to convert string to integer.
Any Help?
Possible this helpful for you -
SELECT TOP(5) 'INSERT INTO dbo.jobs(minexperience,maxexperience) VALUES(' +
CAST(SUBSTRING(
CAST(r.Experience AS VARCHAR(50))
, 0
, r.ToExperience) * 31536000
AS VARCHAR(50))
+ ',' +
CAST(SUBSTRING(
CAST(r.Experience AS VARCHAR(50))
, r.ToExperience + 2
, patindex('%Years%', r.Experience) - r.ToExperience - 2) * 31536000
AS VARCHAR(50))
+')'
FROM (
SELECT
r.Experience
, ToExperience = PATINDEX('%to%', r.Experience)
FROM dbo.requirementsdetailsfororganization r
) r

Resources