SQL Server 2008 R2 bug or what? - sql-server

I am observing a strange behaviour. The following code that is related to conversion of a substring of nvarchar to tinyint works fine:
DECLARE #s nvarchar(50) = N'Line 1'
DECLARE #n tinyint = SUBSTRING(#s, 6, 10)
SELECT
N'Line 1' AS explicit_nvarchar,
#s AS nvarchar_variable,
SUBSTRING(#s, 6, 10) AS nvarchar_substring
, #n AS tinyint_var_converted_earlier
, CAST(SUBSTRING(#s, 6, 10) AS tinyint) AS cast_sub_var
, CAST(SUBSTRING(N'Line 1', 6, 10) AS tinyint) AS cast_nvarchar_substr
I can observe the following output in the Microsoft SQL Server Management Studio:
.
However, the code that tries to do with the SELECT from the tables fails if the commented line is uncommented:
Select
[order].[identifier] As [order_identifier],
[plan_data].[starts_on] As [from],
[plan_data].[ends_on] As [to],
[workplace].[identifier] AS line_identifier,
SUBSTRING([workplace].[identifier], 6, 10) AS line_no_str
--, CAST(SUBSTRING([workplace].[identifier], 6, 10) AS int) AS line_no
From
...
With commented line...
but with uncommented line (both for conversion to tinyint or int)...
Update: Strange. I have added the last line to the WHERE to check whether all lines
contain the 'Line...
Where
[plan].[identifier] = #lPlanIdentifier And
(
[plan_data].[starts_on] Between #fromUTC And #toUTC Or
[plan_data].[ends_on] Between #fromUTC And #toUTC)
AND LEFT([workplace].[identifier], 4) = N'Line'
... and all of a sudden it works also with the uncommented line. How can I discover what causes the problem?
Update2: I should have followed the Hamlet Hakobyan's answer exactly as it was published. When looking inside the table, I can see:

You have value in identifier column for which SUBSTRING([workplace].[identifier], 6, 10) returns ined.
Try this query:
SELECT * FROM [workplace]
WHERE SUBSTRING([identifier], 6, 10) = N'ined'

As indicated by the comments and other answer(s), you have a value in [workplace].[identifier] for one or more rows that fails to cast to an INT. When you added the following to the WHERE clause, you eliminated the row(s) that has this data before it was converted in the SELECT clause:
LEFT([workplace].[identifier], 4) = N'Line'
Run the following to get a distinct list of values in [workplace].[identifier]
SELECT DISTINCT [workplace].[identifier]
FROM [workplace]

Whenever you have an error converting a varchar to a number the easiest way to find the data is using IsNumeric. However IsNumeric doesn't mean IsInteger so its worth removing scientific or decimal formats as described by G Mastros in this answer
SELECT
[order].[identifier] As [order_identifier],
[plan_data].[starts_on] As [from],
[plan_data].[ends_on] As [to],
[workplace].[identifier] AS line_identifier,
SUBSTRING([workplace].[identifier], 6, 10) AS line_no_str
--, CAST(SUBSTRING([workplace].[identifier], 6, 10) AS int) AS line_no
FROM
...
WHERE
IsNumeric(SUBSTRING([workplace].[identifier], 6, 10) + '.0e0') = 0
If you can't fix it but you can use a default value than you could use a case statement to do the conversion only when its legal to do so.
SELECT
[order].[identifier] As [order_identifier],
[plan_data].[starts_on] As [from],
[plan_data].[ends_on] As [to],
[workplace].[identifier] AS line_identifier,
CASE WHEN IsNumeric(SUBSTRING([workplace].[identifier], 6, 10) + '.0e0') = 1
THEN CAST(SUBSTRING([workplace].[identifier], 6, 10) AS int)
ELSE Null --Default to null in this example
END as line_no

Related

Select first 2 characters and replace others

have dynamic numbers like this
33630521
4722829
36968
how can take first 2 numbers and replace others to 0 it can be varchar no problem
want result like this
33000000
4700000
36000
tried this
declare #first2 varchar(2) = (SELECT SUBSTRING('36968795', 1, 2))
declare #others varchar(100) = (SELECT SUBSTRING('36968795', 3, 100))
select #first2 + #others
but i stuck at replace #others to zeros because its unknown numbers may be anything
It could be done using string functions as follows:
Select Concat(Left(#Var, 2),Replicate('0',Len(#Var)-2))
You can use stuff
declare #x varchar(10)='123456789'
select Stuff(#x,3,10,Replicate('0',Len(#x)-2))

How can I query a varchar(x) value for the number of decimal places

I have a varchar(250) ParameterValue that I would like to check the number of decimal places in.
This is the line of code that I cannot get working:
where RIGHT(CAST(ParameterValue as DECIMAL(10,5)), ParameterValue), 1) != 0
The code below is where the line of code is used:
select *
INTO #ParamPrecision
from Data_table
where RIGHT(CAST(ParameterValue as DECIMAL(10,5)), ParameterValue), 1) != 0
AND ParameterBindStatus = 0
UPDATE a
SET a.ParameterBindStatus = 5
FROM Data_table, #ParamPrecision b
WHERE a.SQLParameterId = b.SQLParameterId
INSERT Log_table
(
SQLBatchId,
SQLProcess,
Error,
SQLError_Message,
ParametersSent
)
SELECT SQLBatchId,
'sp_ReadParametersToBindData',
1,
'Invalid parameter value sent from MES',
'some parameter info'
FROM #ParamPrecision
SELECT *
INTO #UnBoundPrompt
FROM Data_table
WHERE DATEADD(DAY, 1, SQLTimeStamp) < GETDATE()
AND ParameterBindStatus = 0
UPDATE a
SET a.ParameterBindStatus = 99
FROM Data_tablea, #UnBoundPrompt b
WHERE a.SQLParameterId = b.SQLParameterId
INSERT Log_table
(
SQLBatchId,
SQLProcess,
Error,
SQLError_Message,
ParametersSent
)
SELECT SQLBatchId,
'sp_ReadParametersToBindData',
1,
'Parameter download timeout',
'some parameter info'
FROM #UnBoundPrompt
If the check for decimal places is not satisfied, the next select statement checks if the parameter timestamp is active for more than 1 day. If this is satisfied, a log entry is made.
If the number of decimal places exceeds 4, then I want to set the ParameterBindStatus = 5 and update the log table.
I have changed the code as follows to allow me to confirm the rest of the code and that works but the code does not execute when trying to detect number of decimal places.
select *
INTO #ParamPrecision
from Data_table
where ParameterValue > '1500'
AND ParameterBindStatus = 0
this may help with your precision problem - I've laid it out as a table so you can see each step of the transformation but you can easily see the pattern :) essentially you just reverse the string and truncate it. All steps included (can be done faster) - you may/may not need to add a bit for the case that there is no decimal point.
--setup
create table test
(stringVal varchar(250));
insert into test values
('12.3456'),
('1.2345678'),
('12'),
('0.123')
--query
SELECT stringVal,
Reverse(CONVERT(VARCHAR(50), stringVal, 128)) as reversedText
, Cast(Reverse(CONVERT(VARCHAR(50), stringVal, 128)) as float) as float
, Cast(Cast(Reverse(CONVERT(VARCHAR(50), stringVal, 128)) as float) as bigint) as bigint
, len(Cast(Cast(Reverse(CONVERT(VARCHAR(50), stringVal, 128)) as float) as bigint)) as decimalPrecision
FROM test

Cast '2016-07-2914:50:13.75300' varchar to datetime T-SQL

I have varchar '2016-07-2914:50:13.75300' and want to convert it to datetime data type.
I tried with select cast('2016-07-2914:50:13.75300' as datetime)
but I am getting
Conversion failed when converting date and/or time from character string.
Insert a space between DDHH and lose the last two MS digits:
select cast(left(stuff('2016-07-2914:50:13.75300', 11, 0, ' '), 23) as datetime)
why don't you simply use
select stuff('2016-07-2914:50:13.75300', 11, 0, ' ') ?
Different approach using SUBSTRING, of course STUFF is the way to go.
DECLARE #TIME VARCHAR(50) = '2016-07-2914:50:13.75300'
SELECT CAST(SUBSTRING(#TIME, 1, 10) AS DATETIME) +
CAST(SUBSTRING(#TIME, 11, LEN(#TIME) - 10) AS TIME)
Result:
2016-07-29 14:50:13.753

Select 'long' data type with formatted output using Substring or Right/Left

My select statement returns a list of long values which cannot be used as the expression in substring.
The values returned by my query are 9 or 10 digits long. If it is 9 digits long for example 123456789 I want the result like 0-123-456-789 else in case of 10 digits like 3123456789 I want it as 3-123-456-789.
Is there any function like 'right' or 'left' which can be used similar to the substring i.e. with 3 parameters where I can mention the starting point.
Just point me to the right direction like the name of the function or the correct way to use formatting functions in a select statement.
Use Stuff and Right string function to do this.
DECLARE #digits Bigint=3123456789
SELECT Stuff(Stuff(Stuff(RIGHT('0'+ convert(varchar(10),#digits),10),2,0, '-'),6,0, '-'),10,0,'-')
Result: 3-123-456-789
IMPORTANT: formatting should be left to your presentation layer, and not your database.
That being said, if you're determined to format the values then you're going to have to convert them to strings first!
SELECT your_field As original_value
, Cast(your_field As varchar(10)) As stringified
, '0000000000' + Cast(your_field As varchar(10)) As pad_with_zeroes
, Right('0000000000' + Cast(your_field As varchar(10)), 10) As trim_to_ten_characters
FROM your_table
Your formatting then becomes easy:
SELECT original_value
, trim_to_ten_characters
, SubString(trim_to_ten_characters, 1, 1)
+ '-'
, SubString(trim_to_ten_characters, 2, 3)
+ '-'
, SubString(trim_to_ten_characters, 5, 2)
+ '-'
, SubString(trim_to_ten_characters, 8, 3) As formatified
, Stuff(Stuff(Stuff(trim_to_ten_characters, 8, 0, '-'), 5, 0, '-'), 2, 0, '-') As alternative_method
FROM (
<that query from above>
) As aliased_query

Increment a uniqueidentifier in TSQL

I am looking for a way to increment a uniqueidentifier by 1 in TSQL. For example, if the id is A6BC60AD-A4D9-46F4-A7D3-98B2A7237A9E, I'd like to be able to select A6BC60AD-A4D9-46F4-A7D3-98B2A7237A9F.
#rein It's for a data import. We have an intermediate table with IDs that we're generating records from, and we join on those IDs later in the import. Unfortunately, now some of those records generate a couple of records in the next table, so we need a new id that is reproducible.
The way you want to increment Guid is not correct for SQL Server as Guid is a structure with different byte order in the byte groups, please have a look at:
http://sqlblog.com/blogs/alberto_ferrari/archive/2007/08/31/how-are-guids-sorted-by-sql-server.aspx
and notice the following:
Now, when I run modified Alberto's query, I'm getting the following sequence:
3, 2, 1, 0, 5, 4, 7, 6, 9, 8, 15, 14, 13, 12, 11, 10
That means, that GUID's byte #3 is the least significant and GUID's byte #10 is the most significant [from SQL Server ORDER BY clause perspective].
Here is simple function to increment a uniqueidentifier accounting for this:
create function [dbo].[IncrementGuid](#guid uniqueidentifier)
returns uniqueidentifier
as
begin
declare #guid_binary binary(16), #b03 binary(4), #b45 binary(2), #b67 binary(2), #b89 binary(2), #bAF binary(6)
select #guid_binary = #guid
select #b03 = convert(binary(4), reverse(substring(#guid_binary,1,4)))
select #b45 = convert(binary(2), reverse(substring(#guid_binary,5,2)))
select #b67 = convert(binary(2), reverse(substring(#guid_binary,7,2)))
select #b89 = convert(binary(2), substring(#guid_binary,9,2))
select #bAF = convert(binary(6), substring(#guid_binary,11,6))
if (#b03 < 'FFFFFFFF')
begin
select #b03 = convert(binary(4), cast(#b03 as int) + 1)
end
else if (#b45 < 'FFFF')
begin
select #b45 = convert(binary(2), cast(#b45 as int) + 1)
end
else if (#b89 < 'FFFF')
begin
select #b89 = convert(binary(2), cast(#b89 as int) + 1)
end
else
begin
select #bAF = convert(binary(6), cast(#bAF as bigint) + 1)
end
return convert(binary(16), reverse(convert(char(4),#b03)) + reverse(convert(char(2),#b45)) + reverse(convert(char(2),#b67)) + convert(char(2),#b89) + convert(char(6),#bAF))
end
Note that bytes 6 and 7 are not incremented as they contain the Guid version bits.
But as others has pointed you really should not be doing this. In your case it might be better if you create a temp table for these Guids (with two columns: one integer as index and second one with generated Guids).
Here is one way I've come up with, but I'm hoping there is a better way.
LEFT([ID], 19) + RIGHT(CONVERT(uniqueidentifier, CONVERT(binary(16), CONVERT(binary(16), [ID]) + CONVERT(bigint, 1))), 17) AS 'MyNewID'
You can do this approach, but I'm not accounting for the case of overflowing lower 8 bytes.
declare #guid uniqueidentifier, #binaryUpper8 binary(8), #binaryLower8 binary(8), #binary16 binary(16), #bigint bigint
set #guid = 'A6BC60AD-A4D9-46F4-A7D3-98B2A7237A9E'
set #binary16 = cast(#guid as binary(16))
--harvest lower 8 bytes
select #binaryUpper8= substring(#binary16, 1, 8)
,#binaryLower8 = substring(#binary16, 9, 8)
set #bigint = cast(#binaryLower8 as bigint)
--increment
set #bigint = #bigint + 1
--convert back
set #binaryLower8 = cast(#bigint as binary(8))
set #binary16 = #binaryUpper8 + #binaryLower8
set #guid = cast(#binary16 as uniqueidentifier)
select #guid

Resources