How to fix error with negative value being passed to LEFT() - sql

The following is designed to extract the first chars from a string. In this case extracting the area from a postcode. As the area may be variable length (1 or 2 chars) the rule is get the first alpha characters, before the numbers in the first half of the string (delimited by a space).
If the string is formatted correctly, the following works. However, if the string is malformed (as demonstrated below) the PATINDEX returns 0, which in turn results in -1 being passed back to the parent LEFT().
How can I trap the negative value and substitute the -1 with a 0?
Malformations could take the format:
'ABO 12BT', 'AB012BT' etc.
DECLARE #Postcode nVARCHAR (10) = 'ab124th'
(
SELECT
RTRIM
(
LEFT
(
LEFT
(
#Postcode, CHARINDEX(' ', #Postcode)
),
(
PATINDEX
(
'%[0-9]%',
LEFT
(
#Postcode,
CHARINDEX(' ', #Postcode)
)
) -1
)
)
)
)

isnull(nullif(patnidex(...) - 1, -1), 0)

Related

Remove items in a delimited list that are non numeric in SQL for Redshift

I am working with a field called codes that is a delimited list of values, separated by commas. Within each item there is a title ending in a colon and then a code number following the colon. I want a list of only the code numbers after each colon.
Example Value:
name-form-na-stage0:3278648990379886572,rules-na-unwanted-sdfle2:6886328308933282817,us-disdg-order-stage1:1273671130817907765
Desired Output:
3278648990379886572,6886328308933282817,1273671130817907765
The title does always start with a letter and the end with a colon so I can see how REGEXP_REPLACE might work to replace any string between starting with a letter and ending with a colon with '' might work but I am not good at REGEXP_REPLACE patterns. Chat GPT is down fml.
Side note, if anyone knows of a good guide for understanding pattern notation for regular expressions it would be much appreciated!
I tried this and it is not working REGEXP_REPLACE(REPLACE(REPLACE(codes,':', ' '), ',', ' ') ,' [^0-9]+ ', ' ')
This solution assumes a few things:
No colons anywhere else except immediately before the numbers
No number at the very start
At a high level, this query finds how many colons there are, splits the entire string into that many parts, and then only keeps the number up to the comma immediately after the number, and then aggregates the numbers into a comma-delimited list.
Assuming a table like this:
create temp table tbl_string (id int, strval varchar(1000));
insert into tbl_string
values
(1, 'name-form-na-stage0:3278648990379886572,rules-na-unwanted-sdfle2:6886328308933282817,us-disdg-order-stage1:1273671130817907765');
with recursive cte_num_of_delims AS (
select max(regexp_count(strval, ':')) AS num_of_delims
from tbl_string
), cte_nums(nums) AS (
select 1 as nums
union all
select nums + 1
from cte_nums
where nums <= (select num_of_delims from cte_num_of_delims)
), cte_strings_nums_combined as (
select id,
strval,
nums as index
from cte_nums
cross join tbl_string
), prefinal as (
select *,
split_part(strval, ':', index) as parsed_vals
from cte_strings_nums_combined
where parsed_vals != ''
and index != 1
), final as (
select *,
case
when charindex(',', parsed_vals) = 0
then parsed_vals
else left(parsed_vals, charindex(',', parsed_vals) - 1)
end as final_vals
from prefinal
)
select listagg(final_vals, ',')
from final

Update or replace a special character text in my database field

I am facing to an issue that I cannot update or replace some characters in my database.
Here is how this text look like in my column when I retrieve it:
As you can see, there is an unknown characters between 'master' and 'degree' which I cannot even paste it here.
I also tried to update and replace it with below code (I cannot paste that two vertical lines here since they are not supported in any browser and I am not sure what they are, Please see the picture above to see what is in my SQL statement).
begin transaction
update gm_desc set projdesc=replace(projdesc,'%â s%','') where projdesc like '%âs%' and proposalno = '15-149-01'
You can see the real SQL Statement here:
I tried to update, or replace it but I cannot do it. The update statement successfully works but I still see that weird special charters. I would be appreciate to help me.
Here's a scalar-valued function which removes all non-alphanumeric characters (preserves spaces) from a string.
Hopefully it helps!
dbfiddle
create function dbo.get_alphanumeric_str
(
#string varchar(max)
)
returns varchar(max)
as
begin
declare #ret varchar(max);
with nums as (
select 1 as n
union all select n+1 from nums
where n < 256
)
select #ret = replace(stuff(
(
select '' + substring(#string, nums.n, 1)
from nums
where patindex('%[^0-9A-Za-z ]%', substring(#string, nums.n,1)) = 0
for xml path('')
), 1, 0, ''
), ' ', ' ')
option (MAXRECURSION 256)
return #ret;
end
Usage
select dbo.get_alphanumeric_str('Helloᶄ âWorld 1234⅊⅐')
Returns: Hello World 1234
How it works
The nums CTE is just to get a list of numbers (you can set the maximum of 256 to a higher value if your strings are longer; n.b. option (MAXRECURSION n) is for this CTE but has to be placed at the query)
The stuff essentially iterates through the string, using the list of numbers above and extracts a substring of length 1; each of these chars are checked if they match the [^0-9A-Za-z ] regex group (0-9 all digits, A-Za-z all letters both lower and upper case, and a single space character)
If they match, patindex() should return 0; i.e. index zero.
Use replace(string, ' ', ' ') for the space character as the xml path returns a special encoding, see this question.
Use a binary collation for accented characters; see this answer

Remove white spaces from string in sql

SELECT CONVERT(DECIMAL(18,2)
,ROUND( REPLACE
(
REPLACE(
SUBSTRING([ColumnName],0, CHARINDEX(' ',[ColumnName],1) )
,'$',''
)
,',',''
)
,2
)
) AS 'ColumnName'
,[ColumnName]
,*
FROM TABLENAME
The CHARINDEX returns index of space, but when there is no space in data it returns 0. What I want is when ever there is a white space at the end data, SUBSTRING should consider that and when there is no white space then it should consider the length of the string.
It seems to you are working with SQL Server, then i would like to apply case expression
case when CHARINDEX(' ',[ColumnName],1) > 0
then CHARINDEX(' ',[ColumnName],1)
else len([ColumnName]) end
Apparently you're working with sql-server and want to convert a string like $123,456,789.99 to a DECIMAL.
TRY_PARSE should get you the result you want:
SELECT
TRY_PARSE(REPLACE('$123,456,789.99 ', '$', '')
AS DECIMAL(18,2)
USING 'en-US') AS myDecimalNumber
Or if it's an option for you to work with the MONEY data-type you can omit the REPLACE:
SELECT
TRY_PARSE('$123,456,789.99 '
AS MONEY
USING 'en-US')) AS myDecimalNumber

Hide characters in email address using an SQL query

I have a very specific business requirement to hide certain characters within an email address when returned from SQL and I have hit the limit of my ability with SQL to achieve this. I was wondering if someone out there would be able to point me in the right direction. Essentially, my business is asking for the following:
test#email.com to become t*\*t#e**l.com
or
thislong#emailaddress.com to become t******h#e**********s.com
I am aware that if either portion of the email before of after the # are less than 3 characters, then this won't work, but I intend on checking for this and dealing with it appropriately. I have tried a mixture of SUBSTRING, STUFF, LEFT/RIGHT etc but I can't quite get it right.
DECLARE #String VARCHAR(100) = 'example#gmail.com'
SELECT LEFT(#String, 1) + '*****#'
+ REVERSE(LEFT(RIGHT(REVERSE(#String) , CHARINDEX('#', #String) +1), 1))
+ '******'
+ RIGHT(#String, 5)
result will be
e******e#g***l.com
Very interesting and very much tough to generate generic solution try this
this may help you
DECLARE #String VARCHAR(100) = 'sample#gmail.com'
SELECT STUFF(STUFF(#STring,
CHARINDEX('#',#String)+2,
(CHARINDEX('.',#String, CHARINDEX('#',#String))-CHARINDEX('#',#String)-3),
REPLICATE('*',CHARINDEX('.',#String, CHARINDEX('#',#String))-CHARINDEX('#',#String)))
,2
,CHARINDEX('#',#String)-3
,REPLICATE('*',CHARINDEX('#',#String)-3))
OUTPUT will be
s****e#g******l.com
Similar way for thislong#emailaddress.com
OUTPUT will be
t******g#e*************s.com
You could also use regular expressions. Something like this (not completely finished):
select regexp_replace('test#email.com', '^(.?).*#(.?).*', '\1***#\2***')
from dual
Results in:
t***#e***
Could be a useful solution if you can only use SELECT statements.
CREATE FUNCTION dbo.EmailObfuscate
( #Email VARCHAR(255)
) RETURNS TABLE AS RETURN
(
SELECT adr.email
, LEFT (adr.email, 1)
+ REPLICATE ('*', AtPos-3)
+ SUBSTRING (adr.Email, AtPos-1, 3)
+ REPLICATE ('*', Length-DotPos - AtPos - 2)
+ SUBSTRING (adr.Email, Length - DotPos, 10) AS hide
FROM ( VALUES ( #Email) ) AS ADR (EMail)
CROSS APPLY ( SELECT CHARINDEX ('#', adr.Email)
, CHARINDEX ('.', REVERSE(Adr.Email))
, LEN (Adr.Email)
) positions ( AtPos
, dotpos
, Length
)
);
GO
-- Calling
SELECT Emails.*
FROM ( Values ( 'this.long#emailaddress.com')
, ( 'test#email.com' )
, ( 'sample#gmail.com' )
, ( 'test#gmx.de' )
) AS adr (email)
CROSS APPLY dbo.EmailObfuscate (Adr.Email) AS Emails
SELECT
-- Email here is the name of the selected Column from your Table
--Display the First Character
SUBSTRING(Email,1,1)+
--Replace selected Number of *
REPLICATE('*',10)+
--Display the One Character before # along with # & One Character after #
SUBSTRING(Email,CHARINDEX('#',Email)-1,3)+
--Replace selected Number of *
REPLICATE('*',10)+
--Display. Character along with the rest selected Number of Characters
SUBSTRING(Email,CHARINDEX('.',Email)-1, LEN(Email) -
CHARINDEX('.',Email)+12)
--NameEmail is the Table Name
FROM NameEmail
Result is:
j**********l#a**********a.co.uk
I've found this answer, but was searching for PostgreSQL version (and have not found it)
So I've made my own for PGSQL
SELECT
-- Email here is the name of the selected Column from your Table
--Display the First Character
SUBSTRING('g40gj30m#my-email.co.uk', 1, 1) ||
--Replace selected Number of *
REPEAT('*', 10) ||
--Display the One Character before # along with # & One Character after #
SUBSTRING('g40gj30m#my-email.co.uk', strpos('g40gj30m#my-email.co.uk', '#') - 1, 3) ||
--Replace selected Number of *
REPEAT('*', 10) ||
--Display. Character along with the rest selected Number of Characters
SUBSTRING(
'g40gj30m#my-email.co.uk',
strpos('g40gj30m#my-email.co.uk', '.') - 1,
length('g40gj30m#my-email.co.uk') - strpos('g40gj30m#my-email.co.uk', '.') + 12
);
the result will be
?column?
---------------------------------
g**********m#m**********l.co.uk
(1 row)

need to replace some underscores with hypens

I have a column with value
AAA_ZZZZ_7890_10_28_2014_123456.jpg
I need to replace the middle underscores so that it displays it as date i.e.
AAA_ZZZZ_7890_10-28-2014_123456.jpg
Can some one please suggest a simple update query for this.
The Number of Underscores would be same for all the values in the column but the length will vary for example some can have
AAA_q10WRQ_001_10_28_2014_12.jpg
The following should do it:
http://sqlfiddle.com/#!3/d41d8/30384/0
declare #filename varchar(64) = 'AAA_ZZZZ_7890_10_28_2014_123456.jpg'
declare #datepattern varchar(64) = '%[_][0-1][0-9][_][0-3][0-9][_][1-2][0-9][0-9][0-9][_]%'
select
filename,
substring(filename,1,datepos+2)+'-'+
substring(filename,datepos+4,2)+'-'+
substring(filename,datepos+7,1000)
from
(
select
#filename filename,
patindex(#datepattern,#filename)
as datepos
) t
;
Resulting in
AAA_ZZZZ_7890_10-28-2014_123456.jpg
Caveats to watch out for:
It is important to exactly define how you find the date. In my definition it is MM_DD_YYYY surrounded by further two underscores, and I check that the first digits of M,D,Y are 0-1,0-3,1-2 respectively (i.e. I do NOT check if month is e.g. 13.) -- of course we assume that there is only one such string in any file name.
datepos actually finds the position of the underscore before the date -- this is not an issue if taken into account in the indexing of substring.
in the 3rd substring the length cannot be NULL or infinity and I couldn't get LEN() to work in SQL Fiddle so I dirty hardcoded a large enough number (1000). Corrections to this are welcome.
Try this (assuming that the DATE portion always starts at the same character index)
declare #string varchar(64) = 'AAA_ZZZZ_7890_10_28_2014_123456.jpg'
select replace(#string, reverse(substring(reverse(#string), charindex('_', reverse(#string), 0) + 1, 10)), replace(reverse(substring(reverse(#string), charindex('_', reverse(#string), 0) + 1, 10)), '_', '-'))
If there are exactly 6 _ then for the first
select STUFF ( 'AAA_ZZZZ_7890_10_28_2014_123456.jpg' , CHARINDEX ( '_' ,'AAA_ZZZZ_7890_10_28_2014_123456.jpg', CHARINDEX ( '_' ,'AAA_ZZZZ_7890_10_28_2014_123456.jpg', CHARINDEX ( '_' ,'AAA_ZZZZ_7890_10_28_2014_123456.jpg', CHARINDEX ( '_' ,'AAA_ZZZZ_7890_10_28_2014_123456.jpg', 0 ) + 1 ) + 1 ) + 1 ) , 1 , '-' )