Concat rows into 1 string - sql-server

I am using SQL Server 2005 and would like to find an easier way to concat multiple rows into 1 string.
PK Column1, Column2
-- ------- -------
PK1 apple orange
PK1 pear banana
PK1 honey
PK2 apple2 orange2
PK2 pear2 banana2
PK2 honey2
Results :
PK1, apple orange pear banana honey
PK2, apple2 orange2 pear2 banana2 honey2
It is very easy to use COALESCE but it is not available in SQL Server 2005. I tried XML Path but it appends additional character to the end.
Could you please provide me some suggestion ? Thanks.

A far easier solution is to use XML PATH
SELECT #cols = STUFF((SELECT DISTINCT ',' + QUOTENAME(a.Column1)
FROM dbo.mytbl AS a WHERE a.ColumnX = somecondition
FOR XML PATH ( '' ) , TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '')
SELECT #cols
Of course the WHERE clause is optional in your case. And without spoon feeding you, apply the same to your other column and concatenate them. Voila!

When your data is some blank spaces ' ' in result of for xml path you will see a at the end like ' ':
select ' ' for xml path ('');
In your case I can use this query:
select t.PK,
ltrim(rtrim(replace(
(select ' ' + isnull(ti.Column1, '') + ' ' + isnull(ti.Column2, '')
from yourTable ti
where ti.PK = t.PK
for xml path (''))
, ' ', ''))) fruits
from yourTable t
group by t.PK;

This will trap null or empty values
Declare #YourTable table (PK int,Column1 varchar(25), Column2 varchar(25))
Insert Into #YourTable values
(1,'apple','orange'),
(1,'pear','banana'),
(1,'honey', null),
(2,'apple2','orange2'),
(2,'pear2','banana2'),
(2,'honey2', null)
Select PK
,DelimString = Stuff((Select case when Column1 is null or Column1='' then '' else ' ' + replace(Column1,char(13),'') end
+case when Column2 is null or Column2='' then '' else ' ' + replace(Column2,char(13),'') end
From #YourTable
Where PK=A.PK
For XML Path('')), 1, 1, '')
From #YourTable A
Group By PK
Returns
PK DelimString
1 apple orange pear banana honey
2 apple2 orange2 pear2 banana2 honey2

Related

Extracting data from xml into SQL server table

My xml is in below format :
[My company customer detail - Account ID <3116131311616116>, Subscriber Name <Jon>, Age <52>, Phone <>, Payment<CC>]
I am unable to transfer data from xml to SQL server table columns. columns name like Account ID, Subscriber Name, Age , Phone ,Payment
Detail of xml mentions above seems like string. XML(String) is in column like below :
enter image description here
I need to extract data from detail column and push data into new table with attributes from xml/string.
Here's a super-duper hack that's not limited to a set number of "columns":
-- Data mock-up.
DECLARE #value VARCHAR(500) = '[My company customer detail - Account ID <3116131311616116>, Subscriber Name <Jon>, Age <52>, Phone <>, Payment<CC>]';
-- Construct the dynamic SQL field/value list.
DECLARE #sql VARCHAR(MAX);
SELECT #sql = ISNULL( #sql, '' )
+ ', ' + QUOTENAME( SUBSTRING( value, CHARINDEX( '<', value ) + 1, CHARINDEX( '>', value ) - CHARINDEX( '<', value ) - 1 ), '''' )
+ ' AS [' + LTRIM( RTRIM( LEFT( value, CHARINDEX( '<', value ) - 1 ) ) ) + ']'
FROM STRING_SPLIT( REPLACE( REPLACE( #value, ']', '' ), '[', '' ), ',' )
-- Complete the dynamic SQL.
SET #sql = 'SELECT ' + STUFF( #sql, 1, 2, '' ) + ';';
-- Print the resulting dynamic SQL.
PRINT #sql;
-- Execute the dynamic SQL.
EXEC( #sql );
PRINT displays:
SELECT '3116131311616116' AS [My company customer detail - Account ID], 'Jon' AS [Subscriber Name], '52' AS [Age], '' AS [Phone], 'CC' AS [Payment];
EXEC returns:
+-----------------------------------------+-----------------+-----+-------+---------+
| My company customer detail - Account ID | Subscriber Name | Age | Phone | Payment |
+-----------------------------------------+-----------------+-----+-------+---------+
| 3116131311616116 | Jon | 52 | | CC |
+-----------------------------------------+-----------------+-----+-------+---------+
By "transfer data from xml to SQL server table columns" I am assuming you mean insert to sql server table. if so this means that parsing the xml is done at the application and you should then construct a string in the form of insert..values, but surly more information is needed.
Another option can be to pass the xml as an input parameter to the stored procedure, parse the xml within the procedure and insert the table.
The more info you provide will help to answer your issue better.
The 2 options are valid and this is a matter of design and preference. In general if the xml you are dealing with is large then doing the parsing at the application is preferred.
You can use such a query
SELECT CAST([XMLData] AS XML).value('(/MyCompany/AccountID)[1]', 'VARCHAR(MAX)') AS "Account ID",
CAST([XMLData] AS XML).value('(/MyCompany/SubscriberName)[1]', 'VARCHAR(MAX)') AS "Subscriber Name",
CAST([XMLData] AS XML).value('(/MyCompany/Age)[1]', 'VARCHAR(MAX)') AS "Age",
CAST([XMLData] AS XML).value('(/MyCompany/Phone)[1]', 'VARCHAR(MAX)') AS "Phone",
CAST([XMLData] AS XML).value('(/MyCompany/Payment)[1]', 'VARCHAR(MAX)') AS "Payment"
FROM tab
assuming you have a column [XMLData] within the table of type VARCHAR with the value
<MyCompany>
<AccountID>3116131311616116</AccountID>
<Phone></Phone>
<Age>52</Age>
<SubscriberName>Jon</SubscriberName>
<Payment>CC</Payment>
</MyCompany>
depending of the provided data.
If the column is of type XML, then casting is not needed, e.g. use as [XMLData].value
Demo
Please try the following solution.
First, garbled string is converted to XML. The moment it is XML the rest is trivial.
SQL
DECLARE #unstructured VARCHAR(max) = '[My company customer detail - Account ID <3116131311616116>, Subscriber Name <Jon>, Age <52>, Phone <>, Payment<CC>]';
DECLARE #separator CHAR(1) = '>'
, #lt CHAR(1) = '<';
;WITH rs AS
(
SELECT TRY_CAST('<root><r><![CDATA[' +
REPLACE(#unstructured, #separator, ']]></r><r><![CDATA[') +
']]></r></root>' AS XML) AS xmldata
), cte AS
(
SELECT c.value('(r[1]/text())[1]','VARCHAR(100)') AS col1
, c.value('(r[2]/text())[1]','VARCHAR(30)') AS col2
, c.value('(r[3]/text())[1]','VARCHAR(30)') AS col3
, c.value('(r[4]/text())[1]','VARCHAR(30)') AS col4
, c.value('(r[5]/text())[1]','VARCHAR(30)') AS col5
FROM rs CROSS APPLY xmldata.nodes('/root') AS t(c)
)
SELECT STUFF(col1, 1, CHARINDEX(#lt, col1,1), '') AS AccountID
, STUFF(col2, 1, CHARINDEX(#lt, col2,1), '') AS SubscriberName
, STUFF(col3, 1, CHARINDEX(#lt, col3,1), '') AS Age
, STUFF(col4, 1, CHARINDEX(#lt, col4,1), '') AS Phone
, STUFF(col5, 1, CHARINDEX(#lt, col5,1), '') AS Payment
FROM cte;
Output
+------------------+----------------+-----+-------+---------+
| AccountID | SubscriberName | Age | Phone | Payment |
+------------------+----------------+-----+-------+---------+
| 3116131311616116 | Jon | 52 | | CC |
+------------------+----------------+-----+-------+---------+

How to get "," instead of "and" in the rows in SQL Server

I have a table Test with 1 column
Module_name
Table
Computer
Laptop
Chair
My expected output:
Table,Computer,Laptop and Chair
My Query:
declare #module_name varchar(50)
SELECT #Module_Name = COALESCE(#Module_Name + ' and ', '') + module_name FROM
(SELECT DISTINCT module_name FROM Test) T
select #module_name
I am getting the output as:
Table and Computer and Laptop and Chair
My concern is how to get the "," instead of "and".
Have you tried xml method with stuff() function ?
declare #Module_names varchar(max)
set #Module_names = stuff((select distinct ',' +Module_name
from table t
for xml path('')),1,1, '')
select REVERSE(STUFF(REVERSE(#Module_names),
CHARINDEX(',', REVERSE(#Module_names)), 1,' dna ')) as Module_names
I don't endorse this solution, like I said in the comments, "grammarisation" should be done in your presentation layer.. You can, however, achieve this in SQL like so:
Edit: Slight update to cater for a single value return.
CREATE TABLE #Sample (Module varchar(10));
INSERT INTO #Sample
VALUES ('Table'),
('Computer'),
('Laptop'),
('Chair');
GO
WITH RNs AS (
SELECT Module,
ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS RN --SELECT NULL as there is no ID field to work with here, thus the order will be random
FROM #Sample)
SELECT STUFF((SELECT CASE WHEN RN = MAX(RN) OVER () AND RN != 1 THEN ' and ' ELSE ', ' END + Module
FROM RNs
ORDER BY RN
FOR XML PATH('')),1,2,'');
GO
DROP TABLE #Sample;
Use the following. First gather all records together with comma, then replace just the last one with "and". Will have to make sure that your column values don't contain comma or it will be misplaced with an "and" if on last occurence.
DECLARE #result VARCHAR(MAX) = STUFF(
(
SELECT DISTINCT
', ' + T.module_name
FROM
Test AS T
FOR XML
PATH('')
),
1, 2, '')
SET #result =
REVERSE(
STUFF( -- Replace
REVERSE(#result), -- ... in the reversed string
CHARINDEX(',', REVERSE(#result)), -- ... at the first position of the comma (the last one on the original string)
1, -- just 1 character (the comma)
'dna ') -- for the reversed " and"
)
SELECT #result
Used Row_number to capture last row,
CREATE TABLE test
([Module_name] varchar(8))
;
INSERT INTO test
([Module_name])
VALUES
('Table'),
('Computer'),
('Laptop'),
('Chair')
;
SELECT STUFF((SELECT CASE WHEN RN = MAX(RN) OVER () THEN ' and ' ELSE ', ' END + Module_name
from
(
SELECT Module_name,
ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS RN
FROM test
) rns
ORDER BY RN
FOR XML PATH('')),1,2,'');

Convert MSAccess Cross tab to MSSQL [duplicate]

I have the following crosstab query in Access:
Transform Count(1) as Count
Select Cust,[Cust#],EntryDate,CloseDate
from Tbl1,Dates
where EntryDate>=[start date]
Group by Cust,[Cust#],EntryDate,CloseDate
Order by EntryDate
Pivot Quote;
I am having difficulty converting this to T-SQL.
Should I be using SSIS for Pivot transformation in order to solve this,
or do we have an equivalent SQL Server query for this?
We don't really have enough information to convert that specific crosstab query, so here is a simple example that may help you achieve your goal:
For a table named [Vehicles] containing...
VehicleID VehicleMake VehicleModel VehicleType
--------- ----------- ------------ ------------
1 Ford Focus Compact car
2 Ford F-150 Pickup truck
3 Dodge RAM 1500 Pickup truck
4 Toyota Tundra Pickup truck
5 Toyota Prius Hybrid car
6 Toyota Tacoma Pickup truck
...the Access crosstab query...
TRANSFORM Count(Vehicles.VehicleID) AS CountOfVehicleID
SELECT Vehicles.VehicleType
FROM Vehicles
GROUP BY Vehicles.VehicleType
PIVOT Vehicles.VehicleMake;
...returns:
VehicleType Dodge Ford Toyota
------------ ----- ---- ------
Compact car 1
Hybrid car 1
Pickup truck 1 1 2
The following T-SQL script accomplishes the same thing
DECLARE
#ColumnList AS NVARCHAR(MAX),
#SQL AS NVARCHAR(MAX)
-- build the list of column names based on the current contents of the table
-- e.g., '[Dodge],[Ford],[Toyota]'
-- required by PIVOT ... IN below
-- ref: http://stackoverflow.com/a/14797796/2144390
SET #ColumnList =
STUFF(
(
SELECT DISTINCT ',' + QUOTENAME([VehicleMake])
FROM [Vehicles]
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)'),
1,
1,
'')
SET #SQL = '
WITH rollup
AS
(
SELECT VehicleMake, VehicleType, COUNT(VehicleID) AS n FROM [Vehicles]
GROUP BY VehicleMake, VehicleType
)
SELECT * FROM rollup
PIVOT (SUM([n]) FOR [VehicleMake] IN (' + #ColumnList + ')) AS Results'
EXECUTE(#SQL)
It returns:
VehicleType Dodge Ford Toyota
------------ ----- ---- ------
Compact car NULL 1 NULL
Hybrid car NULL NULL 1
Pickup truck 1 1 2
drop table #tmpT1
select distinct UserField2 into #tmpT1 from CreateExcelForm
--select * from #tmpT1
DECLARE #PivotColumnHeaders VARCHAR(MAX)
SELECT #PivotColumnHeaders =
COALESCE(
#PivotColumnHeaders + ',[' + cast(UserField2 as varchar) + ']',
'[' + cast(UserField2 as varchar)+ ']'
)
FROM #tmpT1
print(#PivotColumnHeaders)
DECLARE #PivotTableSQL NVARCHAR(MAX)
SET #PivotTableSQL = N'
SELECT *
FROM (
SELECT
* from CreateExcelForm
) AS PivotData
PIVOT (
max(StockCode)
FOR UserField2 IN (
' + #PivotColumnHeaders + '
)
) AS PivotTable
'
EXECUTE(#PivotTableSQL)

How to coalesce many rows into one?

I am using SSMS 2008 R2 and am simply trying to coalesce many rows into one. This should be simple I think, but it is currently repeating data in each row. Consider:
create table test
(
Name varchar(30)
)
insert test values('A'),('B'),('C')
select * from test
select distinct Name, coalesce(Name + ', ', '')
from test
How can I rewrite this to achieve one row like:
A,B,C
SELECT STUFF(( SELECT ', ' + Name
from #test
FOR XML PATH(''), TYPE).
value('.','NVARCHAR(MAX)'),1,2,'')
RESULT: A, B, C
I am sure this not how exactly your rows look that you are trying to concatenate, therefore see below for a slightly different data set and how you would go about doing this kind of operation on that
Test Data
create table #test
(
Id INT,
Name varchar(30)
)
insert #test values
(1,'A'),(1,'B'),(1,'C'),(2,'E'),(2,'F'),(2,'G')
Query
select t.Id
, STUFF(( SELECT ', ' + Name
from #test
WHERE Id = T.Id
FOR XML PATH(''), TYPE).
value('.','NVARCHAR(MAX)'),1,2,'') AS List
FROM #test t
GROUP BY t.Id
Result Set
╔════╦═════════╗
║ Id ║ List ║
╠════╬═════════╣
║ 1 ║ A, B, C ║
║ 2 ║ E, F, G ║
╚════╩═════════╝
In SQL Server Transact-SQL, the easiest way to accomplish this is the following.
A table like this:
create table #foo
(
id int not null identity(1,1) primary key clustered ,
name varchar(32) not null ,
)
insert #foo (name) values ( 'a' )
insert #foo (name) values ( 'b' )
insert #foo (name) values ( 'c' )
insert #foo (name) values ( 'd' )
go
Can flattened using this seemingly dubious (but documented) technique:
declare #text varchar(max) = ''
select #text = #text
+ case len(#text)
when 0 then ''
else ','
end
+ t.name
from #foo t
select list_of_names = #text
go
yielding
list_of_names
-------------
a,b,c,d
Easy!
in the old days of SQL Server 7.0 and SQL Server 2000, I Used to do this with a variable and using COALESCE like this:
DECLARE #List VARCHAR(8000)
SELECT #List = COALESCE(#List + ',', '') + CAST(Color AS VARCHAR)
FROM NameColorTable
SELECT #List
But after SQL Server 2005 and XPATH appearance, this the way I prefer to use:
SELECT STUFF((
select ','+ cast(Color as nvarchar(255))
from NameColorTable b
WHERE a.Name = b.Name
FOR XML PATH('')
)
,1,1,'') AS COLUMN2
FROM NameColorTable a
GROUP BY a.Name
I have a blog post about this here:
https://koukia.ca/stuff-vs-coalesce-in-tsql-for-concatenating-row-values-aefb078536f8#.f4iggl22y
Here is the standard solution for concatenation using XML PATH()
SELECT
STUFF(
(
SELECT
',' + Name
FROM test
FOR XML PATH(''),TYPE
).value('.','VARCHAR(MAX)'
), 1, 1, ''
) As list
Another option can be using the new SQL CONCAT() function introduced with SQL Server 2012.
I used SQL Concat() in the below sample
declare #namelist nvarchar(max)
select #namelist = concat(isnull(#namelist+',',''), name) from test2
select #namelist

Concatenate columns together, columns can be null

I need to bring values together and copy it to a diffrent column.
COLUMN 1 | COLUMN 2 | COLUMN 3 | COLUMN 4
Hallo out there Hallo out there
My NULL name is My name is
I'm a rabbit I'm a rabbit
How to merge column 1, 2, 3 and copy it to column4 separated with space.
Columns can be null.
UPDATE dbo.table
SET column4 = COALESCE(column1, '')
+ COALESCE(' ' + column2, '')
+ COALESCE(' ' + column3, '');
SQL Server 2012
UPDATE table
SET Column4 = CONCAT(Column1 + ' ', Column2 + ' ', Column3)
Just use + sign
select ISNULL([COLUMN 1],'')+' ' +
isnull([COLUMN 2],'')+' ' +
isnull([COLUMN 3],'')
from your_table
Using ISNULL
UPDATE table
SET Column4 =
ISNULL(Column1+' ','') +
ISNULL(Column2+' ','') +
ISNULL(Column3,'')
Or you could consider using a calculated column.
select isnull(convert(varchar(255),[COLUMN1]),'')+' '+isnull(convert(varchar(255),[COLUMN2]),'')+' '+isnull(convert(varchar(255),[COLUMN3]),'')
from table
UPDATE table
SET COLUMN4 = isnull(convert(varchar(255),[COLUMN1]),'')+' '+isnull(convert(varchar(255),[COLUMN2]),'')+' '+isnull(convert(varchar(255),[COLUMN3]),'')
Above codes may not work depending on datatypes so you may convert it to varchar before.
update yourtable SET COlumn4= ltrim(rtrim(isnull([COLUMN 1],'') +' '+isnull([COLUMN 2],'') +' '+ isnull([COLUMN 3],'')))

Resources