TSQL - Move any text after CR/LF to a new column - sql-server

We have an address field in our database that I need to split up into two columns (Address1 and Address2). I'm looking for a way to split up this string after the CR/LF section.
How can I do this? I'm using SSMS 2016

Something like this could be used -
Carriage return is char(13)
DECLARE #Str table (str VARCHAR(100))
insert into #Str values
('Test
Word'),
('NoCRLF')
SELECT CASE WHEN CHARINDEX(CHAR(13), Str ) > 0 THEN SUBSTRING(Str , 1, CHARINDEX(CHAR(13), Str ) - 1) ELSE Str END AS [First],
CASE WHEN CHARINDEX(CHAR(13), Str ) > 0 THEN SUBSTRING(Str , CHARINDEX(CHAR(13), Str ) + 1, LEN(Str )) ELSE NULL END AS [Last]
FROM #Str

You can use CHARINDEX to find if your address has new line character or not. and based on that you can split up you address using SUBSTRING.
For address1, you should start from position 1 and end at position 1 less than index of new line character.
Similarly for address2, you should start from position 1 more than index of new line character and end the end of the string.
Code below handles cases like when there is no CHAR(13) you will get Address1 only and Address2 will be NULL. Same way if Address is NULL or Blank space it will handle it.
Below is the sample code:
DECLARE #AddressTable AS TABLE
(
Address nvarchar(MAX) NULL,
Address1 nvarchar(MAX) NULL,
Address2 nvarchar(MAX) NULL
)
INSERT #AddressTable (Address)
SELECT '1600 Pennsylvania Avenue' + CHAR(13) + 'Washington DC ' Address UNION ALL
SELECT '221 B Baker St, London, England' Address UNION ALL
SELECT NULL Address UNION ALL
SELECT '' Address UNION ALL
SELECT '11 Wall Street' + CHAR(13) + 'New York, NY' Address
SELECT
CASE
WHEN CHARINDEX(CHAR(13), Address) > 0 THEN SUBSTRING(Address, 1, CHARINDEX(CHAR(13), Address) - 1) + '|'
ELSE Address END
Address1,
CASE
WHEN CHARINDEX(CHAR(13), Address) > 0 THEN SUBSTRING(Address, CHARINDEX(CHAR(13), Address) + 1, LEN(Address)) + '|'
ELSE NULL END
Address2
FROM #AddressTable

From SQL Server 2016 there is a string_split function:
https://learn.microsoft.com/en-us/sql/t-sql/functions/string-split-transact-sql

Related

Snowflake convert string into output

select ' \''||
CAST(NVL(X.TYPE,'') AS VARCHAR(50))||'\
\''||CAST(NVL(X.ID_1,'') AS VARCHAR(50))||'\
- \''||
---CAST(NVL(X.ID_2,'') AS VARCHAR(50))
CASE WHEN X.ID_2 IS NOT NULL
THEN X.ID_2
WHEN X.ID_2 IS NULL
THEN 'NULL' END
||'\ '
from
( select ....)
select query returns value for three columns.
Above code gives o/p as 'R'32 - 'NULL
Expected is 'R 32 - NULL'
Can someone help
Using CONCAT and COALESCE:
SELECT *,
CONCAT('\'',COALESCE(X.TYPE,''), ' ' ,
COALESCE(X.ID_1::TEXT, ''), ' - ' ,
COALESCE(X.ID_2::TEXT, 'NULL'), '\'') AS res
FROM (SELECT 'R' AS TYPE, 32::INT AS ID_1, NULL AS ID_2) AS X;
Output:
#Rahul, I believe you are looking for something like this? The escaped apostrophes were the culprit from Lukasz's answer.
SELECT
CONCAT(
COALESCE(X.TYPE,'')
, CHAR(32) /* <-- ADDS A SPACE */
, COALESCE(X.ID_1::TEXT, '')
, CHAR(32), CHAR(45), CHAR(32) /* <-- ADDS A SPACE DASH SPACE */
, COALESCE(X.ID_2::TEXT, 'NULL')
) AS res
FROM (SELECT 'R' AS TYPE, 32::INT AS ID_1, NULL AS ID_2) AS X
;
Returns:
R 32 - NULL

Regex in sql server to match numeric data

I have a table which looks like
name 34
name 4
name 9
n1am3e jyhjgyn 797907
n1am3e 0yhjgyn 797907
Now i want the output to be like
name
name
name
n1am3e jyhjgyn
n1am3e 0yhjgyn
That is if the last word is only numeric then remove it
select dad, PATINDEX('% [0-9]%',dad) from (select (name) as dad from name) as c
I tried the above code to get the index but in the last case it gives 7 instead of 15
Try this:
CREATE TABLE temp(
name VARCHAR(200)
)
INSERT INTO temp VALUES
('name 34'), ('name 4'), ('name 9'), ('n1am3e jyhjgyn 797907'), ('n1am3e 0yhjgyn 7212'), ('n1am3e 0yhjgyn 72e12'), ('11');
SELECT
CASE
WHEN CHARINDEX(' ', name) > 1 THEN
CASE
WHEN RIGHT(name, CHARINDEX(' ', REVERSE(name)) -1) NOT LIKE '%[^0-9]%' -- check if last word is all digits
THEN LEFT(name, LEN(name) - CHARINDEX(' ', REVERSE(name)))
ELSE name
END
ELSE name
END
FROM temp
DROP TABLE temp
You can write a query as:
SELECT
CASE ISNUMERIC (RIGHT(name, NULLIF(charindex(' ', REVERSE(name)),0)))-- get last word
WHEN 1 THEN -- get string without last word
REVERSE(RIGHT(REVERSE (name), len(name) - NULLIF(charindex(' ', REVERSE(name)),0)))
ELSE name -- get whole string
end
AS dad
FROM #tbl
Try this .. Use string FunctionsLeft,Reverse and Substring
CREATE TABLE #test(name VARCHAR(50))
INSERT #test
VALUES ('name 34'),('name 4'),('name 9'),
('n1am3e jyhjgyn 797907'),
('n1am3e 0yhjgyn 797907')
SELECT CASE
WHEN Patindex('%[a-z]%', LEFT(Reverse(name), CASE
WHEN Charindex(' ', Reverse(name)) = 0 THEN Len(name)
ELSE Charindex(' ', Reverse(name))
END)) = 0 THEN Reverse(Substring(Reverse(name), Charindex(' ', Reverse(name)), Len(name)))
ELSE name
END
FROM #test

split alpha and numeric using sql

I have a table and it has a 3 columns. The first column is the data that contains value(numeric) and unit(percentage and etc..), the second column is numeric column, the third is Unit column. What I want to do is split the numeric and the unit from the first column then put those split-ted data to its designated column.
Here is my table:
I tried this function:SO link here..., it really does splitting alpha and numeric but then I'm new in using SQL Function, my problem there is the parameter must be in string STRING, so what I did is change it to Sub Query but it gives me error.
Sample COde:
SQL FUNCTION:
create function [dbo].[GetNumbersFromText](#String varchar(2000))
returns table as return
(
with C as
(
select cast(substring(S.Value, S1.Pos, S2.L) as int) as Number,
stuff(s.Value, 1, S1.Pos + S2.L, '') as Value
from (select #String+' ') as S(Value)
cross apply (select patindex('%[0-9]%', S.Value)) as S1(Pos)
cross apply (select patindex('%[^0-9]%', stuff(S.Value, 1, S1.Pos, ''))) as S2(L)
union all
select cast(substring(S.Value, S1.Pos, S2.L) as int),
stuff(S.Value, 1, S1.Pos + S2.L, '')
from C as S
cross apply (select patindex('%[0-9]%', S.Value)) as S1(Pos)
cross apply (select patindex('%[^0-9]%', stuff(S.Value, 1, S1.Pos, ''))) as S2(L)
where patindex('%[0-9]%', S.Value) > 0
)
select Number
from C
)
SELECT STATEMENT with SUB Query:
declare #S varchar(max)
select number from GetNumbersFromText(Select SomeColm From Table_Name) option (maxrecursion 0)
BTW, im using sql server 2005.
Thanks!
If the numeric part is always at the beginning, then you can use this:
PATINDEX('%[0-9][^0-9]%', ConcUnit)
to get the index of the last digit.
Thus, this:
DECLARE #str VARCHAR(MAX) = '4000 ug/ML'
SELECT LEFT(#str, PATINDEX('%[0-9][^0-9]%', #str )) AS Number,
LTRIM(RIGHT(#str, LEN(#str) - PATINDEX('%[0-9][^0-9]%', #str ))) As Unit
gives you:
Number Unit
-------------
4000 ug/ML
EDIT:
If numeric data include double values as well, then you can use this:
SELECT LEN(#str) - PATINDEX ('%[^0-9][0-9]%', REVERSE(#str))
to get the index of the last digit.
Thus, this:
SELECT LEFT(#str, LEN(#str) - PATINDEX ('%[^0-9][0-9]%', REVERSE(#str)))
gives you the numeric part.
And this:
SELECT LEFT(#str, LEN(#str) - PATINDEX ('%[^0-9][0-9]%', REVERSE(#str))) AS Numeric,
CASE
WHEN CHARINDEX ('%', #str) <> 0 THEN LTRIM(RIGHT(#str, LEN(#str) - CHARINDEX ('%', #str)))
ELSE LTRIM(RIGHT(#str, PATINDEX ('%[^0-9][0-9]%', REVERSE(#str))))
END AS Unit
gives you both numberic and unit part.
Here are some tests that I made with the data you have posted:
Input:
DECLARE #str VARCHAR(MAX) = '50 000ug/ML'
Output:
Numeric Unit
------------
50 000 ug/ML
Input:
DECLARE #str VARCHAR(MAX) = '99.5%'
Output:
Numeric Unit
------------
99.5
Input:
DECLARE #str VARCHAR(MAX) = '4000 . 35 % ug/ML'
Output:
Numeric Unit
------------------
4000 . 35 ug/ML
Here is my answer. Check output in SQLFiddle for the same.
create TABLE temp
(
string NVARCHAR(50)
)
INSERT INTO temp (string)
VALUES
('4000 ug\ml'),
('2000 ug\ml'),
('%'),
('ug\ml')
SELECT subsrtunit,LEFT(subsrtnumeric, PATINDEX('%[^0-9]%', subsrtnumeric+'t') - 1)
FROM (
SELECT subsrtunit = SUBSTRING(string, posofchar, LEN(string)),
subsrtnumeric = SUBSTRING(string, posofnumber, LEN(string))
FROM (
SELECT string, posofchar = PATINDEX('%[^0-9]%', string),
posofnumber = PATINDEX('%[0-9]%', string)
FROM temp
) d
) t
Updated Version to handle 99.5 ug\ml
create TABLE temp
(
string NVARCHAR(50)
)
INSERT INTO temp (string)
VALUES
('4000 ug\ml'),
('2000 ug\ml'),
('%'),
('ug\ml'),
('99.5 ug\ml')
SELECT subsrtunit,LEFT(subsrtnumeric, PATINDEX('%[^0-9.]%', subsrtnumeric+'t') - 1)
FROM (
SELECT subsrtunit = SUBSTRING(string, posofchar, LEN(string)),
subsrtnumeric = SUBSTRING(string, posofnumber, LEN(string))
FROM (
SELECT string, posofchar = PATINDEX('%[^0-9.]%', string),
posofnumber = PATINDEX('%[0-9.]%', string)
FROM temp
) d
) t
Updated Version: To handle 1 000 ug\ml,20 000ug\ml
create TABLE temp
(
string NVARCHAR(50)
)
INSERT INTO temp (string)
VALUES
('4000 ug\ml'),
('2000 ug\ml'),
('%'),
('ug\ml'),
('99.5 ug\ml'),
('1 000 ug\ml'),
('20 000ug\ml')
SELECT substring(replace(subsrtunit,' ',''),PATINDEX('%[0-9.]%', replace(subsrtunit,' ',''))+1,len(subsrtunit)),
LEFT(replace(subsrtnumeric,' ',''), PATINDEX('%[^0-9.]%', replace(subsrtnumeric,' ','')+'t') - 1)
FROM (
SELECT subsrtunit = SUBSTRING(string, posofchar, LEN(string)),
subsrtnumeric = SUBSTRING(string, posofnumber, LEN(string))
FROM (
SELECT string, posofchar = PATINDEX('%[^0-9.]%', replace(string,' ','')),
posofnumber = PATINDEX('%[0-9.]%', replace(string,' ',''))
FROM temp
) d
) t
Check out SQLFiddle for the same.
Would something like this work? Based on the shown data it looks like it would.
Apply it to your data set as a select and if you like the results then you can make an update from it.
WITH cte as (SELECT 'ug/mL' ConcUnit, 500 as [Numeric], '' as Unit
UNION ALL SELECT '2000 ug/mL', NULL, '')
SELECT
[ConcUnit] as [ConcUnit],
[Numeric] as [Original Numeric],
[Unit] as [Original Unit],
CASE WHEN ConcUnit LIKE '% %' THEN
SUBSTRING(ConcUnit, 1, CHARINDEX(' ', ConcUnit) - 1)
ELSE [Numeric] END as [New Numeric],
CASE WHEN ConcUnit LIKE '% %'
THEN SUBSTRING(ConcUnit, CHARINDEX(' ', ConcUnit) + 1, LEN(ConcUnit))
ELSE ConcUnit END as [New Unit]
FROM cte
change #concunit & #unitx Respectively
DECLARE #concunit varchar(10)='45.5%'
DECLARE #unitx varchar(10)='%'
BEGIN
SELECT RTRIM(SUBSTRING( #concunit , 1 , CHARINDEX( #unitx , #concunit
) - 1
)) AS Number,
RTRIM(SUBSTRING( #concunit , CHARINDEX( #unitx , #concunit
) , LEN( #concunit
) - (CHARINDEX( #unitx , #concunit
) - 1)
)) AS Unit
end
I had the same dilemma, but in my case the alpha's were in front of the numerics.
So using the logic that #Giorgos Betsos added to his answer, I just reversed it.
I.e., when your input is :
abc123
You can split it like this:
declare #input varchar(30) = 'abc123'
select
replace(#input,reverse(LEFT(reverse(#input), PATINDEX('%[0-9][^0-9]%', reverse(#input) ))),'') Alpha
, reverse(LEFT(reverse(#input), PATINDEX('%[0-9][^0-9]%', reverse(#input) ))) Numeric
Results :

How to get a comma-separated single address from a SQL Server database where some elements might be NULL

So this is a pretty standard requirement; I have a table holding address information and I want to pull out a single, "formatted" address with all the fields concatenated with comma separators.
The problem is that some fields can be NULL and so I end up with trailing commas. For example, one address might be "10 The Strand, London" with no country on the end but the next one might be "5 Fleet Street, London, England". If I pick out each address element and assume it is always present then I would present these two addresses as:
"5 Fleet Street, , , London,"
"10 The Strand, , , London, England"
Getting rid of the extra commas from the middle is simple, just a test for NULL.
I know how to fix the trailing comma problem in two passes, either using a CTE or a subquery, but can this be done in a single pass through the data?
Here is an example using a CTE:
DECLARE #Address TABLE (
Name VARCHAR(255),
Line1 VARCHAR(255),
Line2 VARCHAR(255),
Line3 VARCHAR(255),
City VARCHAR(255),
Country VARCHAR(255));
INSERT INTO #Address VALUES ('Complete', 'Test 1', 'Test 2', 'Test 3', 'Oxford', 'England');
INSERT INTO #Address VALUES ('Incomplete', '22 Accacia', NULL, NULL, 'York', 'England');
INSERT INTO #Address VALUES ('Missing End', '10 Bond Street', NULL, NULL, 'London', NULL);
WITH Addresses AS
(
SELECT
CASE WHEN Name IS NULL THEN '' ELSE Name + ', ' END +
CASE WHEN Line1 IS NULL THEN '' ELSE Line1 + ', ' END +
CASE WHEN Line2 IS NULL THEN '' ELSE Line2 + ', ' END +
CASE WHEN Line3 IS NULL THEN '' ELSE Line3 + ', ' END +
CASE WHEN City IS NULL THEN '' ELSE City + ', ' END +
CASE WHEN Country IS NULL THEN '' ELSE Country + ', ' END AS [Address]
FROM
#Address)
SELECT LEFT([Address], LEN([Address]) - 1) AS [Address Clean] FROM Addresses;
Which gives me:
Complete, Test 1, Test 2, Test 3, Oxford, England
Incomplete, 22 Accacia, York, England
Missing End, 10 Bond Street, London
Why do I want this? Partly because I can't think of a way to do this but "feel" there should be a way to get what I want and partly because this query is running over a linked server to a SQL 2000 box and so I can't use CTEs (although I could easily rewrite the query using a sub query instead).
Use coalesce, for example:
WITH Addresses AS
(
SELECT
coalesce(Name, '') +
coalesce(', ' + Line1, '') +
coalesce(', ' + Line2, '') +
coalesce(', ' + Line3, '') +
coalesce(', ' + City, '') +
coalesce(', ' + Country, '') AS [Address]
FROM
#Address)
SELECT Address FROM Addresses
This will return the first parameter that is not null, so for example if Line1 is null, then '' will be returned (otherwise ,Line1).
Note that for this to work, CONCAT_NULL_YIELDS_NULL must be set to ON.
Results against the test data:
Complete, Test 1, Test 2, Test 3, Oxford, England
Incomplete, 22 Accacia, York, England
Missing End, 10 Bond Street, London
I did a couple of things here. First I used ISNULL to determine if the value was NULL and if so return a '' (empty string). I added the ', ' as a delimiter to the value before testing to see if it is NULL or not. This way if the column is null then the column+delimiter is also NULL and the ISNULL test still returns a ''. I put the delimiter before the column value to make it easier to remove the extra delimiter. Had the extra delimiter been at the end of the string then I would have had to use a LEN function or something similar to calculate where the extra delimiter was. This way it is always at the beginning of the string and by using the STUFF function I could replace the first 2 characters with a '' effectively removing them.
DECLARE #Address TABLE (
Name VARCHAR(255),
Line1 VARCHAR(255),
Line2 VARCHAR(255),
Line3 VARCHAR(255),
City VARCHAR(255),
Country VARCHAR(255));
INSERT INTO #Address VALUES ('Complete', 'Test 1', 'Test 2', 'Test 3', 'Oxford', 'England');
INSERT INTO #Address VALUES ('Incomplete', '22 Accacia', NULL, NULL, 'York', 'England');
INSERT INTO #Address VALUES ('Missing End', '10 Bond Street', NULL, NULL, 'London', NULL);
SELECT STUFF(
ISNULL(', '+Name,'') + ISNULL(', '+Line1,'') + ISNULL(', '+Line2,'') +
ISNULL(', '+Line3,'') + ISNULL(', '+City,'') + ISNULL(', '+Country,'')
,1,2,'')
FROM #Address

SQL: Concatenate column values in a single row into a string separated by comma

Let's say I have a table like this in SQL Server:
Id City Province Country
1 Vancouver British Columbia Canada
2 New York null null
3 null Adama null
4 null null France
5 Winnepeg Manitoba null
6 null Quebec Canada
7 Seattle null USA
How can I get a query result so that the location is a concatenation of the City, Province, and Country separated by ", ", with nulls omitted. I'd like to ensure that there aren't any trailing comma, preceding commas, or empty strings. For example:
Id Location
1 Vancouver, British Columbia, Canada
2 New York
3 Adama
4 France
5 Winnepeg, Manitoba
6 Quebec, Canada
7 Seattle, USA
I think this takes care of all of the issues I spotted in other answers. No need to test the length of the output or check if the leading character is a comma, no worry about concatenating non-string types, no significant increase in complexity when other columns (e.g. Postal Code) are inevitably added...
DECLARE #x TABLE(Id INT, City VARCHAR(32), Province VARCHAR(32), Country VARCHAR(32));
INSERT #x(Id, City, Province, Country) VALUES
(1,'Vancouver','British Columbia','Canada'),
(2,'New York' , null , null ),
(3, null ,'Adama' , null ),
(4, null , null ,'France'),
(5,'Winnepeg' ,'Manitoba' , null ),
(6, null ,'Quebec' ,'Canada'),
(7,'Seattle' , null ,'USA' );
SELECT Id, Location = STUFF(
COALESCE(', ' + RTRIM(City), '')
+ COALESCE(', ' + RTRIM(Province), '')
+ COALESCE(', ' + RTRIM(Country), '')
, 1, 2, '')
FROM #x;
SQL Server 2012 added a new T-SQL function called CONCAT, but it is not useful here, since you still have to optionally include commas between discovered values, and there is no facility to do that - it just munges values together with no option for a separator. This avoids having to worry about non-string types, but doesn't allow you to handle nulls vs. non-nulls very elegantly.
select Id ,
Coalesce( City + ',' +Province + ',' + Country,
City+ ',' + Province,
Province + ',' + Country,
City+ ',' + Country,
City,
Province,
Country
) as location
from table
This is a hard problem, because the commas have to go in-between:
select id, coalesce(city+', ', '')+coalesce(province+', ', '')+coalesce(country, '')
from t
seems like it should work, but we can get an extraneous comma at the end, such as when country is NULL. So, it needs to be a bit more complicated:
select id,
(case when right(val, 2) = ', ' then left(val, len(val) - 1)
else val
end) as val
from (select id, coalesce(city+', ', '')+coalesce(province+', ', '')+coalesce(country, '') as val
from t
) t
Without a lot of intermediate logic, I think the simplest way is to add a comma to each element, and then remove any extraneous comma at the end.
Use the '+' operator.
Understand that null values don't work with the '+' operator (so for example: 'Winnepeg' + null = null), so be sure to use the ISNULL() or COALESCE() functions to replace nulls with an empty string, e.g.: ISNULL('Winnepeg','') + ISNULL(null,'').
Also, if it is even remotely possible that one of your collumns could be interpreted as a number, then be sure to use the CAST() function as well, in order to avoid error returns, e.g.: CAST('Winnepeg' as varchar(100)).
Most of the examples so far neglect one or more pieces of this. Also -- some of the examples use subqueries or do a length check, which you really ought not to do -- just not necessary -- though your optimizer might save you anyway if you do.
Good Luck
ugly but it will work for MS SQL:
select
id,
case
when right(rtrim(coalesce(city + ', ','') + coalesce(province + ', ','') + coalesce(country,'')),1)=',' then left(rtrim(coalesce(city + ', ','') + coalesce(province + ', ','') + coalesce(country,'')),LEN(rtrim(coalesce(city + ', ','') + coalesce(province + ', ','') + coalesce(country,'')))-1)
else rtrim(coalesce(city + ', ','') + coalesce(province + ', ','') + coalesce(country,''))
end
from
table
I know it's an old question, but should someone should stumble upon this today, SQL Server 2017 and later has the STRING_AGG function, with the WITHIN GROUP option :
with level1 as
(select id,city as varcharColumn,1 as columnRanking from mytable
union
select id,province,2 from mytable
union
select id,country,3 from mytable)
select STRING_AGG(varcharColumn,', ')
within group(order by columnRanking)
from level1
group by id
Should empty strings exist aside of nulls, they should be excluded with some WHERE clause in level1.
Here is an option:
SELECT (CASE WHEN City IS NULL THEN '' ELSE City + ', ' END) +
(CASE WHEN Province IS NULL THEN '' ELSE Province + ', ' END) +
(CASE WHEN Country IS NULL THEN '' ELSE Country END) AS LOCATION
FROM MYTABLE

Resources