SQL Server CSV per row - sql-server

I have Data Like:
StudentID | Course
1 | .NET
1 | SQL Server
1 | Ajax
2 | Java
2 | JSP
2 | Struts
I want the query to get the Output data Like the following.
StudentID | Course
1 | .NET, SQL Server, Ajax
2 | Java, JSP, Struts

In SQL Server 2005+, the easiest method is the to use the FOR XML trick:
SELECT StudentID, STUFF((SELECT ',' + Course
FROM table t1
WHERE t1.StudentID = t.StudentID
FOR XML PATH('')), 1, 1, '')
FROM table t

In SQLServer2000+ you can use following
create table tbl (StudentID int, course varchar(10))
insert into tbl values (1,'.NET'),(1, 'SQL Server'), (1, 'Ajax'),(2,'Java'),(2,'JSP'),(2,'Struts')
GO
CREATE FUNCTION dbo.GetCourses(#id INTEGER)
RETURNS VARCHAR(MAX)
AS
BEGIN
DECLARE #Result VARCHAR(MAX)
SET #Result = ''
SELECT #Result = #Result + [course] + ' ' FROM tbl WHERE StudentID = #id
RETURN RTRIM(#Result)
END
GO
SELECT DISTINCT StudentID, dbo.GetCourses(StudentID) FROM tbl
GO
drop table tbl
drop function dbo.GetCourses

Related

SQL Server : select merge all values with same keys

I have a table in SQL Server that looks like this:
id: int
key: nvarchar(max)
value: nvarchar(max)
I want to select distinct keys as first column and all values with same key joined with '<br/>' as my second column. The key values are dynamic and is not predefined - and the performance really matters - I don't want to use Linq or any UDF!
id key value
---------------------------------------
1 color red<br/>white<br/>black
4 size 15"
PS: I have searched a lot sorry if it's duplicated, currently running on SQL Server 2014 but I can move to 2019
Please try the following solution. It will work starting from SQL Server 2008 onwards.
SQL
-- DDL and sample data population, start
DECLARE #tbl TABLE (ID INT IDENTITY PRIMARY KEY, [key] NVARCHAR(MAX), [value] NVARCHAR(MAX));
INSERT INTO #tbl ([key], [value]) VALUES
(N'color', N'red'),
(N'color', N'white'),
(N'color', N'black'),
(N'size', N'15"');
-- DDL and sample data population, end
DECLARE #separator CHAR(5) = '<br/>'
, #encoded VARCHAR(20) = '<br/>';
SELECT c.[key]
, STUFF(REPLACE(
(SELECT #separator + CAST([value] AS NVARCHAR(MAX)) AS [text()]
FROM #tbl AS O
WHERE O.[key] = C.[key]
FOR XML PATH('')
), #encoded, #separator)
, 1, LEN(#separator), NULL) AS valueList
FROM #tbl AS c
GROUP BY c.[key];
Output
+-------+-------------------------+
| key | valueList |
+-------+-------------------------+
| color | red<br/>white<br/>black |
| size | 15" |
+-------+-------------------------+

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 |
+------------------+----------------+-----+-------+---------+

Pivot and concatenate values from column in SQL Server

I have table with these columns:
ID | Name | Value
------------------
1 | Test1 | 0
2 | Test2 | 1
3 | Test3 | 0
4 | Test4 | 0
5 | Test5 | 1
And I want to have pivoted and concatenated value column as string
01001
The below code will give the expected result:
SELECT #Result = #Result + CAST(VALUE AS VARCHAR)
FROM #TmpTestingTable
Or you can use the STUFF:
SELECT STUFF(
( SELECT CAST(VALUE AS VARCHAR)
FROM #TmpTestingTable
FOR XML PATH ('')
), 1, 0, '')
For sample, I inserted the columns into the temporary table and execute the code.
CREATE TABLE #TmpTestingTable (ID INT, Name VARCHAR (20), Value INT)
INSERT INTO #TmpTestingTable (ID, Name, Value) VALUES
(1 , 'Test1' , 0),
(2 , 'Test2' , 1),
(3 , 'Test3' , 0),
(4 , 'Test4' , 0),
(5 , 'Test5' , 1)
DECLARE #Result AS VARCHAR (100) = '';
-- using variable approach
SELECT #Result = #Result + CAST(VALUE AS VARCHAR)
FROM #TmpTestingTable
SELECT #Result
-- using STUFF approach
SELECT STUFF(
( SELECT CAST(VALUE AS VARCHAR)
FROM #TmpTestingTable
FOR XML PATH ('')
), 1, 0, '')
DROP TABLE #TmpTestingTable
Use FOR XML to concatinate. It is important that you also include an ORDER BY. Otherwise you have no control of the order of the values and you risk an arbitrary order.
SELECT
(SELECT CAST([VALUE] AS CHAR(1))
FROM yourtable
ORDER BY ID
FOR XML PATH ('')
)
SELECT GROUP_CONCAT(Value SEPARATOR '') FROM Table
EDIT:
Not working on SQL Server. Have a look at Simulating group_concat MySQL function in Microsoft SQL Server 2005? to try to make it work

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

Assigning select results into variable in stored procedure in mssql?

I have the following select:
SELECT School_Type,COUNT(ID) from Schools where City_ID = 1 group by School_Type
I get results:
10 | 3
20 | 4
30 | 14
I want to put results that are:
type 10 to variable #ElementarySchools
type 20 to variable #HighSchools
type 30 to variable #ProfessionalSchools
and get this result back from the Stored Procedure.
How do I do this ?
something like this? :)
declare #val varchar(max) = ''
select #val = #val + rtrim(foryear) + ' | ' + RTRIM( COUNT(*)) + ',' from mytable
group by ForYear
select #val
Using a table variable like this:
declare #tmp table (School_Type int, School_Count int)
insert into #tmp
SELECT School_Type,COUNT(ID) from Schools where City_ID = 1 group by School_Type
select #ElementarySchools=School_Count from #tmp where School_Type=10
select #HighSchools=School_Count from #tmp where School_Type=20
select #ProfessionalSchools=School_Count from #tmp where School_Type=30

Resources