T-SQL: How to obtain the exact length of a string in characters? - tsql

I'm generating T-SQL SELECT statements for tables for which I have no data type information up-front. In these statements, I need to perform string manipulation operations that depend on the length of the original value of the tables' columns.
One example (but not the only one) is to insert some text at a specific position in a string, including the option to insert it at the end:
SELECT
CASE WHEN (LEN ([t0].[Product] = 8)
THEN [t0].[Product] + 'test'
ELSE STUFF ([t0].[Product], 8, 0, 'test')
END
FROM [OrderItem] [t0]
(The CASE WHEN + LEN is required because STUFF doesn't allow me to insert text at the end of a string.)
The problem is that LEN excludes trailing blanks, which will ruin the calculation.
I know I can use DATALENGTH, which does not exclude trailing blanks, but I can't convert the bytes returned by DATALENGTH to the characters required by STUFF because I don't know whether the Product column is of type varchar or nvarchar.
So, how can I generate a SQL statement that depends on the exact length of a string in characters without up-front information about the string data type being used?

Here's what I ended up using:
SELECT
CASE WHEN ((LEN ([t0].[Product] + '#') - 1) = 8)
THEN [t0].[Product] + 'test'
ELSE STUFF ([t0].[Product], 8, 0, 'test')
END
FROM [OrderItem] [t0]
Measurements indicate that the LEN (... + '#') - 1 trick is about the same speed as LEN (...) alone.
Thanks for all the good answers!

try this:
SELECT
CASE WHEN (LEN (REPLACE([t0].[Product],' ', '#') = 8)
THEN [t0].[Product] + 'test'
ELSE STUFF ([t0].[Product], 8, 0, 'test')
END
FROM [OrderItem] [t0]

Can't you look up the type information for the columns in the system tables?
If not then to determine whether or not a column is varchar or nvarchar this would do it.
create table #test
(
c varchar(50),
n nvarchar(50)
)
insert into #test values ('1,2,3,4 ',N'1,2,3,4,5 ')
SELECT
CASE
WHEN datalength(CAST(c AS nvarchar(MAX))) = datalength(c)
THEN 'c is nvarchar'
ELSE 'c is char'
END,
CASE
WHEN datalength(CAST(n AS nvarchar(MAX))) = datalength(n)
THEN 'n is nvarchar'
ELSE 'n is char'
END
FROM #test

Use DATALENGTH and SQL_VARIANT_PROPERTY:
SELECT
CASE
WHEN 8
= DATALENGTH([t0].[Product])
/ CASE SQL_VARIANT_PROPERTY([t0].[Product],'BaseType') WHEN 'nvarchar' THEN 2 ELSE 1 END
THEN [t0].[Product] + 'test'
ELSE STUFF ([t0].[Product], 8, 0, 'test')
END
FROM [OrderItem] [t0]

If there are no leading blanks, len(reverse(column_name)) will give you the column length.

Related

convert actual string and characters to numbers in PostgreSQL

Is there a way i could convert an actual string to a number in postgresql
i have record stored in db and below is an example of unique identifier found in each record.
d895774d-7b91-4adc-bfba-f1264e5aae04
I want to store this record in another database but i would want to generate another unique number for each transaction/line
Is there a way i can convert this d895774d-7b91-4adc-bfba-f1264e5aae04 to some number.
like this actual numbers 13693660123082308270370273012321321312321
select 'd895774d-7b91-4adc-bfba-f1264e5aae04' as id
First convert the string into a table of single characters (the from clause);
Then select the digits 'as is', a as 10, b as 11 and so on (the case expression);
Finally aggregate into a string (string_agg)
skipping the - characters (where clause).
select
string_agg(case when c between '0' and '9' then c else (ascii(c) - 87)::text end, '')
from unnest(string_to_array('d895774d-7b91-4adc-bfba-f1264e5aae04', null)) c
where c <> '-';
Result: 13895774137119141013121115111015126414510101404
Edit
select
td.date,
(
select string_agg(case when c between '0' and '9' then c else (ascii(c) - 87)::text end, '')
from unnest(string_to_array(td.id, null)) c
where c <> '-'
) as id
from table_d td;

How to find/replace weird whitespace in string

I find in my sql database string whit weird whitespace which cannot be replace like REPLACE(string, ' ', '') RTRIM and cant it even find with string = '% %'. This space is even transfered to new table when using SELECT string INTO
If i select this string in managment studio and copy that is seems is normal space and when everything is works but cant do nothing directly from database. What else can i do? Its some kind of error or can i try some special character for this?
First, you must identify the character.
You can do that by using a tally table (or a cte) and the Unicode function:
The following script will return a table with two columns: one contains a char and the other it's unicode value:
DECLARE #Str nvarchar(100) = N'This is a string containing 1 number and some words.';
with Tally(n) as
(
SELECT TOP(LEN(#str)) ROW_NUMBER() OVER(ORDER BY ##SPID)
FROM sys.objects a
--CROSS JOIN sys.objects b -- (unremark if there are not enough rows in the tally cte)
)
SELECT SUBSTRING(#str, n, 1) As TheChar,
UNICODE(SUBSTRING(#str, n, 1)) As TheCode
FROM Tally
WHERE n <= LEN(#str)
You can also add a condition to the where clause to only include "special" chars:
AND SUBSTRING(#str, n, 1) NOT LIKE '[a-zA-Z0-9]'
Then you can replace it using it's unicode value using nchar (I've used 32 in this example since it's unicode "regular" space:
SELECT REPLACE(#str, NCHAR(32), '|')
Result:
This|is|a|string|containing|1|number|and|some|words.

Get Substrings From DB2 Column

I Have: AAAA/DATA1/Data2;xyx;pqr
this data
I want only:DATA1 And Data2
If this is for a specific row, maybe use SUBSTR? Something like
SELECT
SUBSTR(column, 6, 5) AS col1
, SUBSTR(column, 13, 5) AS col2
FROM table
Here is something else you can do.. Although it gets pretty complicated, and this isn't the exact answer you are looking for but it will get you started. Hope this helps:
WITH test AS (
SELECT characters
FROM ( VALUES
( 'AAAA/DATA1/Data2;xyx;pqr'
) )
AS testing(characters)
)
SELECT
SUBSTR(characters, 1, LOCATE('/', characters) - 1) AS FIRST_PART
, SUBSTR(characters, LOCATE('/', characters) + 1) AS SECOND_PART
, SUBSTR(characters, LOCATE('/', characters, LOCATE('/', characters) + 1) + 1)
AS THIRD_PART
FROM test
;
DB2 does not have a single function for this, unfortunately. Check out this answer here: How to split a string value based on a delimiter in DB2

How instead of Numeric display string as '-' in a CASE statement

I have a simple CASE statement where in my ELSE block numeric column I want to display as '-'.
But it gives me an error
Arithmetic overflow error converting varchar to data type numeric.
How would I do that?
I want it like this:
SELECT
CASE WHEN ChargeName = 'Premium' THEN CompanyCommissionPercentage ELSE '-' END AS CompanyCommissionPercentage
,CASE WHEN ChargeName = 'Premium' THEN RemitterCommissionPercentage ELSE '-' END AS RemitterCommissionPercentage
,CASE WHEN ChargeName = 'Premium' THEN RemitterCommission ELSE '-'END AS RemitterCommission
,CASE WHEN ChargeName = 'Premium' THEN GrossCommission ELSE '-'END AS GrossCommission
FROM #tmpAccountsPayable
`SELECT
CASE WHEN ChargeName = 'Premium' THEN CAST(CompanyCommissionPercentage as varchar(10)) ELSE CAST('-' as varchar(10)) END AS CompanyCommissionPercentage
FROM #tmpAccountsPayable`
Here's a picture of where in excel you can set formatting to a -
You may be better off passing in a zero instead of the dash and allowing excel formatting to make the 0 a - (assuming of course the output is going into excel)
Notice below in E20 the 0 value is converted to a - when accounting format (comma format) is used.
Also notice how a regular dash is left aligned while the accounting - is indented.

Extract the first word of a string in a SQL Server query

What's the best way to extract the first word of a string in sql server query?
SELECT CASE CHARINDEX(' ', #Foo, 1)
WHEN 0 THEN #Foo -- empty or single word
ELSE SUBSTRING(#Foo, 1, CHARINDEX(' ', #Foo, 1) - 1) -- multi-word
END
You could perhaps use this in a UDF:
CREATE FUNCTION [dbo].[FirstWord] (#value varchar(max))
RETURNS varchar(max)
AS
BEGIN
RETURN CASE CHARINDEX(' ', #value, 1)
WHEN 0 THEN #value
ELSE SUBSTRING(#value, 1, CHARINDEX(' ', #value, 1) - 1) END
END
GO -- test:
SELECT dbo.FirstWord(NULL)
SELECT dbo.FirstWord('')
SELECT dbo.FirstWord('abc')
SELECT dbo.FirstWord('abc def')
SELECT dbo.FirstWord('abc def ghi')
I wanted to do something like this without making a separate function, and came up with this simple one-line approach:
DECLARE #test NVARCHAR(255)
SET #test = 'First Second'
SELECT SUBSTRING(#test,1,(CHARINDEX(' ',#test + ' ')-1))
This would return the result "First"
It's short, just not as robust, as it assumes your string doesn't start with a space. It will handle one-word inputs, multi-word inputs, and empty string inputs.
Enhancement of Ben Brandt's answer to compensate even if the string starts with space by applying LTRIM(). Tried to edit his answer but rejected, so I am now posting it here separately.
DECLARE #test NVARCHAR(255)
SET #test = 'First Second'
SELECT SUBSTRING(LTRIM(#test),1,(CHARINDEX(' ',LTRIM(#test) + ' ')-1))
Adding the following before the RETURN statement would solve for the cases where a leading space was included in the field:
SET #Value = LTRIM(RTRIM(#Value))
Marc's answer got me most of the way to what I needed, but I had to go with patIndex rather than charIndex because sometimes characters other than spaces mark the ends of my data's words. Here I'm using '%[ /-]%' to look for space, slash, or dash.
Select race_id, race_description
, Case patIndex ('%[ /-]%', LTrim (race_description))
When 0 Then LTrim (race_description)
Else substring (LTrim (race_description), 1, patIndex ('%[ /-]%', LTrim (race_description)) - 1)
End race_abbreviation
from tbl_races
Results...
race_id race_description race_abbreviation
------- ------------------------- -----------------
1 White White
2 Black or African American Black
3 Hispanic/Latino Hispanic
Caveat: this is for a small data set (US federal race reporting categories); I don't know what would happen to performance when scaled up to huge numbers.
DECLARE #string NVARCHAR(50)
SET #string = 'CUT STRING'
SELECT LEFT(#string,(PATINDEX('% %',#string)))
Extract the first word from the indicated field:
SELECT SUBSTRING(field1, 1, CHARINDEX(' ', field1)) FROM table1;
Extract the second and successive words from the indicated field:
SELECT SUBSTRING(field1, CHARINDEX(' ', field1)+1, LEN (field1)-CHARINDEX(' ', field1)) FROM table1;
A slight tweak to the function returns the next word from a start point in the entry
CREATE FUNCTION [dbo].[GetWord]
(
#value varchar(max)
, #startLocation int
)
RETURNS varchar(max)
AS
BEGIN
SET #value = LTRIM(RTRIM(#Value))
SELECT #startLocation =
CASE
WHEN #startLocation > Len(#value) THEN LEN(#value)
ELSE #startLocation
END
SELECT #value =
CASE
WHEN #startLocation > 1
THEN LTRIM(RTRIM(RIGHT(#value, LEN(#value) - #startLocation)))
ELSE #value
END
RETURN CASE CHARINDEX(' ', #value, 1)
WHEN 0 THEN #value
ELSE SUBSTRING(#value, 1, CHARINDEX(' ', #value, 1) - 1)
END
END
GO
SELECT dbo.GetWord(NULL, 1)
SELECT dbo.GetWord('', 1)
SELECT dbo.GetWord('abc', 1)
SELECT dbo.GetWord('abc def', 4)
SELECT dbo.GetWord('abc def ghi', 20)
Try This:
Select race_id, race_description
, Case patIndex ('%[ /-]%', LTrim (race_description))
When 0 Then LTrim (race_description)
Else substring (LTrim (race_description), 1, patIndex ('%[ /-]%', LTrim (race_description)) - 1)
End race_abbreviation
from tbl_races