Remove everything after a specific special character in SQL Server - sql

This Question is pretty much answered in this forum but for my situation which is exactly the same the answers I find here are not working.
I'm trying to remove all the characters after the pipe " | " in sql server. Any suggestions why it's not working?
query that i'm using:
SELECT left(name, charindex('|',name) -1) from table1
Error:
Invalid length parameter passed to the LEFT or SUBSTRING function.
table1:
name
----
jack| rose|
wil |jo|rdy
karl||jay
jo smith
jjill
raj |kumar
Desired output:
name
----
jack
wil
karl
jo smith
jjill
raj

try:
select LEFT(name,CHARINDEX('|',name + '|')-1)
from table1

Think you're almost there - you just need to filter to ensure you aren't passing a negative number to the LEFT
e.g.,
SELECT left(name, charindex('|',name) -1) from table1
WHERE name LIKE '%|%'
Remember charindex is 0 if it doesn't exist in string, then you subtract 1. This means you're trying to do a LEFT(name, -1) which is invalid.
To get your full output (with all rows, regardless of the | symbol) you can use it in a CASE instead.
SELECT CASE
WHEN [name] LIKE '%|%' THEN left(name, charindex('|',name) -1)
ELSE [name] END AS [name]
from table1
Edit: Here's a db<>fiddle with results.

You can try the below -
SELECT left(name, case when charindex('|',name)=0 then len(name)
else charindex('|',name)-1 end),charindex('|',name)
from tablename

based on your query, its expecting values to have | always. So this will error on name = jo smith'. Use select case to solve this.
declare #str varchar(200)
set #str = 'jo smith'
select case when charindex('|',#str) > 0 then left(#str, charindex('|',#str) -1) else #str end

Instead of using LEFT, use a combination of STUFF and ISNULL.
While LEFT will raise the "Invalid length parameter passed to the left function." error if it gets a negative length parameter,
STUFF will simply return null if it's starting position argument is less than less than 1.
This means that if the original value doesn't contain | and charindex return 0, stuff will simply return null, and you can use ISNULL to return the original value in this case.
First, create and populate sample table (Please save us this step in your future questions):
DECLARE #T TABLE (
[name] varchar(20)
);
INSERT INTO #T ([name]) VALUES
('jack| rose|'),
('wil |jo|rdy'),
('karl||jay'),
('jo smith'),
('jjill'),
('raj |kumar');
The query:
SELECT [name],
ISNULL(STUFF([name], CHARINDEX('|', [name]), LEN([name]), ''), [name]) As Replaced
FROM #T
Results:
name Replaced
jack| rose| jack
wil |jo|rdy wil
karl||jay karl
jo smith jo smith
jjill jjill
raj |kumar raj

Related

extract sub-string in T-SQL based on condition

I have address in sql column which also contain postcode i.e. as following format
10 westminister way Road, London (NW10 5NQ)
but not all record may have postcode which in case will be like
10 westminister way Road, London
I need to extract post from string which is working fine except following character throw error if postcode doesn't exist which i believe i need to use contain but not sure how to modify existing code to do so
select
,REVERSE(SUBSTRING(REVERSE([address]),2, CHARINDEX('(', REVERSE([address]))-2)) PostCode
,CHARINDEX('(', REVERSE([address]))-2 indexDetail
my question is how to use contain or if condition so
if(CHARINDEX('(', REVERSE([address])) = true then proceed with substring
else ignore record
Using IIF. This will grab everything in the parentheses, regardless of how long the postal code is. I'm not sure if they vary in the UK.
declare #table table ([address] varchar(256))
insert into #table
values
('10 westminister way Road, London (NW10 5NQ)'),
('10 westminister way Road, London')
select
[address],
PostCode = iif(charindex('(',[address]) > 1, substring([address],charindex('(',[address]),charindex(')',[address])),''),
PostalNoParan = iif(charindex('(',[address]) > 1, replace(replace(substring([address],charindex('(',[address]),charindex(')',[address])),'(',''),')',''),'')
from #table
SELECT
[address],
PostalCode = SUBSTRING([address], NULLIF(charindex('(',[address]),0), 256)
FROM #table;
If you need an empty string instead of NULL:
SELECT
[address],
PostalCode = COALESCE(SUBSTRING([address], NULLIF(charindex('(',[address]),0), 256),'')
FROM #table;
How about this?
select (case when address like '% (%)'
then left(right(address, 9), 8)
end) as postcode

SQL statement to retrieve specific string

I'm trying to extract a number from a Notes column. I only want the number if it is preceded by a specific string of characters. The number can be 5 or 6 characters long, and may eventually go up to 7 or more in the future.
What combination of LEFT, RIGHT, CHARINDEX, or SUBSTRING do I need to utilize to accomplish this? or do I need to use something else altogether? I haven't been able to figure anything out that works.
The Notes column contains lots of different kinds of notes as well, so that's been making it difficult.
Thanks in advance.
EDIT: Sorry, here's some sample data and expected output.
EDIT2: Sorry again, I should've made the sample data a bit more clear. There are multiple numbers, but I only want the numbers that are preceded by 'Account #'.
Sample Data Output
'Account #12345' 12345
'Account #123456' 123456
'Random #12345' NULL
'Account #1234567' 1234567
'12345' NULL
'Random' NULL
This should do it.
SELECT YT.[Sample Data],
S2.[Output]
FROM YourTable YT
CROSS APPLY (SELECT 'Account #') S(S)
CROSS APPLY (SELECT CASE
WHEN CHARINDEX(S.S, [Sample Data]) > 0
THEN SUBSTRING([Sample Data], LEN(S.S) + CHARINDEX(S.S, [Sample Data]), 8000)
END) S1(StringWithNumberAtStart)
CROSS APPLY (SELECT LEFT(StringWithNumberAtStart,
PATINDEX('%[^0-9]%', StringWithNumberAtStart + 'X'))) S2([Output])
You might try this:
DECLARE #dummyTbl TABLE(YourString VARCHAR(100));
INSERT INTO #dummyTbl VALUES
('Account #12345')
,('Account #123456')
,('Random # note')
,('Account #1234567');
WITH Casted AS
(
SELECT YourString
,CAST('<x>' + REPLACE(YourString,' #','</x><x>') + '</x>' AS XML) toXML
FROM #dummyTbl
)
SELECT YourString
,toXML
,toXML.value('x[1]','nvarchar(max)') AS KeyText
,toXML.value('x[2] cast as xs:int?','int') AS NumberIfCastable
FROM Casted;
The result
YourString toXML KeyText NumberIfCastable
Account #12345 <x>Account</x><x>12345</x> Account 12345
Account #123456 <x>Account</x><x>123456</x> Account 123456
Random # note <x>Random</x><x> note</x> Random NULL
Account #1234567 <x>Account</x><x>1234567</x> Account 1234567
You can see, that I used a trick with CAST and REPLACE to transform your string to an XML which allows to address each part separately. The XPath to the first element x[1] returns Account or Random while the second x[2] returns the number.
A second trick I use is XQuerys implicit ability to try_cast a value. If the value cannot be converted to an xs:int? it will be returned as a NULL.
This ensures 5 digits after 'Account #' and ask for isnumeric when longer. This is not 100% your request but is an approach. Isnumeric function works in a special way
https://learn.microsoft.com/en-us/sql/t-sql/functions/isnumeric-transact-sql
--len('Account #') is 9
select case when Notes like 'Account #[0-9][0-9][0-9][0-9][0-9]%'
and isnumeric(right(Notes,len(Notes)-9) )=1 then right(Notes,len(Notes)-9)
else null end
from myTable
For SQLServer 2012+ use try_parse
https://learn.microsoft.com/en-us/sql/t-sql/functions/try-parse-transact-sql
select case when Notes like 'Account #[0-9][0-9][0-9][0-9][0-9]%'
and isnumeric(try_parse(right(Notes,len(Notes)-9) as bigint ))=1
then right(Notes,len(Notes)-9) else null end
from myTable
SQLFIDDLE http://sqlfiddle.com/#!6/cd617/2
I would use substring() with patindex() function to get the numeric values only
SELECT sampledata, SUBSTRING(sampledata, PATINDEX('%[1-9]%', SampleData),
case
when(LEN(sampledata)-PATINDEX('%[1-9]%', SampleData)+1) > LEN(SampleData)
then 0
else LEN(sampledata)-PATINDEX('%[1-9]%', SampleData)+1
end) numericvalues
FROM table;
EDIT:
Changing the question after you have asked it is rude. It can invalidate answers which in turn can attract down votes.
Well, i updated the answer according to the edited post.
select sampledata,
case
when patindex('%Account #%', sampledata) > 0
then SUBSTRING(sampledata, PATINDEX('%[1-9]%', sampledata), LEN(sampledata)-PATINDEX('%[1-9]%', sampledata)+1)
end [Output]
from table
Result :
Sample Data Output
'Account #12345' 12345
'Account #123456' 123456
'Random #12345' NULL
'Account #1234567' 1234567
'12345' NULL
'Random' NULL

Unexpected execution in an update query in SQL

I am getting an 'Unexpected' result with an update query in SQL Server 2012.
This is what I am trying to do.
From a column (IDENTIFIER) composed by an ID ','name (e.g. 258967,Sarah Jones), I have to fill other two columns: ID and SELLER_NAME.
The original column has some values with a blank at the end and the rest with out it:
'258967,Sarah Jones'
'98745,Richard James '
This is the update query that I am executing:
UPDATE SELLER
SET
IDENTIFIER = LTRIM(RTRIM(IDENTIFIER)),
ID = Left(IDENTIFIER , charindex(',', IDENTIFIER )-1),
SELLER_NAME = UPPER(RIGHT((IDENTIFIER ),LEN(IDENTIFIER )-CHARINDEX(',',IDENTIFIER )));
But I am having a wrong result at the end
258967,Sarah Jones 258967 SARAH JONES
98745,Richard James 98745 ICHARD JAMES
The same happens with all the names that has the blank at the end. At this point I wonder, if I have specified that I want to eliminate all the blanks at the begining and at the end of the value of IDENTIFIER as a first action, why the system updates the ID and SELLER_NAMES and then does this action?.
Just to specify: The IDENTIFIER column is part of the seller table which is updating from another person that imports the data from an Excel file. I receive this values and I have to normalize the information. I only can read the SELLER table, take this into account before answer
Try this, because you have space in right side of name, so it will just truncate one char from name. So just need to RTRIM(IDENTIFIER) and thats it.
SELLER_NAME = UPPER(RIGHT((RTRIM(IDENTIFIER)),LEN(IDENTIFIER )-CHARINDEX(',',IDENTIFIER)));
The design of your tables violates 1NF and is nothing but painful. Instead of doing all this crazy string manipulation you could leverage PARSENAME here quite easily.
with Something(SomeValue) as
(
select '258967,Sarah Jones' union all
select '98745,Richard James '
)
select *
, ltrim(rtrim(PARSENAME(replace(SomeValue, ',', '.'), 2)))
, ltrim(rtrim(PARSENAME(replace(SomeValue, ',', '.'), 1)))
from Something
Instead of using Right(), use SubString().
Here's an example. I've tried to show each step individually to illustrate
; WITH x (identifier) AS (
SELECT '258967,Sarah Jones'
UNION ALL
SELECT '98745,Richar James '
)
SELECT identifier
, CharIndex(',', identifier) As comma
, SubString(identifier, CharIndex(',', identifier) + 1, 1000) As name_only
, LTrim(RTrim(SubString(identifier, CharIndex(',', identifier) + 1, 1000))) As trimmed_name_only
FROM x
Note that the 1000 used should be the maximum length of the column definition or higher e.g. if your IDENTIFIER column is a varchar(2000) then use 2,000 instead.
try trim the IDENTIFIER first like this
SALLER_NAME = UPPER(RIGHT((RTRIM(IDENTIFIER),LEN(IDENTIFIER )-CHARINDEX(',',IDENTIFIER )));

SQL Server - Select column that contains query string and split values into anothers 'columns'

I need to do a select in a column that contains a query string like:
user_id=300&company_id=201503&status=WAITING OPERATION&count=1
I want to perform a select and break each value in a new column, something like:
user_id | company_id | status | count
300 | 201503 | WAITING OPERATION | 1
How can i do it in SQL Server without use procs?
I've tried a function:
CREATE FUNCTION [xpto].[SplitGriswold]
(
#List NVARCHAR(MAX),
#Delim1 NCHAR(1),
#Delim2 NCHAR(1)
)
RETURNS TABLE
AS
RETURN
(
SELECT
Val1 = PARSENAME(Value,2),
Val2 = PARSENAME(Value,1)
FROM
(
SELECT REPLACE(Value, #Delim2, '&') FROM
(
SELECT LTRIM(RTRIM(SUBSTRING(#List, [Number],
CHARINDEX(#Delim1, #List + #Delim1, [Number]) - [Number])))
FROM (SELECT Number = ROW_NUMBER() OVER (ORDER BY name)
FROM sys.all_objects) AS x
WHERE Number <= LEN(#List)
AND SUBSTRING(#Delim1 + #List, [Number], LEN(#Delim1)) = #Delim1
) AS y(Value)
) AS z(Value)
);
GO
Execution:
select QueryString
from User.Log
CROSS APPLY notifier.SplitGriswold(REPLACE(QueryString, ' ', N'ŏ'), N'ŏ', '&') AS t;
But it returns me only one column with all inside:
QueryString
user_id=300&company_id=201503&status=WAITING OPERATION&count=1
Thanks in advance.
I've had to do this many times before, and you're in luck! Since you only have 3 delimiters per string, and that number is fixed, you can use SQL Server's PARSENAME function to do it. That's far less ugly than the best alternative (using the XML parsing stuff). Try this (untested) query (replace TABLE_NAME and COLUMN_NAME with the appropriate names):
SELECT
PARSENAME(REPLACE(COLUMN_NAME,'&','.'),1) AS 'User',
PARSENAME(REPLACE(COLUMN_NAME,'&','.'),2) AS 'Company_ID',
PARSENAME(REPLACE(COLUMN_NAME,'&','.'),3) AS 'Status',
PARSENAME(REPLACE(COLUMN_NAME,'&','.'),4) AS 'Count',
FROM TABLE_NAME
That'll get you the results in the form "user_id=300", which is far and away the hard part of what you want. I'll leave it to you to do the easy part (drop the stuff before the "=" sign).
NOTE: I can't remember if PARSENAME will freak out over the illegal name character (the "=" sign). If it does, simply nest another REPLACE in there to turn it into something else, like an underscore.
You need to use SQL SUBSTRING as part of your select statement. You would first need to build the first row, then use a UNION to return the second row.

Determine if zip code contains numbers only

I have a field called zip, type char(5), which contains zip codes like
12345
54321
ABCDE
I'd like to check with an sql statement if a zip code contains numbers only.
The following isn't working
SELECT * FROM S1234.PERSON
WHERE ZIP NOT LIKE '%'
It can't work because even '12345' is an "array" of characters (it is '%', right?
I found out that the following is working:
SELECT * FROM S1234.PERSON
WHERE ZIP NOT LIKE ' %'
It has a space before %. Why is this working?
If you use SQL Server 2012 or up the following script should work.
DECLARE #t TABLE (Zip VARCHAR(10))
INSERT INTO #t VALUES ('12345')
INSERT INTO #t VALUES ('54321')
INSERT INTO #t VALUES ('ABCDE')
SELECT *
FROM #t AS t
WHERE TRY_CAST(Zip AS NUMERIC) IS NOT NULL
Using answer from here to check if all are digit
SELECT col1,col2
FROM
(
SELECT col1,col2,
CASE
WHEN LENGTH(RTRIM(TRANSLATE(ZIP , '*', ' 0123456789'))) = 0
THEN 0 ELSE 1
END as IsAllDigit
FROM S1234.PERSON
) AS Z
WHERE IsAllDigit=0
DB2 doesnot have regular expression facility like MySQL REGEXP
USE ISNUMERIC function;
ISUMERIC returns 1 if the parameter contains only numbers and zero if it not
EXAMPLE:
SELECT * FROM S1234.PERSON
WHERE ISNUMERIC(ZIP) = 1
Your statement doesn't validate against numbers but it says get everything that doesn't start with a space.
Let's suppose you ZIP code is a USA zip code, composed by 5 numbers.
db2 "with val as (
select *
from S1234.PERSON t
where xmlcast(xmlquery('fn:matches(\$ZIP,''^\d{5}$'')') as integer) = 1
)
select * from val"
For more information about xQuery:fn:matches: http://pic.dhe.ibm.com/infocenter/db2luw/v10r5/topic/com.ibm.db2.luw.xml.doc/doc/xqrfnmat.html
mySql does not have a native isNumberic() function. This would be pretty straight-forward in Excel with the ISNUMBER() function, or in T-SQL with ISNUMERIC(), but neither work in MySQL so after a little searching around I came across this solution...
SELECT * FROM S1234.PERSON
WHERE ZIP REGEXP ('[0-9]')
Effectively we're processing a regular expression on the contents of the 'ZIP' field, it may seem like using a sledgehammer to crack a nut and I've no idea how performance would differ from a more simple approach but it worked and I guess that's the point.
I have made more error-prone version based on the solution https://stackoverflow.com/a/36211270/565525, added intermedia result, some examples:
select
test_str
, TRIM(TRANSLATE(replace(trim(test_str), ' ', 'x'), 'yyyyyyyyyyy', '0123456789'))
, case when length(TRIM(TRANSLATE(replace(trim(test_str), ' ', 'x'), 'yyyyyyyyyyy', '0123456789')))=5 then '5-digit-zip' else 'not 5d-zip' end is_zip
from (VALUES
(' 123 ' )
,(' abc ' )
,(' a12 ' )
,(' 12 3 ')
,(' 99435 ')
,('99323' )
) AS X(test_str)
;
The result for this example set is:
TEST_STR 2 IS_ZIP
-------- -------- -----------
123 yyy not 5d-zip
abc abc not 5d-zip
a12 ayy not 5d-zip
12 3 yyxy not 5d-zip
99435 yyyyy 5-digit-zip
99323 yyyyy 5-digit-zip
Try checking if there's a difference between lower case and upper case. Numerics and special chars will look the same:
SELECT *
FROM S1234.PERSON
WHERE UPPER(ZIP COLLATE Latin1_General_CS_AI ) = LOWER(ZIP COLLATE Latin1_General_CS_AI)
Here's a working example for the case where you'd want to check zip codes in a range. You could use this code for inspiration to make a simple single post code check, if you want:
if local_test_environment?
# SQLite supports GLOB which is similar to LIKE (which it only has limited support for), for matching in strings.
where("(zip_code NOT GLOB '*[^0-9]*' AND zip_code <> '') AND (CAST(zip_code AS int) >= :range_start AND CAST(zip_code AS int) <= :range_finish)", range_start: range_start, range_finish: range_finish)
else
# SQLServer supports LIKE with more advanced matching in strings than what SQLite supports.
# SQLServer supports TRY_PARSE which is non-standard SQL, but fixes the error SQLServer gives with CAST, namely: Conversion failed when converting the nvarchar value 'US-19803' to data type int.
where("(zip_code NOT LIKE '%[^0-9]%' AND zip_code <> '') AND (TRY_PARSE(zip_code AS int) >= :range_start AND TRY_PARSE(zip_code AS int) <= :range_finish)", range_start: range_start, range_finish: range_finish)
end
Use regex.
SELECT * FROM S1234.PERSON
WHERE ZIP REGEXP '\d+'