Replace non-numeric characters with numbers - sql

I have a column that looks like:
Column A
1A
2B
5Z
Essentially, I need to replace the letter part with the correspending number in the alphabet. LIke:
Column A
1.1
2.2
5.26
I was thinking of creating a "lookup" table to retrieve the correspnding letter number but wanted to see if there is a more elegant/efficient way?
Many thanks

Just give you an algorithm to convert character to number and let parsing characters within a column up to you:
DECLARE #offset int
SET #offset = 64
DECLARE #character char
-- OUTPUT | A | 1 |
SET #character = 'A'
SELECT #character, ASCII(#character) - #offset
-- OUTPUT | Z | 26 |
SET #character = 'Z'
SELECT #character, ASCII(#character) - #offset

Related

Convert Negative Varchar, Null to Decimal in SQL Server

I am trying to convert one of my measure column values having below from varchar to decimal(20,3). The 3rd value is a null value without any space and to make you understand I enclosed with quotes. I have tried with CAST but CAST is not able to change datatype as having negative sign. But I want to keep negative value.
I tried using TRY_CONVERT and TRY_CAST but that is not giving desired result. If anyone can help will be really helpful. I have tried below
CAST(COALESCE((NULLIF(SalesColumn,'')),'0') AS NUMERIC(20,12))
-992.0
0
I use SQL Server 2017 and CAST works just fine and maintains the "-"-sign, below is an example. Beware of the rounding, you might need to use ROUND() before converting it to decimal(20,3) by first converting it to a decimal with higher precision, using ROUND() and then casting it to decimal(20,3).
Using CAST to numeric or decimal works just fine on NULL as well.
DECLARE
#Val1 VARCHAR(10) = '-992.123'
,#Val2 VARCHAR(10) = '-45678'
,#Val3 VARCHAR(10) = NULL
,#Val4 VARCHAR(10) = ''
,#Val5 VARCHAR(10) = '321'
,#Val6 VARCHAR(10) = ' '
SELECT ISNULL(CAST(NULLIF(A, '') as decimal(20,3)), 0)
FROM (
VALUES (#Val1), (#Val2), (#Val3), (#Val4), (#Val5), (#Val6)
) Sub (A)
Which gives the result..
---------------------------------------
-992.123
-45678.000
0.000
0.000
321.000
0.000
(6 rows affected)
You can add another coalesce() within nullif() to handle null value. Please look into below examples.
declare #var as varchar(50);
set #var=null;
select cast(COALESCE((NULLIF(coalesce(trim(#var),0),'')),'0') AS NUMERIC(20,12))
GO
| (No column name) |
| ---------------: |
| 0.000000000000 |
declare #var as varchar(50);
set #var=' ';
select cast(COALESCE((NULLIF(coalesce(trim(#var),0),'')),'0') AS NUMERIC(20,12))
GO
| (No column name) |
| ---------------: |
| 0.000000000000 |
declare #var as varchar(50);
set #var='- 992 ';
select cast(COALESCE((NULLIF(coalesce(trim(#var),0),'')),'0') AS NUMERIC(20,12))
GO
| (No column name) |
| ----------------: |
| -992.000000000000 |
db<>fiddle here

SQL change word command

How to change the location of the letters 5 and 6 in sql. For example:
word:weather new word:weatehr
You can try to use SUBSTRING with LEN function to make it.
CREATE TABLE T(
col varchar(40)
);
INSERT INTO T VALUES ('weather')
Query 1:
SELECT
col 'word' ,CONCAT(SUBSTRING(col,1,4),SUBSTRING(col,6,1),SUBSTRING(col,5,1),SUBSTRING(col,7,LEN(col) - 5)) 'new word'
FROM T
Results:
| word | new word |
|---------|----------|
| weather | weatehr |
declare #s varchar(32) = 'weather';
set #s = stuff(#s, 5, 2, reverse(substring(#s, 5, 2)));
Since you're just reversing adjacent characters it's a little easier than swapping arbitrary characters.

Unique 8 digit incremental number

I'm trying to generate a number which will ultimately be stored as string(varchar). e.g.
First - ABC00000001
Second- ABC00000002
.........................
I am able to generate character string as expected. Now the problem is,incremental number.
What i am trying to do is get the last number stored e.g. ABC00000009 and generate the next number that is ABC00000010. How to do the same?
If i extract integers from this than i will get 1 or 10,how to make it according to 8 digit format.
Any help would really be appreciated.
Of course if changing the table structure is not an option, you can try this:
DECLARE #lastValue VARCHAR(15) = 'ABC00000001'
SELECT CONCAT('ABC', RIGHT(100000000 + CAST(RIGHT(#lastValue, 8) AS INT) + 1, 8))
Result
-----------
ABC00000002
I would suggest that you create an identity column. This will increment (usually by 1, but not always). Then create a computed column:
alter table t add generated_number as
('ABC' + right(replicate('0', 8) + cast(idcol as varchar(255)), 8));
Almost the same approach Gordon Linoff has taken, I just prefer to use math where possible instead of string concatenation. My answer is different only because I add id value to 100000000 instead of using replicate.
CREATE TABLE dbo.test (
id int IDENTITY(1, 1) PRIMARY KEY
, some_value sysname UNIQUE
, super_column AS 'ABC' + RIGHT(100000000 + id, 8));
GO
INSERT INTO dbo.test (some_value)
VALUES ('some_value_1'), ('some_value_2');
SELECT *
FROM dbo.test AS T;
Result:
+----+--------------+--------------+
| id | some_value | super_column |
+----+--------------+--------------+
| 1 | some_value_1 | ABC00000001 |
| 2 | some_value_2 | ABC00000002 |
+----+--------------+--------------+

create sql view from comma separated values

T-sql question:
I need help to build a join from 2 tables, where on one of the tables I have aggregated data (comma separated values).
I have a table - Users where I have 3 columns: UserId, DefaultLanguage and OtherLanguages.
The table looks like this:
UserId | DefaultLanguage | OtherLanguages
---------------------------------------------
1 | en | NULL
2 | en | it, fr
3 | fr | en, it
4 | en | sp
and so on.
I have another table where I have the association between language code (en, fr, ro, it, sp) and language name:
LangCode | LanguageName
-------------------------
en | English
fr | French
it | Italian
sp | Spanish
and so on.
I want to create a view like this:
UserId | DefaultLanguage | OtherLanguages
---------------------------------------------
1 | English | NULL
2 | English | Italian, French
3 | French | English, Italian
4 | English | Spanish
and so on.
In short, I need a view where the language code is replaced by language name.
Any help, please?
Several solutions of course you can recreate all table change the data structure.
1. If all the language are 2 digits:
select t1.UserId, t2.LanguageName,
ISNULL( t3.LanguageName, '') + ISNULL(', '+t4.LanguageName, '') + ISNULL( ', '+t5.LanguageName, '') OtherLanguages
from Table1 t1
inner join Table2 t2 on t1.DefaultLanguage = t2.LangCode
left join Table2 t3 on Left(t1.OtherLanguages,2) = t3.LangCode
left join Table2 t4 on CASE WHEN len(Replace(t1.OtherLanguages, ' ', '')) > 3 THEN
SUBSTRING( Replace(t1.OtherLanguages, ' ', ''), 4, 2) ELSE null END = t4.LangCode
left join Table2 t5 on CASE WHEN len(Replace(t1.OtherLanguages, ' ', '')) > 6 THEN
SUBSTRING( Replace(t1.OtherLanguages, ' ', ''), 7, 2) ELSE null END = t5.LangCode
Use user-define function:
CREATE FUNCTION [dbo].[func_GetLanguageName] (#pLanguageList varchar(max))
RETURNS varchar(max) AS
BEGIN
Declare #aLanguageList varchar(max) = #pLanguageList
Declare #aLangCode varchar(max) = null
Declare #aReturnName varchar(max) = null
WHILE LEN(#aLanguageList) > 0
BEGIN
IF PATINDEX('%,%',#aLanguageList) > 0
BEGIN
SET #aLangCode = RTRIM(LTRIM(SUBSTRING(#aLanguageList, 0, PATINDEX('%,%',#aLanguageList))))
SET #aLanguageList = LTRIM(SUBSTRING(#aLanguageList, LEN(#aLangCode + ',') + 1,LEN(#aLanguageList)))
END
ELSE
BEGIN
SET #aLangCode = #aLanguageList
SET #aLanguageList = NULL
END
Select #aReturnName = ISNULL( #aReturnName + ', ' , '') + LanguageName from Table2 where LangCode=#aLangCode
END
RETURN(#aReturnName)
END
and use select
select UserId, dbo.func_GetLanguageName(DefaultLanguage)DefaultLanguage, dbo.func_GetLanguageName(OtherLanguages) OtherLanguages from table1
Best practice would dictate not to have this type of comma delimited
data in a column...
Since you stated in comments that the schema cannot be changed, the next best thing is a function. This can be used in a select query in-line.
SQL is notoriously slow with string manipulation. Here is an interesting article on the topic. There are many SQL "string split" functions out there. They all generally split a comma delimited string and return a table.
For this specific use-case, you actually need a scalar-valued
function (a function which returns one value) rather than a
table-valued function (one which returns a table of values).
Below is a modified such function, which returns a scalar value in place of the original comma delimited string of language codes.
The comments explain what is happening line by line.
The gist is that you must loop through the input string keeping track of the last comma location, extract each code, lookup the full language from the languages table, and then return the output as a comma-delimited string.
Language codes to languages function:
Create Function [dbo].fn_languageCodeToFull
( #Input Varchar(100) )
Returns Varchar(1000)
As
Begin
-- To address null input, based on the example you provided, we set the output to NULL if there is no input
If #Input = '' Or #Input Is Null
Return Null
Declare
#CodeLength int, -- constant for code length to avoid hardcoded "magic numbers"
#Output varchar(1000), -- will contain the final comma delimited string of full languages
#LastIndex int, -- tracks the location of the input we are searching as we loop over the string
#CurrentCode varchar(2), -- for code readability, we extract each language code to this variable
#CurrentLanguage varchar(50), -- for code readability, we store the full language in this variable
#IndexIncrement int -- constant to increment the search index by 1 at each iteration
-- ensuring the loop moves forward
Set #LastIndex = 0 -- seed the index, so we begin to search at 0 index
Set #CodeLength = 2 -- ISO language codes are always 2 characters in length
Set #Output = '' -- seed with empty string to avoid NULL when concatenating
Set #IndexIncrement = 1 -- again avoiding hardcoded values...
-- We will loop until we have gone to or beyond the length of the input string
While #LastIndex < len(#Input)
Begin
-- Set the index of each comma (charindex is 1-based)
Set #LastIndex = CHARINDEX(',', #Input, #LastIndex)
-- When we get to the last item, CharIndex will return 0 when it does not find a comma.
-- To pull the last item, we will artificially set #LastIndex to be 1 greater than the input string
-- This will allow the code following this line to be unaltered for this scenario
If #LastIndex = 0 set #LastIndex = len(#Input) + 1 -- account for 1-based index of substring
-- Extract the code prior to the current comma that charindex has identified
Set #CurrentCode = substring(#Input, #LastIndex - #CodeLength, #CodeLength)
-- Do a lookup to get the language for the current code
Set #CurrentLanguage = (Select LanguageName From languages Where code = #CurrentCode)
-- Only add comma after first language to ensure no extra comma will be present in Output
If #LastIndex > 3 Set #Output = #Output + ','
-- Here we build the Output string with the language
Set #Output = #Output + #CurrentLanguage
-- Finally, we increment #LastIndex by 1 to avoid loop on first instance of comma
Set #LastIndex = #LastIndex + #IndexIncrement
End
Return #Output
End
Then your view would simply do something like:
Sample view using the function:
Create View vw_UserLanguages
As
Select
UserId,
dbo.fn_languageCodeToFull(DefaultLanguage) as DefaultLanguage,
dbo.fn_languageCodeToFull(OtherLanguages) as OtherLanguages,
From UserLanguageCodes -- you do not provide a name so I made one up
Note that the function will work whether there are commas or not, so there is no need to join the Languages table here as you can just have the function do all the work in this case.
One quick and dirty solution would be to use a nested REPLACE command but that could result in a very complex statement a bit long winded, especially if you have more than five languages.
As an example:
SELECT [UserId],[DefaultLanguage],
CASE
WHEN [OtherLanguages] IS NULL THEN ''
ELSE REPLACE(
REPLACE(
REPLACE(
REPLACE(
REPLACE([OtherLanguages],
'en','English'),
'fr','French'),
'it','Italian'),
'ro','Romulan'), --Probably not the intended language ;-)
'sp','Spanish')
END as [OtherLanguages]
FROM YourTable
Personally, I'd create a scalar function, again using the REPLACE command, but you can then check the number of languages present and add a counter so that you're not doing unnecessary lookups.
SELECT [UserId],[DefaultLanguage],
CASE
WHEN [OtherLanguages] IS NULL THEN ''
WHEN [OtherLanguages] = '' THEN ''
ELSE do_function_name([OtherLanguages])
END as [OtherLanguages]
FROM YourTable
It might not be good practice but there are times when it is more efficient to store multiple values in a single field but accept that when you do, it will slow down the way you handle that data.

SQL search for complex string within a row

Ok so I have a database row with a specified string in for example i am here.
I want to know how I could match this row (in a T-SQL query) if for example my input was hello i am here in this bright room.
To be clearer and get a better answer hopefully, here is a rough example:
Table:
1 | i am there |
2 | i am here |
3 | i am not here |
Problem:
I have the input hello i am here in this bright room - this should return a match to row 2 above only as only row 2 contains i am here definitively whilst the others contain the characters for i am here but with subtle differences.
If anyone can help it would be much appreciated. I would like to do this all in SQL so I can create a stored procedure for the above.
DECLARE #InputString VARCHAR(100);
SET #InputString = 'hello i am here in this bright room';
SELECT *
FROM YourTable
WHERE CHARINDEX(YourColumn, #InputString) <> 0;
declare #input as varchar
set #input = 'hello i am here in this bright room'
select *
from MyTable
where #input like '%' + MyCol + '%'