I have a one database table field called Amount which type is decimal(18,6). so it is stored in database up to 6 decimal points like 9.786534 But while retrieving that field using select query i have to take care like following
Remove trialling zero e.g if number is 9.230000 then result is only 9.23
If decimal points are all zero then only remove only four trialling zero e.g If number is 9.000000 then result is 9.00
Result is up to 2 decimal point if there are trialling zero.
If we write simple query like
select TOP 1 Amount From EmployeeMaster
then it gives 9.230000
but my intension is to remove trailing zero..
Please help me..
It works for removing trailing zeros, but I am still not able to convert 9 to 9.00 in this method.
Declare #myvalue varchar(50),
#Price Varchar(50)
Set #Price = '9.230000'
set #Myvalue = reverse(substring(#Price,patindex('%.%',#Price)+1,len(#Price)))
SELECT
case
When patindex('%.%[1-9]%',#price) = 0 Then
substring(#price,1,patindex('%.%',#price)-1)
else
substring(#price,1,patindex('%.%',#price)-1) + '.' + Reverse(substring(#Myvalue,patindex('%[1-9]%',#Myvalue),len(#Myvalue)))
END
Coming from decimal(18,6) you could do...
select cast(Amount as decimal(18,2))
Most databases that support the CAST function will round the number while converting it. On SQLServer this is what I would do if I wanted rounding.
If what you actually want is a string with only two digits after the decimal then you could
select cast((Amount as decimal(18,2)) as nvarchar)
nvarchar is SQLServer's variable length unicode type. Databases do not agree much on string types. Your database may have a different one. The rest of that sql is ANSI standard. Not all dbs support that either but many do.
This should work
SELECT CAST(REPLACE(RTRIM(REPLACE(CAST(CAST(33.9082976 AS DECIMAL(38,8)) AS NVARCHAR(256)),'0',' ')),' ','0') AS FLOAT)
Does this work?
select TOP 1 ROUND(Amount, 2) From EmployeeMaster
TRY below mentioned code.
SELECT TOP 1 CONVERT(DECIMAL(10,2),Amount) From EmployeeMaster
Hope it will work as expected.
An alternative approach:
1) convert the decimal to a string;
2) split the string into 2 parts, separating the last 4 characters from the rest of the string;
3) remove trailing zeros from the last 4 characters;
4) concatenate the two parts back.
WITH data (V) AS (SELECT CAST(9.786534 AS decimal(18,6))
UNION ALL
SELECT CAST(9.78653 AS decimal(18,6))
UNION ALL
SELECT CAST(9.7800 AS decimal(18,6))
UNION ALL
SELECT CAST(9.7 AS decimal(18,6))
UNION ALL
SELECT CAST(9.00000 AS decimal(18,6))
)
, AsString (V) AS (SELECT CAST(V AS varchar) FROM data)
, Split (L, R) AS (SELECT LEFT(V, LEN(V) - 4), RIGHT(V, 4) FROM AsString)
, Adjusted AS (SELECT L,
REPLACE(RTRIM(REPLACE(R, '0', ' ')), ' ', '0') AS R
FROM Split)
SELECT Result = L + R FROM Adjusted
The output of the above script is:
Result
--------
9.786534
9.78653
9.78
9.70
9.00
I guess using patindex in your case:
CASE WHEN FLOOR(Amount) <> CEILING(Amount) THEN
LTRIM(SUBSTRING(STR(Amount, 18, 6), 1, LEN(STR(Amount, 18, 6)) - PATINDEX('%[^0]%', REVERSE(str(Amount, 18, 6))) + 1))
ELSE STR(Amount,18,2)
END
for a decimal(18,6) field this should work:
select trim(to_char(Amount, '999999999999999999.99')) from EmployeeMaster
(at least for Oracle, not sure about other types)
Related
I am writing a SQL query returning gross pay from two separate tables. The amounts in both tables are of nvarchar datatype. In one table I have '150.0000' and on the other I have '150' is there any way to remove the trailing 0's? Here is my code
SELECT DISTINCT
s.EEid,
s.Gross AS 'S Gross',
pr.EEid,
pr.Gross AS 'PR Gross'
FROM
starPayHistory s
INNER JOIN
payRunPayHistory pr ON pr.EEid = s.EEid
ORDER BY
s.EEid
Results from query
I have tried this line of code to trim the leading 0's but it returns '15.' it removes all 0's.
Here is the code
REPLACE(ltrim(REPLACE(s.Gross,'0','')), ' ', '0') AS 'S Gross'
Result from this line of code
Any Help would be greatly appreciated!
DECLARE #x as varchar(32) = '653710000.01';
--note rounding error occurs
select CAST(CAST(#x As FLOAT) AS NVARCHAR(32));
--how to break this one?
select
CASE WHEN PATINDEX('%[.]%',REVERSE(#x)) = PATINDEX('%[123456789.]%',REVERSE(#x))
THEN
LEFT(#x, LEN(#X) - PATINDEX('%[.]%',REVERSE(#x)))
ELSE
CASE WHEN PATINDEX('%[.]%',REVERSE(#x)) > 0
THEN
LEFT(#x, 1 + LEN(#X) - PATINDEX('%[123456789.]%',REVERSE(#x)))
ELSE
#X
END
END ;
Try this:
CAST(CAST(s.Gross As FLOAT) AS NVARCHAR(32))
But if you have control of the table note that several comments on your question are saying that would be better to store as numeric. And not that this takes advantage of making it numeric to work with. So if you can, consider changing the type on the table and I agree with the others, the problem just goes away.
I wouldn't recommend converting to a float, because that can have unintended consequences because float is not an exact representation. You can just use string operations. For instance:
select (case when s.gross like '%.%'
then replace(rtrim(replace(s.gross, '0', ' ')), ' ', '0')
else s.gross
end)
For instance, try running this db<>fiddle.
I have the following code in a stored procedure and am trying to conditionally format a calculated number based on its length (if the number is less than 4 digits, pad with leading zeros). However, my case statement is not working. The "formattedNumber2" result is the one I'm looking for.
I'm assuming the case statement treats the variable strangely, but I also don't know of a way around this.
DECLARE #Number int = 5
SELECT
CASE
WHEN (LEN(CONVERT(VARCHAR, #Number)) > 4)
THEN #Number
ELSE RIGHT('0000' + CAST(#Number AS VARCHAR(4)), 4)
END AS formattedNumber,
LEN(CONVERT(VARCHAR, #Number)) AS numberLength,
RIGHT('0000' + CAST(#Number AS VARCHAR(4)), 4) AS formattedNumber2
I get the following results when I run the query:
formattedNumber numberLength formattedNumber2
-------------------------------------------------
5 1 0005
SQL DEMO
The problem is you are using different data type on your case , integer and string. So the CASE stay with the first type he find and convert the rest.
CASE WHEN (LEN(convert(VARCHAR, #Number)) > 4) THEN convert(VARCHAR, #Number)
This can be done a lot easier with format() since version 2012.
format(n,
'0000')
And that would also handle negative values, which your current approach apparently doesn't.
Prior 2012 it can be handled with basically replicate() and + (string concatenation).
isnull(replicate('-',
-sign(n)), '')
+
isnull(replicate('0',
4
-
len(cast(abs(n) AS varchar(10)))
),
'')
+
cast(abs(n) AS varchar(10))
(It targets integer values, choose a larger length for the varchar casts for bigint.)
db<>fiddle
So if I have a data (varchar) like say 10.1
I need the value as 0000101000000.
means (000010) whole number and (1000000) decimal value.
Its a 13 character string ,numbers coming before decimal point should be in first 6 characters and numbers coming after decimal point should be in last 7 characters
Maybe..?
DECLARE #d decimal(13,7) = 10.1;
SELECT RIGHT('0000000000000' + CONVERT(varchar(13),CONVERT(bigint,(#d * 10000000))),13);
Using my crystal ball here though.
Edit: As, for some reason, the OP is storing a decimal as a varchar (this is a really bad bad idea on it's own), I have added further logic to attempt to convert the value to a decimal first.
As experience has taught many of us, give a user a non-numeric column to store a numeric value in and they're more than happily store a non-numeric value in it, so i have used TRY_CONVERT and assumed you are using SQL Server 2012+:
DECLARE #d varchar(13) = 10.1;
SELECT RIGHT('0000000000000' + CONVERT(varchar(13),CONVERT(bigint,(TRY_CONVERT(decimal(13,7),#d) * 10000000))),13);
SELECT REPLICATE('0',6-LEN(SUBSTRING(CAST([data] AS VARCHAR), 1,
CHARINDEX('.',CAST([data] AS VARCHAR)) -1)))+SUBSTRING(CAST([data] AS VARCHAR), 1,
CHARINDEX('.',CAST([data] AS VARCHAR)) -1)+
SUBSTRING(CAST([data] AS VARCHAR), CHARINDEX('.',CAST([data] AS VARCHAR)) + 1,
LEN(CAST([data] AS VARCHAR)))+REPLICATE('0',7-LEN(SUBSTRING(CAST([data] AS VARCHAR), CHARINDEX('.',CAST([data] AS VARCHAR)) + 1,
LEN(CAST([data] AS VARCHAR))))) AS Whole
FROM Table1
Output
Whole
0000101000000
Demo
http://sqlfiddle.com/#!18/8649d/16
You can use some math and string operations to do it like below
see live demo
declare #var decimal(10,4)
set #var=10.1
select #var,
right(cast(cast(( floor(#var)+ power(10,7)) as int) as varchar(13)),6)
+
cast(cast(((#var- floor(#var)) * power(10,7)) as int) as varchar(13))
There's a fair amount of string manipulation to be done here. I'll step through what I did.
I used a variable for the base number so I could verify different results:
declare #n decimal(9,3) = 10.1
You need 6 spaces left of the decimal and 7 spaces to the right, so I'm doing all the manipulation on a VARCHAR(13). I didn't create a new variable as a VARCHAR because I'm assuming you want to be able to do this conversion in line on the fly, so I'm using that CAST over and over again.
Start by finding the decimal place.
SELECT CHARINDEX('.',CAST(#n as VARCHAR(13)))
In the sample number, that's a 3, but it could obviously change.
Now, get the portion of the number to the left of the decimal place.
SELECT SUBSTRING(CAST(#n as VARCHAR(13)),1,CHARINDEX('.',CAST(#n as VARCHAR(13)))-1)
Then get the portion to the right of the decimal.
SELECT SUBSTRING(CAST(#n as VARCHAR(13)),CHARINDEX('.',CAST(#n as VARCHAR(13)))+1,LEN(CAST(#n as VARCHAR(13))))
Pad the leading zeroes. Put 6 on, concatenate, and take a RIGHT 6. Accounts for no digits to the left of the decimal.
SELECT RIGHT(REPLICATE(0,6) + SUBSTRING(CAST(#n as VARCHAR(13)),1,CHARINDEX('.',CAST(#n as VARCHAR(13)))-1), 6)
Pad the trailing zeroes. Same idea, but in the other direction.
SELECT LEFT(SUBSTRING(CAST(#n as VARCHAR(13)),CHARINDEX('.',CAST(#n as VARCHAR(13)))+1,LEN(CAST(#n as VARCHAR(13)))) + REPLICATE(0,7),7)
Then put it all together.
SELECT RIGHT(REPLICATE(0,6) + SUBSTRING(CAST(#n as VARCHAR(13)),1,CHARINDEX('.',CAST(#n as VARCHAR(13)))-1), 6)
+
LEFT(SUBSTRING(CAST(#n as VARCHAR(13)),CHARINDEX('.',CAST(#n as VARCHAR(13)))+1,LEN(CAST(#n as VARCHAR(13)))) + REPLICATE(0,7),7)
Results.
0000101000000
declare #var varchar(20) = '10000.112'
SELECT FORMAT (FLOOR(#var), '000000') + left((PARSENAME(#var,1)) + replicate('0',7),7)
I want to convert a varchar(max) column to decimal(10,4).
When I try to use cast or convert I am getting an arithmetic overflow exception. The issue is that the data stored in the varchar column may contain different precisions and different scales. For example, 123456789.1234567', 1.12345678 or 123456.1234.
For values like 123456.1234 it is converting with out any issue but for other values I am having some problems.
After testing I found that it was not the decimal place that was causing the problem, it was the precision (10)
This doesn't work: Arithmetic overflow error converting varchar to data type numeric.
DECLARE #TestConvert VARCHAR(MAX) = '123456789.12343594'
SELECT CAST(#TestConvert AS DECIMAL(10, 4))
This worked
DECLARE #TestConvert VARCHAR(MAX) = '123456789.12343594'
SELECT CAST(#TestConvert AS DECIMAL(13, 4))
Should be like 9 int + 4 floating = 13 chars
My explanation is in the code. :)
DECLARE #TestConvert VARCHAR(MAX) = '123456789.1234567'
BEGIN TRY
SELECT CAST(#TestConvert AS DECIMAL(10, 4))
END TRY
BEGIN CATCH
SELECT 'The reason you get the message "' + ERROR_MESSAGE() + '" is because DECIMAL(10, 4) only allows for 4 numbers after the decimal.'
END CATCH
-- Here's one way to truncate the string to a castable value.
SELECT CAST(LEFT(#TestConvert, (CHARINDEX('.', #TestConvert, 1) + 4)) AS DECIMAL(14, 4))
-- If you noticed, I changed it to DECIMAL(14, 4) instead of DECIMAL(10, 4) That's because this number has 14 digits, as proven below.
-- Read this for a better explanation as to what precision, scale and length mean: http://msdn.microsoft.com/en-us/library/ms190476(v=sql.105).aspx
SELECT LEN(LEFT(#TestConvert, (CHARINDEX('.', #TestConvert, 1) + 4)))
I came up with the following solution:
SELECT [Str], DecimalParsed = CASE
WHEN ISNUMERIC([Str]) = 1 AND CHARINDEX('.', [Str])=0 AND LEN(REPLACE(REPLACE([Str], '-', ''), '+', '')) < 29 THEN CONVERT(decimal(38,10), [Str])
WHEN ISNUMERIC([Str]) = 1 AND (CHARINDEX('.', [Str])!=0 AND CHARINDEX('.', REPLACE(REPLACE([Str], '-', ''), '+', ''))<=29) THEN
CONVERT(decimal(38,10),
CASE WHEN LEN([Str]) - LEN(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE([Str], '0', ''), '1', ''), '2', ''), '3', ''), '4', ''), '5', ''), '6', ''), '7', ''), '8', ''), '9', '')) <= 38
THEN [Str]
ELSE SUBSTRING([Str], 1, 38 + LEN(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE([Str], '0', ''), '1', ''), '2', ''), '3', ''), '4', ''), '5', ''), '6', ''), '7', ''), '8', ''), '9', ''))) END)
ELSE NULL END
FROM TestStrToDecimal
I know it looks like an overkill and probably it is, but it works for me (checked both positive, negative, big and small numbers of different precision and scale - everything is converted to decimal(38,10) or NULL).
It is hard-coded to decimal(38,10) type, so if you need different precision, change the constants in the code (38, 10, 29).
How it works? The result is:
if conversion is simple without overflow or precision loss (e.g. 123 or 123.456), then it just convert it.
if number is not too big, but has too many digits after decimal point (e.g. 123.1234567890123456789012345678901234567890), then it trims the exceeding digits at the end keeping only 38 first digits.
if number is too big and can't be converted to decimal without an overflow (e.g. 9876543210987654321098765432109876543210), then NULL is returned
each case is separate WHEN statement inthe code above.
Here are few examples of conversion:
You still haven't explained why you can't use a Float data type, so here is an example:
DECLARE #StringVal varchar(50)
SET #StringVal = '123456789.1234567'
SELECT #StringVal, CAST(#StringVal AS FLOAT)
SET #StringVal = '1.12345678'
SELECT #StringVal, CAST(#StringVal AS FLOAT)
SET #StringVal = '123456.1234'
SELECT #StringVal, CAST(#StringVal AS FLOAT)
You are missing the fact that 6.999,50 is not a valid decimal. You can't have a comma and a decimal point in a decimal value surely? What number is it supposed to be?
Assuming your locale specifies . as grouping and , as decimal separator: To remove the grouping digits:
SELECT CONVERT(decimal(11,2), REPLACE('6.999,50', '.', ''))
will yield 6999,50 as a decimal
You are going to have to truncate the values yourself as strings before you put them into that column.
Otherwise, if you want more decimal places, you will need to change your declaration of the decimal column.
Your major problem is not the stuff to the right of the decimal, it is the stuff to the left. The two values in your type declaration are precision and scale.
From MSDN: "Precision is the number of digits in a number. Scale is
the number of digits to the right of the decimal point in a number.
For example, the number 123.45 has a precision of 5 and a scale of 2."
If you specify (10, 4), that means you can only store 6 digits to the left of the decimal, or a max number of 999999.9999. Anything bigger than that will cause an overflow.
Implemented using Custom Function.
This will check whether the string value can be converted to Decimal safely
CREATE FUNCTION [dbo].[TryParseAsDecimal]
(
#Value NVARCHAR(4000)
,#Precision INT
,#Scale INT
)
RETURNS BIT
AS
BEGIN
IF(ISNUMERIC(#Value) =0) BEGIN
RETURN CAST(0 AS BIT)
END
SELECT #Value = REPLACE(#Value,',','') --Removes the comma
--This function validates only the first part eg '1234567.8901111111'
--It validates only the values before the '.' ie '1234567.'
DECLARE #Index INT
DECLARE #Part1Length INT
DECLARE #Part1 VARCHAR(4000)
SELECT #Index = CHARINDEX('.', #Value, 0)
IF (#Index>0) BEGIN
--If decimal places, extract the left part only and cast it to avoid leading zeros (eg.'0000000001' => '1')
SELECT #Part1 =LEFT(#Value, #Index-1);
SELECT #Part1=SUBSTRING(#Part1, PATINDEX('%[^0]%', #Part1+'.'), LEN(#Part1));
SELECT #Part1Length = LEN(#Part1);
END
ELSE BEGIN
SELECT #Part1 =CAST(#Value AS DECIMAL);
SELECT #Part1Length= LEN(#Part1)
END
IF (#Part1Length > (#Precision-#Scale)) BEGIN
RETURN CAST(0 AS BIT)
END
RETURN CAST(1 AS BIT)
END
I know this is an old question, but Bill seems to be the only one that has actually "Explained" the issue. Everyone else seems to be coming up with complex solutions to a misuse of a declaration.
"The two values in your type declaration are precision and scale."
...
"If you specify (10, 4), that means you can only store 6 digits to the
left of the decimal, or a max number of 999999.9999. Anything bigger
than that will cause an overflow."
So if you declare DECIMAL(10,4) you can have a total of 10 numbers, with 4 of them coming AFTER the decimal point.
so 123456.1234 has 10 digits, 4 after the decimal point. That will fit into the parameters of DECIMAL(10,4).
1234567.1234 will throw an error. there are 11 digits to fit into a 10 digit space, and 4 digits MUST be used AFTER the decimal point. Trimming a digit off the left side of the decimal is not an option.
If your 11 characters were 123456.12345, this would not throw an error as trimming(Rounding) from the end of a decimal value is acceptable.
When declaring decimals, always try to declare the maximum that your column will realistically use and the maximum number of decimal places you want to see.
So if your column would only ever show values with a maximum of 1 million and you only care about the first two decimal places, declare as DECIMAL(9,2).
This will give you a maximum number of 9,999,999.99 before an error is thrown.
Understanding the issue before you try to fix it, will ensure you choose the right fix for your situation, and help you to understand the reason why the fix is needed / works.
Again, i know i'm five years late to the party.
However, my two cents on a solution for this, (judging by your comments that the column is already set as DECIMAL(10,4) and cant be changed)
Easiest way to do it would be two steps.
Check that your decimal is not further than 10 points away, then trim to 10 digits.
CASE WHEN CHARINDEX('.',CONVERT(VARCHAR(50),[columnName]))>10 THEN 'DealWithIt'
ELSE LEFT(CONVERT(VARCHAR(50),[columnName]),10)
END AS [10PointDecimalString]
The reason i left this as a string is so you can deal with the values that are over 10 digits long on the left of the decimal.
But its a start.
create function [Sistema].[fParseDecimal]
(
#Valor nvarchar(4000)
)
returns decimal(18, 4) as begin
declare #Valores table (Valor varchar(50));
insert into #Valores values (#Valor);
declare #Resultado decimal(18, 4) = (select top 1
cast('' as xml).value('sql:column("Valor") cast as xs:decimal ?', 'decimal(18, 4)')
from #Valores);
return #Resultado;
END
In case you need to ROUND the result, not truncate, can use this:
select convert(decimal(38,4), round(convert(decimal(38,10), '123456789.1234567'),4))
This will return the following:
'123456789.1235' for '123456789.1234567'
'123456789.1234' for '123456789.1234467'
In MySQL
select convert( if( listPrice REGEXP '^[0-9]+$', listPrice, '0' ), DECIMAL(15, 3) ) from MyProduct WHERE 1
I've got a column in a database table (SQL Server 2005) that contains data like this:
TQ7394
SZ910284
T r1534
su8472
I would like to update this column so that the first two characters are uppercase. I would also like to remove any spaces between the first two characters. So T q1234 would become TQ1234.
The solution should be able to cope with multiple spaces between the first two characters.
Is this possible in T-SQL? How about in ANSI-92? I'm always interested in seeing how this is done in other db's too, so feel free to post answers for PostgreSQL, MySQL, et al.
Here is a solution:
EDIT: Updated to support replacement of multiple spaces between the first and the second non-space characters
/* TEST TABLE */
DECLARE #T AS TABLE(code Varchar(20))
INSERT INTO #T SELECT 'ab1234x1' UNION SELECT ' ab1234x2'
UNION SELECT ' ab1234x3' UNION SELECT 'a b1234x4'
UNION SELECT 'a b1234x5' UNION SELECT 'a b1234x6'
UNION SELECT 'ab 1234x7' UNION SELECT 'ab 1234x8'
SELECT * FROM #T
/* INPUT
code
--------------------
ab1234x3
ab1234x2
a b1234x6
a b1234x5
a b1234x4
ab 1234x8
ab 1234x7
ab1234x1
*/
/* START PROCESSING SECTION */
DECLARE #s Varchar(20)
DECLARE #firstChar INT
DECLARE #secondChar INT
UPDATE #T SET
#firstChar = PATINDEX('%[^ ]%',code)
,#secondChar = #firstChar + PATINDEX('%[^ ]%', STUFF(code,1, #firstChar,'' ) )
,#s = STUFF(
code,
1,
#secondChar,
REPLACE(LEFT(code,
#secondChar
),' ','')
)
,#s = STUFF(
#s,
1,
2,
UPPER(LEFT(#s,2))
)
,code = #s
/* END PROCESSING SECTION */
SELECT * FROM #T
/* OUTPUT
code
--------------------
AB1234x3
AB1234x2
AB1234x6
AB1234x5
AB1234x4
AB 1234x8
AB 1234x7
AB1234x1
*/
UPDATE YourTable
SET YourColumn = UPPER(
SUBSTRING(
REPLACE(YourColumn, ' ', ''), 1, 2
)
)
+
SUBSTRING(YourColumn, 3, LEN(YourColumn))
UPPER isn't going to hurt any numbers, so if the examples you gave are completely representative, there's not really any harm in doing:
UPDATE tbl
SET col = REPLACE(UPPER(col), ' ', '')
The sample data only has spaces and lowercase letters at the start. If this holds true for the real data then simply:
UPPER(REPLACE(YourColumn, ' ', ''))
For a more specific answer I'd politely ask you to expand on your spec, otherwise I'd have to code around all the other possibilities (e.g. values of less than three characters) without knowing if I was overengineering my solution to handle data that wouldn't actually arise in reality :)
As ever, once you've fixed the data, put in a database constraint to ensure the bad data does not reoccur e.g.
ALTER TABLE YourTable ADD
CONSTRAINT YourColumn__char_pos_1_uppercase_letter
CHECK (ASCII(SUBSTRING(YourColumn, 1, 1)) BETWEEN ASCII('A') AND ASCII('Z'));
ALTER TABLE YourTable ADD
CONSTRAINT YourColumn__char_pos_2_uppercase_letter
CHECK (ASCII(SUBSTRING(YourColumn, 2, 1)) BETWEEN ASCII('A') AND ASCII('Z'));
#huo73: yours doesn't work for me on SQL Server 2008: I get 'TRr1534' instead of 'TR1534'.
update Table set Column = case when len(rtrim(substring (Column , 1 , 2))) < 2
then UPPER(substring (Column , 1 , 1) + substring (Column , 3 , 1)) + substring(Column , 4, len(Column)
else UPPER(substring (Column , 1 , 2)) + substring(Column , 3, len(Column) end
This works on the fact that if there is a space then the trim of that part of string would yield length less than 2 so we split the string in three and use upper on the 1st and 3rd char. In all other cases we can split the string in 2 parts and use upper to make the first two chars to upper case.
If you are doing an UPDATE, I would do it in 2 steps; first get rid of the space (RTRIM on a SUBSTRING), and second do the UPPER on the first 2 chars:
// uses a fixed column length - 20-odd in this case
UPDATE FOO
SET bar = RTRIM(SUBSTRING(bar, 1, 2)) + SUBSTRING(bar, 3, 20)
UPDATE FOO
SET bar = UPPER(SUBSTRING(bar, 1, 2)) + SUBSTRING(bar, 3, 20)
If you need it in a SELECT (i.e. inline), then I'd be tempted to write a scalar UDF