Get specific character values from a SKU Field - sql-server

I'm trying to write a SQL Server 2008 R2 statement in a stored procedure that takes a 9-character SKU value as input, and has to grab certain character values in specific positions of that input field and concatenate them to a value.
I wrote the following but it doesn't seem to be working, though that could just be me. It seems to make sense on paper...
IF NOT #SKU = '' BEGIN
 
DECLARE #StringPos SMALLINT = 1, #StringLen SMALLINT, #ColNum SMALLINT, #ItemVal VARCHAR(10)
SET #StringLen = LEN(#RecallSortMode)
WHILE #StringPos <= #StringLen BEGIN
SET #ColNum = SUBSTRING(#RecallSortMode, #StringPos, 1)
SET #ItemVal += SUBSTRING(#SKU, #ColNum, 1)
SET #StringPos = #StringPos + 1
END
PRINT 'Item Val: ' + #ItemVal
END
So, if the columns I need are 1, 2 and 9 the value of #RecallSortMode would be 129 and if the value of #SKU is 9 characters long and is 987654321 I'm looking for the statement to return an #ItemVal of 981.
Does this work or am I doing something wrong? It seems to compile OK but I get back nothing from it.
Thanks

Related

Sql server: strange value (rectangle, g and ASCII symbol)

I`m working with SQL SERVER 2008 in Management Studio. Select statement returned an empty string in one column:
But inside I`ve found strange values:
I would like to ask, what thoose values mean?
Best regards
The values could be binary representations of numeric values, each being 2 greater than the previous value.
Execute this code and do "Edit top 200 rows"
create table stranGe ( A varchar(40), B varchar(40) )
go
declare #I int = 91901
while #I < 99999
begin
insert into StranGe ( A, B ) values ( cast(#I as varbinary), cast(#I as varchar) )
set #I = #I + 2
end
select * from stranGe
You might be able to get the original number back by casting to varbinary
select cast(A as varbinary), cast(cast(A as varbinary)as int) from stranGe

Remove the 7th digit of a bigint for every row in a Sql Server table

I am maintaining SQL Server database and some c# code which uploads data to it from a third party. The database has a table 'LessonRoom' which contains a row for each lesson which occurs in a particluar room, it has a field 'SourceKey' which is a bigint and is formed by concatenating a room id and a lesson id, the c# which returns this key is as follows:
SourceKey = long.Parse(RoomId.ToString().PadRight(7, '0') + LessonId.ToString());
This code started falling over because the lessonId's grew too large and the resulting int is too large to fit in a bigint (c# long). The RoomIds are only ever 5 digits long so an easy fix is to PadRight(6, '0').
Now I have a solution but I need to update the existing data. I don't know how to remove a zero from the 7th digit of a SQL Server bigint in every row of 500,000 rows. Do I have to write a query to convert the value to a string, remove the zero, parse and put it back or can anyone think of a more succinct way to do it?
Essentially I need to turn this number:
6,159,800,830,114,069,893
Into this one:
615,980,830,114,069,893
Sine you know it is always the 7th character you want to remove you can do this quite easily.
declare #SourceKey bigint = 6159800830114069893
select cast(stuff(cast(#SourceKey as varchar(25)), 7, 1, '') as bigint)
you could resolve them with the modulo-Operator :)
here a simple T SQL example
DECLARE #input AS BIGINT
DECLARE #expect AS BIGINT
DECLARE #rest AS BIGINT
DECLARE #result AS BIGINT
DECLARE #resultShort AS BIGINT
SET #input = 6159800830114069893
SET #expect= 615980830114069893
SET #rest = #input % 1000000000000
SET #result = ( ( #input - #rest ) / 10 ) + #rest
SET #resultShort = ( ( #input - #input % 1000000000000 ) / 10 ) + #input %
1000000000000
SELECT #rest, #result,
CASE
WHEN #result = #expect THEN 'true'
ELSE 'false'
END AS test,
#resultShort,
CASE
WHEN #resultShort = #expect THEN 'true'
ELSE 'false'
END AS test2

Unable to add string at the end using STUFF

I tried to Insert a string within another string using STUFF.
DECLARE #Number VARCHAR(100) = '12345'
SELECT STUFF(#Number, 3, 0, '6') -- Add value in 3rd Position, Returns '126345'
I tried to append the value at the end
SELECT STUFF(#Number, 6, 0, '6') -- Add value in 6th Position
-- Actual length is 5, So it Returns 'NULL'
Expected Output:
123456
I tried using STUFF for simple statement. But not able to append string at the end. Can anyone help me to do in any other simple way?
From here:
The second paramter is an integer value that specifies the location to
start deletion and insertion. If start or length is negative, a null
string is returned. If start is longer than the first
character_expression, a null string is returned. start can be of type
bigint.
This query :
DECLARE #Number VARCHAR(100) = '12345'
SELECT STUFF(#Number, 6, 0, '6')
return NULL because there is only 5 characters.
If you want append string at the end just concat that string using + operator.
DECLARE #Number VARCHAR(100) = '12345'
SELECT #Number + '6'
You can use CONCAT to append strings one after the other. They will be appended in the order in which they are passed to the function:
DECLARE #Number VARCHAR(100) = '12345'
select concat(#Number,'6')
You can also use the '+' operator for this purpose. However, if one the strings being appended is NULL, the result will also be NULL using '+', whereas the NULL string is treated as a blank string by CONCAT.
select concat(#Number,null,6) -- = '123456'
select #Number + null + 6 -- == null
If the position to insert is not fixed, you can use a CASE statement along with STUFF and CONCAT to either insert into or append in a single statement, like so:
declare #number varchar(100) = '12345'
declare #pos int = 6
declare #append varchar(100) = '6'
select
case
when #pos < len(#number) then stuff(#number,#pos,0,#append)
else concat(#number,#append)
end result
As a hack you can do this,
SELECT STUFF(#Number, 5, 1, '56')

SQL Server : converting varchar to INT

I am stuck on converting a varchar column UserID to INT. I know, please don't ask why this UserID column was not created as INT initially, long story.
So I tried this, but it doesn't work. and give me an error:
select CAST(userID AS int) from audit
Error:
Conversion failed when converting the varchar value
'1581............................................................................................................................' to data type int.
I did select len(userID) from audit and it returns 128 characters, which are not spaces.
I tried to detect ASCII characters for those trailing after the ID number and ASCII value = 0.
I have also tried LTRIM, RTRIM, and replace char(0) with '', but does not work.
The only way it works when I tell the fixed number of character like this below, but UserID is not always 4 characters.
select CAST(LEFT(userID, 4) AS int) from audit
You could try updating the table to get rid of these characters:
UPDATE dbo.[audit]
SET UserID = REPLACE(UserID, CHAR(0), '')
WHERE CHARINDEX(CHAR(0), UserID) > 0;
But then you'll also need to fix whatever is putting this bad data into the table in the first place. In the meantime perhaps try:
SELECT CONVERT(INT, REPLACE(UserID, CHAR(0), ''))
FROM dbo.[audit];
But that is not a long term solution. Fix the data (and the data type while you're at it). If you can't fix the data type immediately, then you can quickly find the culprit by adding a check constraint:
ALTER TABLE dbo.[audit]
ADD CONSTRAINT do_not_allow_stupid_data
CHECK (CHARINDEX(CHAR(0), UserID) = 0);
EDIT
Ok, so that is definitely a 4-digit integer followed by six instances of CHAR(0). And the workaround I posted definitely works for me:
DECLARE #foo TABLE(UserID VARCHAR(32));
INSERT #foo SELECT 0x31353831000000000000;
-- this succeeds:
SELECT CONVERT(INT, REPLACE(UserID, CHAR(0), '')) FROM #foo;
-- this fails:
SELECT CONVERT(INT, UserID) FROM #foo;
Please confirm that this code on its own (well, the first SELECT, anyway) works for you. If it does then the error you are getting is from a different non-numeric character in a different row (and if it doesn't then perhaps you have a build where a particular bug hasn't been fixed). To try and narrow it down you can take random values from the following query and then loop through the characters:
SELECT UserID, CONVERT(VARBINARY(32), UserID)
FROM dbo.[audit]
WHERE UserID LIKE '%[^0-9]%';
So take a random row, and then paste the output into a query like this:
DECLARE #x VARCHAR(32), #i INT;
SET #x = CONVERT(VARCHAR(32), 0x...); -- paste the value here
SET #i = 1;
WHILE #i <= LEN(#x)
BEGIN
PRINT RTRIM(#i) + ' = ' + RTRIM(ASCII(SUBSTRING(#x, #i, 1)))
SET #i = #i + 1;
END
This may take some trial and error before you encounter a row that fails for some other reason than CHAR(0) - since you can't really filter out the rows that contain CHAR(0) because they could contain CHAR(0) and CHAR(something else). For all we know you have values in the table like:
SELECT '15' + CHAR(9) + '23' + CHAR(0);
...which also can't be converted to an integer, whether you've replaced CHAR(0) or not.
I know you don't want to hear it, but I am really glad this is painful for people, because now they have more war stories to push back when people make very poor decisions about data types.
This question has got 91,000 views so perhaps many people are looking for a more generic solution to the issue in the title "error converting varchar to INT"
If you are on SQL Server 2012+ one way of handling this invalid data is to use TRY_CAST
SELECT TRY_CAST (userID AS INT)
FROM audit
On previous versions you could use
SELECT CASE
WHEN ISNUMERIC(RTRIM(userID) + '.0e0') = 1
AND LEN(userID) <= 11
THEN CAST(userID AS INT)
END
FROM audit
Both return NULL if the value cannot be cast.
In the specific case that you have in your question with known bad values I would use the following however.
CAST(REPLACE(userID COLLATE Latin1_General_Bin, CHAR(0),'') AS INT)
Trying to replace the null character is often problematic except if using a binary collation.
This is more for someone Searching for a result, than the original post-er. This worked for me...
declare #value varchar(max) = 'sad';
select sum(cast(iif(isnumeric(#value) = 1, #value, 0) as bigint));
returns 0
declare #value varchar(max) = '3';
select sum(cast(iif(isnumeric(#value) = 1, #value, 0) as bigint));
returns 3
I would try triming the number to see what you get:
select len(rtrim(ltrim(userid))) from audit
if that return the correct value then just do:
select convert(int, rtrim(ltrim(userid))) from audit
if that doesn't return the correct value then I would do a replace to remove the empty space:
select convert(int, replace(userid, char(0), '')) from audit
This is how I solved the problem in my case:
First of all I made sure the column I need to convert to integer doesn't contain any spaces:
update data set col1 = TRIM(col1)
I also checked whether the column only contains numeric digits.
You can check it by:
select * from data where col1 like '%[^0-9]%' order by col1
If any nonnumeric values are present, you can save them to another table and remove them from the table you are working on.
select * into nonnumeric_data from data where col1 like '%[^0-9]%'
delete from data where col1 like '%[^0-9]%'
Problems with my data were the cases above. So after fixing them, I created a bigint variable and set the values of the varchar column to the integer column I created.
alter table data add int_col1 bigint
update data set int_col1 = CAST(col1 AS VARCHAR)
This worked for me, hope you find it useful as well.

SQL convert int to time

I have a database that displays time as an integer. However I am wanting to output this into a report with the correct format. This is the format that I would like to change:
eg.
183000 would become 18:30
500 would become 00:05
160000 would become 16:00
and so on.
I have had a look and CAST and CONVERT but not successfully managed to get this the time in the correct format.
Assuming your input will always be an int, you can parse it with something like:
DECLARE #stringTime varchar(6)
SET #stringTime = RIGHT('000000' + CAST(intTime AS VARCHAR), 6)
SELECT CAST(LEFT(#stringTime, 2) + ':' + RIGHT(LEFT(#stringTime, 4), 2) AS TIME) as TimeValue
I'd DEFINITELY look to change this field to an actual time or datetime field, as this level of conversion is not advised, especially for a heavily used database. There's just really got to be a better way to store your data.
Using an int value this way allows for a lot of bad data, without adding a lot of additional checks and/or constraints on your inputs (i.e.: 260000, 127900, etc.)
looks like you need to divide by 100 to get the seconds, divide by 10000 to get the minutes, and divide by 1000000 to get the hours, then format those values as a string, inserting a colon between hours and minutes, like
hh:mm
First cast to a varchar field and convert all times to 4 digits adding leading zeros if need be (500 would become 0500)and then break up the field with concantenation Left(myfield,2) + ':' + right(myfield,2)
This is something stupid to do every time you run a report, it is wasteful of server resources. If possible change the field to varchar and runthe code once. If not possible, can you add a formatted field and have a trigger do the formatiing on insertion (you'll still need to update the field the first time? Possibly a constraint would do instead of a trigger, but that would depend on the database.
I'm assuming that you are on SQL Server based on use of CONVERT and your previous questions.
You could use DATEADD for this too.
WITH Times AS
(
SELECT 183000 AS T
UNION ALL
SELECT 500
UNION ALL
SELECT 160000
)
SELECT CAST(DATEADD(SECOND, T%100 + (60*(T%10000 / 100)) + 3600*(T/10000),0)
AS time /*Or datetime if < SQL Server 2008*/)
FROM Times
declare #i int = 235959
select cast(stuff(stuff(
case when len(cast(#i as varchar(6))) = 6 then cast(#i as varchar(6))
else REPLICATE('0',6 - LEN(cast(#i as varchar(6))))+cast(#i as varchar(6))
end
,3,0,':'), 6, 0,':') as datetime)
set #i = 500
select cast(stuff(stuff(
case when len(cast(#i as varchar(6))) = 6 then cast(#i as varchar(6))
else REPLICATE('0',6 - LEN(cast(#i as varchar(6))))+cast(#i as varchar(6))
end
,3,0,':'), 6, 0,':') as datetime)
DECLARE #intTime int
SET #intTime = 50000
SELECT CAST(STUFF(STUFF(CONCAT(REPLICATE('0', 6 - LEN(#intTime)),#intTime),3,0,':'),6,0,':') AS TIME(0))
ALTER FUNCTION [dbo].[MinutesToDuration]
(
#minutes int
)
RETURNS nvarchar(30)
AS
BEGIN
declare #hours nvarchar(20)
DECLARE #XX NVARCHAR(10)
DECLARE #HH VARCHAR(2)
DECLARE #TT VARCHAR(2)
DECLARE #BL NVARCHAR(2)
DECLARE #TM VARCHAR(5)
SET #XX=#minutes
IF #XX<60
BEGIN
IF #XX<10
BEGIN
SET #HH='00'
SET #TT='0'+#XX
END
ELSE
BEGIN
SET #HH='00'
SET #TT=#XX
END
END
ELSE
BEGIN
IF #XX%60=0
BEGIN
SET #HH=#XX/60
SET #TT='00'
END
ELSE
BEGIN
SET #BL= #XX%60
IF #BL<10
BEGIN
SET #HH=#XX/60
SET #TT='0'+#BL
END
ELSE
BEGIN
SET #HH=#XX/60
SET #TT=#BL
END
END
END
SET #hours= #HH+':'+#TT
return #hours
END

Resources