SQL Extract Values from a String - sql

How do I extract values from a string? I'm trying to separate into 3 new columns. A separate column for city, state and zipcode.
I've tried
select address2,
left(address2, charindex('',address2)-1)
from table
and ---when I try the below code I get "Invalid length parameter passed to the left or substring function"
,LTRIM(substring(a.Address2, CHARINDEX(' ', a.Address2)+1, CHARINDEX(' ', substring(a.address2, charindex(' ',
a.address2)+1, len(a.address2)))-1))
I can break out the city (except for West Warwick) using the following code, but not sure how to make it work for state and zip. This also removes the error.
SUBSTRING(Address2,1,CHARINDEX(' ', a.address2+ ' ')-1) as city
Any ideas what to try?

It looks like your zip codes and your states are all the same length. If that is true, you should be able to use something like this:
SELECT
LEFT(a.Address2,LEN(a.Address2) - 13) AS City,
RIGHT(LEFT(a.Address2,LEN(a.Address2) - 11),2) AS State,
RIGHT(a.Address2,10) AS Zip_Code
FROM
table;
DEMO CODE
Create the table and data:
CREATE TABLE MyTable (Address2 VARCHAR(100));
INSERT INTO MyTable
VALUES
('SAN DIEGO CA 92128-1234'),
('WEST WARWICK RI 02893-1349'),
('RICHMOND IN 47374-9409');
The query:
SELECT
LEFT(Address2,LEN(Address2) - 13) AS City,
RIGHT(LEFT(Address2,LEN(Address2) - 11),2) AS State,
RIGHT(Address2,10) AS Zip_Code
FROM
MyTable;
The output:

Since you only have 3 parts (City/State/Zip) you can take advantage of a function called parsename in SQL Server 2008 and later. (The original intent of the function is to parse out object names.)
Using a combination of the replace and parsename functions will allow you to be able to separate the data into 3 parts, even if the length of the State (not likely) or the Zip (more likely) change.
Example Data:
create table #my_table
(
address2 varchar(75) not null
)
insert into #my_table values ('CONNERSVILLE IN 47331-3351')
insert into #my_table values ('WEST WARWICK RI 02893-1349')
insert into #my_table values ('RICHMOND IN 47374-9409')
insert into #my_table values ('WILLIAMSBURG IN 47393-9617')
insert into #my_table values ('FARMERSVILLE OH 45325-9226')
--this record is an example of a likely scenario for when the zip length would change.
insert into #my_table values ('WILLIAMSBURG IN 47393')
Solution:
with len_vals as
(
select t.address2
, len(parsename(replace(t.address2,' ','.'), 1)) as zip_len
, len(parsename(replace(t.address2,' ','.'), 2)) as st_len
from #my_table as t
group by t.address2
)
select left(a.address2, len(a.address2) - b.zip_len - b.st_len - 2) as city
, substring(a.address2, len(a.address2) - b.zip_len - 2, b.st_len) as st
, right(a.address2, b.zip_len) as zip_code
from #my_table as a
inner join len_vals as b on a.address2 = b.address2
Results:

Related

When Merge null columns are not merged just inserted

My table and it's data are below
CREATE TABLE Sales (Id int ITDENTITY(1,1) NOT NULL,stateid int, district int Sitecount int)
CREATE TABLE Sales1(stateid int, district int,
Sitecount Int)
insert into Sales values (1,2,12)
insert into Sales values (1,3,20)
insert into Sales values (1, NULL, 10)
insert into Salesi values (1,2,10)
insert into Salesi values (1,2. 100)
insert into Select values (1,ULL, 18)
I have used the below query to merge
MERGE Sales AS T
USING (Select stateid, district, Sitecount from Sales1 group by stateid,district) as S
ON(S.stateid =T.stateld and S.district=T.district)
WHEN MATCHED
Then UPDATE SET
T.Sitecount=T.Sitecount+S.Sitecount
WHEN NOT MATCHED BY TARGET THEN INSERT (stateid,district,Sitecount) VALUES(S stateid, S.district, S.5itecount);
Whenever I run the query, if the matched data all columns are not null then only data merged,
Otherwise it is inserted as a new row.
If district data is null, need to add the sitecount based on the stateid.How to achieve it. Suggest me..
You can match on stateid using ISNULL in join like below:
MERGE Sales AS T
USING (Select stateid, district, max(Sitecount) Sitecount from Sales1 group by stateid,district) as S
ON(S.stateid =T.stateid and isnull(S.district,'')=isnull(T.district, ''))
WHEN MATCHED
Then UPDATE SET
T.Sitecount=T.Sitecount+S.Sitecount
WHEN NOT MATCHED BY TARGET THEN INSERT (stateid,district,Sitecount) VALUES(S.stateid, S.district, S.Sitecount);
select * from Sales
Also, please note that I have used max(Sitecount) to avoid the duplicates in join. Please change as per your requirement.
Please find the db<>fiddle here.
Try this:
MERGE Sales AS T
USING (Select stateid, district, Sitecount from Sales1 group by stateid,district) as S
ON(ISNULL(S.stateid, -1) = ISNULL(T.stateld, -1) and ISNULL(S.district, '')= ISNULL(T.district, ''))
WHEN MATCHED
Then UPDATE SET
T.Sitecount=T.Sitecount+S.Sitecount
WHEN NOT MATCHED BY TARGET THEN INSERT (stateid,district,Sitecount) VALUES(S stateid, S.district, S.5itecount);
The following element was adjusted to isnull the comparison fields as a null value as you have observed are treated differently and incorrectly (for what you desire) execute the insert element:
ON(ISNULL(S.stateid, -1) = ISNULL(T.stateld, -1) and ISNULL(S.district, '')= ISNULL(T.district, ''))

Show Grouped By Items in Comma Separated Format

I have a table like below:
create table Location
(
ContinentID int not null,
CountryID int not null,
StateCode nvarchar(10) not null
)
Insert into Location Values (1, 1, 'AP')
Insert into Location Values (1, 1, 'WB')
Insert into Location Values (1, 1, 'MH')
Insert into Location Values (1, 2, 'KA')
Insert into Location Values (1, 2, 'ID')
Insert into Location Values (3, 1, 'NY')
Insert into Location Values (3, 1, 'WA')
Insert into Location Values (3, 2, 'VI')
Here I need all the state codes should be shown in a comma separated format based on ContinentID and CountryID. So the output must look like below:
ContinentID CountryID StateCodes
----------- --------- ----------
1 1 AP,WB,MH
1 2 KA,ID
3 1 NY,WA
3 2 VI
I don't have much idea about SQL queries, I tried one below, but it didn't work:
SELECT Continentid, CountryID, CONCAT(StateCode, ',') FROM Location
GROUP BY Continentid, CountryID
How can I get the desired output using a single SQL Query ? Any help is appreciated.
In T-SQL, FOR XML PATH probably gives you the best performance. STUFF handles the out of place leading comma.
Click here to see the SQL Fiddle.
SELECT ContinentID, CountryID,
StateCode =
STUFF((SELECT ', ' + StateCode
FROM Location b
WHERE b.ContinentID = a.ContinentID
and
b.CountryID = a.CountryID
FOR XML PATH('')), 1, 2, '')
FROM Location a
GROUP BY ContinentID, CountryID
This answer can also help.
Ah -- it's tricky :) Here is one of the methods from here adapted for your data structure. I have verified that it produces the result you want except for a trailing comma...
https://www.simple-talk.com/sql/t-sql-programming/concatenating-row-values-in-transact-sql/
SELECT p1.ContinentID, p1.CountryID,
( SELECT StateCode + ','
FROM Location p2
WHERE p2.ContinentID = p1.ContinentID AND p2.CountryID = p1.CountryID
ORDER BY StateCode
FOR XML PATH('') ) AS StateCodes
FROM Location p1
GROUP BY ContinentID, CountryID

SQL Column / Row Grouping

I am new to SQL. I am looking for a simple SQL solution to combine a row/column for row whose column contain the same data, in this case a zip code. For example, the data look looks like this:
state, county, city, zip, count
"CA","ALAMEDA","HAYWARD","94541",5371
"CA","ALAMEDA","HAYWARD","94542",2209
"CA","ALAMEDA","HAYWARD","94544",7179
"CA","ALAMEDA","HAYWARD","94545",4209
"CA","ALAMEDA","CASTRO VALLEY","94546",7213
"CA","ALAMEDA","HAYWARD","94546",37
"CA","ALAMEDA","LIVERMORE","94550",9809
"CA","ALAMEDA","LIVERMORE","94551",6558
"CA","ALAMEDA","CASTRO VALLEY","94552",3121
"CA","ALAMEDA","HAYWARD","94552",12
"CA","ALAMEDA","FREMONT","94555",5392
I'd like to end up with the data to look like this:
state, county, city, zip, count
"CA","ALAMEDA","HAYWARD","94541",5371
"CA","ALAMEDA","HAYWARD","94542",2209
"CA","ALAMEDA","HAYWARD","94544",7179
"CA","ALAMEDA","HAYWARD","94545",4209
"CA","ALAMEDA","CASTRO VALLEY / HAYWARD","94546",7250
"CA","ALAMEDA","LIVERMORE","94550",9809
"CA","ALAMEDA","LIVERMORE","94551",6558
"CA","ALAMEDA","CASTRO VALLEY HAYWARD","94552",3133
"CA","ALAMEDA","FREMONT","94555",5392
You can see that in two rows the data has been combined or summed. For rows that contain the exact same zip code, the city names (both) appear in the city column and the count is the sum of the count from each row.
Is there any way to do this using SQL? Even if it requires two different SQL statements that is fine.
Assuming SQL Server, you can use FOR XML to get your desired results.
select distinct t.state,t.county,t.zip,t2.sumcount,
STUFF(
(
SELECT '/' + city AS [text()]
FROM mytable t3
WHERE t.zip = t3.zip
FOR XML PATH('')
), 1, 1, '') AS ColList
from mytable t
join (select zip, sum(count) as sumcount
from mytable
group by zip) t2 on t.zip=t2.zip
And some SQL Fiddle.
If you are using MySQL, look at using GROUP_CONCAT:
select distinct t.state,t.county,t.zip,t2.sumcount,
GROUP_CONCAT(t.city) as cities
from mytable t
join (select zip, sum(count) as sumcount
from mytable
group by zip) t2 on t.zip=t2.zip
GROUP BY t.state,t.county,t.zip,t2.sumcount
And more Fiddle.
Good luck.
I tweaked sgeddes' superb answer to actually get the count and avoid duplicate entries, and added a support script so you can test it. This does assume SQL Server.
IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'MYTABLE') BEGIN
drop table MYTABLE;
END;
go
create table MYTABLE
(
state nvarchar(2)
,county nvarchar(100)
,city nvarchar(100)
,zip nvarchar(10)
)
go
insert into MYTABLE(state,county,city,zip) values('CA','ALAMEDA','HAYWARD','94541');
insert into MYTABLE(state,county,city,zip) values('CA','ALAMEDA','HAYWARD','94541');
insert into MYTABLE(state,county,city,zip) values('CA','ALAMEDA','HAYWARD','94544');
insert into MYTABLE(state,county,city,zip) values('CA','ALAMEDA','HAYWARD','94545');
insert into MYTABLE(state,county,city,zip) values('CA','ALAMEDA','CASTRO VALLEY','94546');
insert into MYTABLE(state,county,city,zip) values('CA','ALAMEDA','CASTRO VALLEY','94546');
insert into MYTABLE(state,county,city,zip) values('CA','ALAMEDA','CASTRO VALLEY','94546');
insert into MYTABLE(state,county,city,zip) values('CA','ALAMEDA','CASTRO VALLEY','94546');
insert into MYTABLE(state,county,city,zip) values('CA','ALAMEDA','CASTRO VALLEY','94546');
insert into MYTABLE(state,county,city,zip) values('CA','ALAMEDA','HAYWARD','94546');
insert into MYTABLE(state,county,city,zip) values('CA','ALAMEDA','HAYWARD','94546');
insert into MYTABLE(state,county,city,zip) values('CA','ALAMEDA','HAYWARD','94546');
insert into MYTABLE(state,county,city,zip) values('CA','ALAMEDA','HAYWARD','94546');
insert into MYTABLE(state,county,city,zip) values('CA','ALAMEDA','LIVERMORE','94550');
insert into MYTABLE(state,county,city,zip) values('CA','ALAMEDA','LIVERMORE','94551');
insert into MYTABLE(state,county,city,zip) values('CA','ALAMEDA','CASTRO VALLEY','94552');
insert into MYTABLE(state,county,city,zip) values('CA','ALAMEDA','HAYWARD','94552');
insert into MYTABLE(state,county,city,zip) values('CA','ALAMEDA','FREMONT','94555');
select distinct
t.state
,t.county
,t.zip
,t2.sumcount
,STUFF((
SELECT distinct '/' + city AS [text()]
FROM mytable t3
WHERE t.zip = t3.zip
FOR XML PATH('')
), 1, 1, '') AS ColList
from
mytable t
inner join
(
select zip, sum(count) as sumcount
from
(
select zip,count(*) as count
from mytable
group by zip
) x
group by zip
) t2
on t.zip=t2.zip
The output looks like this:
state county zip sumcount ColList
CA ALAMEDA 94541 2 HAYWARD
CA ALAMEDA 94544 1 HAYWARD
CA ALAMEDA 94545 1 HAYWARD
CA ALAMEDA 94546 9 CASTRO VALLEY/HAYWARD
CA ALAMEDA 94550 1 LIVERMORE
CA ALAMEDA 94551 1 LIVERMORE
CA ALAMEDA 94552 2 CASTRO VALLEY/HAYWARD
CA ALAMEDA 94555 1 FREMONT

How to update value of column x with value of column y?

I have a large table with a column x that I want to replicate to another column y in the same table. What would be the SQL statement for this?
The 1 st column is called name and it contains first name and last name separated by a space. How to move those to new first name and last name columns?
Sometimes names have extra spaces, this script will handle those situations
create table #t(name nvarchar(100), firstname nvarchar(50), lastname nvarchar(50))
insert #t (name) values ('Thomas Clausen')
insert #t (name) values (' Bill Gates')
insert #t (name) values ('Steven Jobs ')
insert #t (name) values ('Donald of Duck')
insert #t (name) values ('microsoft')
insert #t (name) values ('')
update #t
set firstname = nullif(left(ltrim(name), charindex(' ', ltrim(name) + ' ')-1), ''),
lastname = nullif(stuff(rtrim(name), 1,len(rtrim(name))
- charindex(' ', reverse(rtrim(name))), ''), '')
Result:
FirstName LastName
Thomas Clausen
Bill Gates
Steven Jobs
Donald Duck
microsoft NULL
NULL NULL
update YourTable
set y = x
Edited:
UPDATE Table
SET y = x
Edited:
From the modification on your question I understand that you have a column 'name' with something like John Adams and want to fill two columns that already exist, FirstName and LastName. If that is the case, try this
UPDATE table
SET FirstName = PARSENAME(REPLACE(name, ' ', '.'), 2),
LastName = PARSENAME(REPLACE(name, ' ', '.'), 1)
If there's no space separating the first name and last name, no error message will be encountered and a NULL value will be returned for the FirstName while the whole full name will be returned as the LastName.
Select t1.Name,t1.Marks as Maths,t2.Marks as physics,t3.Marks as Chemistry from
(select T.Name,T.Marks,T.Subjects
from Student as T
where T.Subjects like 'Maths') as t1
inner join
(select D.Name,D.Marks,D.Subjects
from Student as D
where D.Subjects like 'Physics') as t2
on t1.Name like t2.Name
inner join
(select E.Name,E.Marks,E.Subjects
from Student as E
where E.Subjects like 'Chemistry') as t3
on t2.Name like t3.Name

Column conflicts with the type of other columns in the unpivot list

Im pivoting sys.[views] into key value pairs to compare with values on another server for consistency testing. Im running into an issue which returns the error.
Msg 8167, Level 16, State 1, Line 51
The type of column "type" conflicts with the type of other columns specified in the UNPIVOT list.
Query:
SELECT
sourceUnpivoted.idServer,
sourceUnpivoted.sourceServerName,
sourceUnpivoted.name,
sourceUnpivoted.columnName,
sourceUnpivoted.columnValue
FROM (
SELECT
CAST('1' AS VARCHAR(255)) AS idServer,
CAST('thisOne' AS VARCHAR(255)) AS sourceServerName,
CAST('theDatabase' AS VARCHAR(255)) AS sourceDatabaseName,
CAST(name AS VARCHAR(255)) AS name,
CAST(object_id AS VARCHAR(255)) AS object_id,
CAST(principal_id AS VARCHAR(255)) AS principal_id,
CAST(schema_id AS VARCHAR(255)) AS schema_id,
CAST(parent_object_id AS VARCHAR(255)) AS parent_object_id,
CAST(type AS VARCHAR(255)) AS type,
CAST(type_desc AS VARCHAR(255)) AS type_desc,
CAST(create_date AS VARCHAR(255)) AS create_date,
CAST(lock_escalation_desc AS VARCHAR(255)) AS lock_escalation_desc
...
FROM noc_test.dbo.stage_sysTables
) AS databaseTables
UNPIVOT (
columnValue FOR columnName IN (
object_id,
principal_id,
schema_id,
parent_object_id,
type,
type_desc,
create_date,
lock_escalation_desc
)
) AS sourceUnpivoted
Why does this not like [type],[type_desc],[lock_escalation_desc] ???
Ive also tried CONVERT(VARCHAR(255),type) AS type
It's actually a collation issue. I can resolve it by changing these lines:
CAST([type] collate database_default AS VARCHAR(255)) AS [type],
CAST(type_desc collate database_default AS VARCHAR(255)) AS type_desc,
CAST(create_date AS VARCHAR(255)) AS create_date,
CAST(lock_escalation_desc collate database_default AS VARCHAR(255)) AS lock_escalation_desc
The specific issue is that name is collated as Latin1_General_CI_AS, whereas the other 3 columns you mentioned are collated as Latin1_General_CI_AS_KS_WS (At least, on my machine, I'm not sure what it would be like on a server/database with different default collation).
This is one of the solution for this type error
1: create the this table
CREATE TABLE People
(
PersonId int,
Firstname varchar(50),
Lastname varchar(25)
)
2: Then insert
INSERT INTO People VALUES (1, 'Jim', 'Smith');
INSERT INTO People VALUES (2, 'Jane', 'Jones');
INSERT INTO People VALUES (3, 'Bob', 'Unicorn');
3: run this script you get the error
Msg 8167, Level 16, State 1, Line 3
The type of column "Lastname" conflicts with the type of other columns specified in the UNPIVOT list.
SELECT PersonId, ColumnName, Value
FROM People
unpivot(Value FOR ColumnName IN (FirstName, LastName)) unpiv;
4: the solution is you must use a subquery to first cast the Lastname column to have the same length as Firstname
SELECT PersonId, ColumnName, Value
FROM (
SELECT personid, firstname, cast(lastname AS VARCHAR(50)) lastname
FROM People
) d
unpivot(Value FOR ColumnName IN (FirstName, LastName)) unpiv;
Ran into this same error and I just made all the columns in the table of the same data type - I had a mix of int, varchar, nvarchar of various lengths. Once I converted all the columns in my table to the same type - nvarchar(255) it worked perfectly.
The PIVOT/UNPIVOT clause is sensitive to the ANSI Padding Status of the column (right-click -> properties in SSMS) as well as the type, size and collation. Try specifying SET ANSI_PADDING ON|OFF in the session before adding or recreating the column in question so it matches the others in the PIVOT/UNPIVOT clause.
I had the same issue. Fixed it by right clicking on the column header and selecting change type "using locale". See attached screen shot
1: