SQL Server: select value, split on a delimiter, then update two columns - sql

I have a SQL Server 2008 database table with three varchar Columns - Col1, Col2, Col3. Col1 has data in it with a single space in between, Col2 and Col3 are empty.
I need to write a query to select the data from Col1, break up each value using the space as the delimiter, and inserting the data on either side of the space into Col2 and Col3 respectively.
I am not too sure how to proceed. Can this be accomplished in SQL, or should I create a small program to do the work for me? I'd appreciate a pointer in the right direction if this can be accomplished via SQL.
Thanks.

UPDATE table SET
Col2 = SUBSTRING(Col1, 1, CHARINDEX(' ', Col1)-1),
Col3 = SUBSTRING(Col1, CHARINDEX(' ', Col1)+1, 8000)
WHERE Col1 LIKE '% %';

If you can guarantee there is only one space:
create table #temp (col1 varchar(50),col2 varchar(50), col3 varchar(50))
insert into #temp (col1)
select 'test 1'
union all
select 'test 2'
union all
select 'test 3'
update #temp
set col2 = left(col1, charindex (' ', col1)),
col3 = substring(col1,charindex (' ', col1)+1, len(col1))
from #temp

Related

Replicating VBA solution in SQL

I have a SQL query that provides me with the output in the 'Output Before' pic.
Column 5 if essentially doing a countif on column1 for items in column1, similar to Excel.
I would like to add some code/sub query, so that the output becomes like the 'Output After' pic.
Does anyone have any ideas how I can do this?
I can do it in excel with VBA but just cant get my head around how to do it in SQL.
Output Before
Output After
Since you are using SQL Server 2017 you can get what you want with STRING_AGG:
select column1, column2, string_agg(column3, '&') as column3, column4
from outputbefore
group by column1, column2, column4
You're looking to concatenate multiple rows into a single value. Your options depend on your version of SQL Server. In older versions (I think 2005+) you have to use a torturous XML procedure. Run this on your server and you'll see how it works, but I'll leave it to you to work out the query for your data:
SELECT STUFF(
(SELECT ', <' + name + '>'
FROM sys.databases
WHERE database_id > 4
ORDER BY name
FOR XML PATH('') ,
ROOT('MyString') ,
TYPE
).value('/MyString[1]', 'varchar(max)'), 1, 2, '') AS namelist;
As of SQL 2017 you can use the STRING_AGG function, explained here.
As I see this is a string concatenation issue. I used a replace to handle the character & in XML.
select a.col1, Col3=replace(stuff((SELECT '#' + b.col3 AS 'data()'
FROM OutputBefore) b
where b.Col1=a.Col1
FOR XML PATH('')),1,1,''),'#','&')
from (select distinct Col1 from OutputBefore) a;
As mentioned by forpas and Russell, as of SQL 2017 you're able to use STRING_AGG function.
For SQL 2008+
Refer back to this:
How Stuff and 'For Xml Path' work in Sql Server
In your case you want the delimiter to be '&' which will cause a problem with FOR XML PATH due to XML Special character. So you'll want to escape XML Special characters, example:
DECLARE #TableA TABLE (Col1 NVARCHAR(10), Col2 INT, Col3 NVARCHAR(10), Col4
NVARCHAR(10), Col5 INT)
INSERT INTO #TableA (Col1, Col2, Col3, Col4, Col5)
VALUES ('Dave' , 24 , 'house' , 'married' , 2)
, ('Dave' , 24 , 'car' , 'married' , 2)
, ('Bob' , 32 , 'House' , 'single' , 1)
, ('George' , 12 , 'house' , 'divorced' , 1)
SELECT
t2.Col1
, t2.Col2
, STUFF ( ( SELECT '&' + Col3 -- Adding '&' as delimited
FROM #TableA t1
WHERE t1.Col2 = t2.Col2
FOR XML PATH (''), TYPE
).value('.', 'VARCHAR(MAX)'),1,1,''-- To escape special characters
) AS Col3
, t2.Col4
FROM #TableA AS t2
GROUP BY t2.Col1
, t2.Col2
, t2.Col4

SQL Server : Reuse calculated variable in select clause

I have the following table structure:
col1 col2 col3 col4
-------------------------------
aK Mbcd ABc defgh
col2, col3 and col4 columns are of type varchar(100) and col1 has type varchar(500).
I need a select query to have the output as following
col1 col2 col3 col4
-------------------------------
aK,Mb cd,A Bc,d efgh
Logic is explained as mentioned below:
In the result, Col2, col3 and col4 can have maximum 4 characters but col1 can have more than 4 characters upto 100.
If any column has more characters, last 4 characters will be retained in the same column and other extra columns will be concatenated with previous column's value separated by comma , and the same rule will be applied on the concatenated values as well.
I've written the following T-SQL statement. It works fine for last two columns. But I want to use new calculated value of col3 to strip out extra characters after adding some from col4
SELECT
CASE
WHEN X.Col4Length > 4
THEN concat(X.col3, ',', substring(x.col4, 0, X.Col4Length - 3))
ELSE X.col3
END AS col3,
CASE
WHEN X.Col4Length > 4
THEN substring(x.col4, X.Col4Length - 3, x.Col4Length)
ELSE X.col4
END AS col4
FROM
(SELECT
Col1, Col2, Col3, Col4,
Len(Col1) AS Col1Length,
Len(Col2) AS Col2Length,
Len(Col3) AS Col3Length,
Len(Col4) AS Col4Length
FROM
mytable) X
My try with a simple sub-query
with t1 as (
select 'aK' col1, 'Mbcd' col2, 'ABc' col3, 'defgh' col4
---
SELECT LEFT(col, LEN(col) - 12) col1,
RIGHT(LEFT(col, LEN(col) - 8), 4) col2,
RIGHT(LEFT(col, LEN(col) - 4), 4) col3,
RIGHT(col, 4) AS col4
FROM
(
SELECT col1+','+col2+','+col3+','+col4 AS col
FROM t1
) t;
You want to reuse calculated variables
There are two set-based /inline / adhoc approaches (and many more ugly procedural):
CTEs to do this for the whole set in advance
CROSS APPLY for the same on row level
Try it like this (CTE approach)
DECLARE #tbl TABLE(col1 VARCHAR(100),col2 VARCHAR(100),col3 VARCHAR(100),col4 VARCHAR(100));
INSERT INTO #tbl VALUES
('aK','Mbcd','ABc','defgh')
,('123456','abc','3456','123456789');
WITH ResolveCol4 AS
(
SELECT *
,RIGHT(col4,4) AS Col4_resolved
,col3 + ',' + CASE WHEN LEN(col4)>4 THEN SUBSTRING(col4,1,LEN(col4)-4) ELSE '' END AS col3_New
FROM #tbl
)
,ResolveCol3 AS
(
SELECT *
,RIGHT(col3_New,4) AS Col3_resolved
,col2 + ',' + CASE WHEN LEN(col3_New)>4 THEN SUBSTRING(col3_New,1,LEN(col3_New)-4) ELSE '' END AS col2_New
FROM ResolveCol4
)
,ResolveCol2 AS
(
SELECT *
,RIGHT(col2_New,4) AS Col2_resolved
,col1 + ',' + CASE WHEN LEN(col2_New)>4 THEN SUBSTRING(col2_New,1,LEN(col2_New)-4) ELSE '' END AS col1_New
FROM ResolveCol3
)
SELECT col1_new,Col2_resolved,Col3_resolved,Col4_resolved
FROM ResolveCol2
The result
aK,Mb cd,A Bc,d efgh
123456,abc,34 56,1 2345 6789

Combine columns from three different tables into a single column

I am new to SQL and I am not sure what to Google. I have three tables with different numbers of columns. I would like to combine these following three tables into a single column(no duplicates).
Table1
Col1 Col2 Col3
1 a aa
2 b ab
3 c bb
Table2
Col1 Col2
123 Test
456 Test2
346 Test3
Table3
Col1 Col2 Col3 Col4
5695 93234 ABC CDE
4534 92349 MSF KSK
3244 12323 SLE SNE
Expected Output:
FileOutput
1aaa
123Test
569593234ABCCDE
2bab
456Test2
453492349MSFKSK
...
Any help would be much appreciated. Thanks!
The term you would want to Google would be: UNION and CONCAT.
Note: CONCAT is not supported in prior versions to SQL Server 2012.
To get your expected output, I would do this:
select
concat(cast(col1 as varchar(10)),col2,col3) as FileOutput
from table1
UNION
select
concat(cast(col1 as varchar(10)),col2) as FileOutput
from table2
UNION
select
concat(cast(col1 as varchar(10)),cast(col2 as varchar(10)),col3,col4) as FileOutput
from table3
SQL Fiddle Demo
Not sure how you would parse the data, but you could do this:
select convert(varchar(100), col1) + convert(varchar(100), col2) + convert(varchar(100), col3) as fileOutput
from table1
union all
select convert(varchar(100), col1) + convert(varchar(100), col2) as fileOutput
from table2
union all
select convert(varchar(100), col1) + convert(varchar(100), col2) +
convert(varchar(100), col3) + convert(varchar(100), col4) as fileOutput
from table4
note not knowing your column data types, your varchar(100) may need to expand, or could potentially shrink depending on your data.
You can combine them using + (may need to cast your ints as varchars for this to work), then put them all in one table using union all. Example:
Select cast(col1 as varchar(100)) + col2 + col3
from Table1
union all
select cast(col1 as varchar(100)) + col2
from Table2
etc.
Note: be sure to use union all rather than union if you want to keep any duplicates you may create.

string or binary data would be truncated. in sql server

How to overcome this?
I have insert statements for large tables. there were lot of columns in those tables.
Is there any simple way to find the column which is causing above error?
The simplest way is to increase the length of the column which is giving you the error "string or binary data would be truncated. in sql server".
Now to find which column is giving that error you can check How to find what column caused the String or binary data would be truncated message
First thing we need to know is what the columns are in the table that are (n)chars or (n)varchar
select column_name
from information_schema.columns
where table_name = 'temp'
and data_type in('varchar','char','nvarchar','nchar')
Output:
column_name
————–
Col1
Col2
Col3
Col4
Col5
That was easy, now we want to know the max length of the data in each column
declare #sql varchar(8000)
select #sql = 'select 0 as _col0 ,'
select #sql += 'max(len( ' + column_name+ ')) AS ' + column_name + ','
from information_schema.columns
where table_name = 'temp'
and data_type in('varchar','char','nvarchar','nchar')
select #sql = left(#sql,len(#sql) -1)
select #sql +=' into MaxLengths from temp'
--select #sql -debugging so simple, a caveman can do it
exec (#sql)
That code basically creates and runs the following
select 0 as _col0 ,
max(len( Col1)) AS Col1,
max(len( Col2)) AS Col2,
max(len( Col3)) AS Col3,
max(len( Col4)) AS Col4,
max(len( Col5)) AS Col5
into MaxLengths
from temp
If we now look in the MaxLengths table we will see the following
select * from MaxLengths
_col0 Col1 Col2 Col3 Col4 Col5
---------------------------------------------------
0 13 20 6 4 15
Next to figure out is what the max length of the column itself is in the table that we want to insert into
Run the following query
select character_maximum_length,column_name
from information_schema.columns
where table_name = 'TestTrunc'
and data_type in('varchar','char','nvarchar','nchar')
Result
character_maximum_length column_name
--------------------------------------------
10 Col1
15 Col2
20 Col3
3 Col4
10 Col5
We will again do this dynamically and insert the values into another table
declare #sql varchar(8000)
select #sql = 'select 0 as _col0, '
select #sql += '' + convert(varchar(20),character_maximum_length)+ ' AS ' + column_name + ','
from information_schema.columns
where table_name = 'TestTrunc'
and data_type in('varchar','char','nvarchar','nchar')
select #sql = left(#sql,len(#sql) -1)
select #sql +=' into TempTrunc '
--select #sql -debugging so simple, a caveman can do it
exec (#sql)
Now we can see what we have in the two tables
select 'TempTrunc' as TableNAme,* from TempTrunc
union all
select 'MaxLengths' as TableNAme,* from MaxLengths
TableNAme _col0 Col1 Col2 Col3 Col4 Col5
-------------------------------------------------------------
TempTrunc 0 10 15 20 3 10
MaxLengths 0 13 20 6 4 15
As you can see, all columns except for Col3 will cause the truncation problem
Of course we want to do something like this, it will tell us which columns have truncation problems
select case when t.col1 > tt.col1 then 'truncation' else 'no truncation' end as Col1,
case when t.col2 > tt.col2 then 'truncation' else 'no truncation' end as Col2,
case when t.col3 > tt.col3 then 'truncation' else 'no truncation' end as Col3,
case when t.col4 > tt.col4 then 'truncation' else 'no truncation' end as Col4,
case when t.col5 > tt.col5 then 'truncation' else 'no truncation' end as Col5
from MaxLengths t
join TempTrunc tt on t._col0 = tt._col0
Col1 Col2 Col3 Col4 Col5
------------------------------------------------------------------------------------
truncation truncation no truncation truncation truncation
Source
I believe you already know that this is related with inserting a string value into a column with less size
You can query table columns from know table names and compare them.
select
OBJECT_NAME(object_id),
name,
max_length,
precision,
scale
from sys.columns where object_id in (
select object_id from sys.tables where name in ('tbl','Emp')
)

How can I combine multiple rows into one during a Select?

I'm joining a bunch of tables and then inserting that data into a table variable. I then SELECT those records from the table. The data looks like this:
As you can see from the example, the unique data is only in column 7 and 8. In this example, there's only two rows. But it can be an infinite number. So instead of sending a bunch of rows to the client and then sorting out the data, I want to do it in SQL and only send back one row.
Since all of the data is the same, except for two columns, I wanted to concatenate the data and separate them by commas. This will make the client side operations much easier.
So in the end, I'll have one row and Col7 and Col8 will look like this:
Any ideas on how to accomplish this task?
You could try using FOR XML:
SELECT STUFF((SELECT',' + Col7 FROM #test
FOR XML PATH('')), 1, 1, '' ) as col7
Assuming you want to collapse the entire table into a single row, and discard the data in columns like ID:
DECLARE #x TABLE(Col7 varchar(255), Col8 varchar(255));
INSERT #x SELECT 'foo','bar'
UNION ALL SELECT 'blat','splunge';
SELECT Col7 = STUFF((SELECT ',' + Col7 FROM #x FOR XML PATH(''),
TYPE).value(N'./text()[1]', N'varchar(max)'), 1, 1, ''),
Col8 = STUFF((SELECT ',' + Col8 FROM #x FOR XML PATH(''),
TYPE).value(N'./text()[1]', N'varchar(max)'), 1, 1, '');
Result:
Col7 Col8
-------- -----------
foo,blat bar,splunge
On SQL Server 2017+, it is much simpler:
SELECT Col7 = STRING_AGG(Col7, ','),
Col8 = STRING_AGG(Col8, ',')
FROM #x;