I like to get the SQL Server query output in the following way. Currently my output is:
Class | Student ID | Student Name
------+------------+---------------
121 S1 Test 1
121 S2 Test 2
500 S22 Test a
500 S23 Test b
But I want the data output in the following way -
Class: 121
--------------------------------
Student ID | Student Name
-----------+---------------
S1 Test 1
S2 Test 2
Class: 500
--------------------------------
Student ID | Student Name
-----------+---------------
S22 Test a
S23 Test b
Could someone help me how can I achieve these?
Thanks
While I would agree SQL isn't the place to do your formatting.... sometimes you get stuck....
This Code
select '121' as Class, 'S1 ' as [Student ID], 'Test 1' as [Student Name] into #test
union select '121', 'S2 ', 'Test 2'
union select '500', 'S22 ', 'Test a'
union select '500', 'S23 ', 'Test b'
;
WITH class
AS (
SELECT DISTINCT class
FROM #test
)
,student
AS (
SELECT class
,(
SELECT STUFF((
SELECT CHAR(13) + t.[Student ID] + space(len('Student ID | ') - len(t.[Student ID])) + [Student Name]
FROM #Test t
WHERE t.[class] = class.[class]
FOR XML PATH('')
,type
).value('.[1]', 'nvarchar(max)'), 1, 1, '')
) AS info
FROM class
)
,theoutput
AS (
SELECT 'Class: ' + class + CHAR(13) + '------------------------' + CHAR(13) + 'Student ID | Student Name' + CHAR(13) + info + CHAR(13) + CHAR(13) AS txt
FROM student c
)
SELECT STUFF((
SELECT CHAR(13) + txt
FROM theoutput
FOR XML PATH('')
,type
).value('.[1]', 'nvarchar(max)'), 1, 1, '')
Will Produce
Class: 121
------------------------
Student ID | Student Name
S1 Test 1
S2 Test 2
Class: 500
------------------------
Student ID | Student Name
S22 Test a
S23 Test b
By using While loop we can get the Desired result Created sample data
IF OBJECT_ID('Tempdb..#TEMP') IS NOT NULL
Drop table #TEMP
;With cte(Class, StudentID, StudentName)
AS
(
SELECT 121,'S1' ,'Test 1' UNION ALL
SELECT 121,'S2' ,'Test 2' UNION ALL
SELECT 500,'S22','Test a' UNION ALL
SELECT 500,'S23','Test b'
)
SELECT * INTO #TEMP FROM CTe
SET NOCOUNT ON
DECLARE #CLassCOUnt INT
,#minID INT
,#maxid INT
,#Sql NVARCHAR(max)
,#SelectQuery INT
DECLARE #Table TABLE (
ID INT IDENTITY
,CLass INT
)
INSERT INTO #Table (CLass)
SELECT DISTINCT Class
FROM #TEMP
SELECT #minID = min(Id)
FROM #Table
SELECT #maxid = MAX(Id)
FROM #Table
WHILE (#minID <= #maxid)
BEGIN
SELECT #SelectQuery = Class
FROM #Table
WHERE ID = #minID
SELECT #Sql = 'SELECT Class, StudentID, StudentName FROM #TEMP WHERE Class=' + CAST(#SelectQuery AS VARCHAR)
PRINT 'Result Of Class:'+ CAST(#SelectQuery AS VARCHAR)+CHAR(13)+CHAR(10)+'------------------------'
PRINT #Sql
EXEC (#Sql)
SET #minID = #minID + 1
END
SET NOCOUNT OFF
Related
I have the sample data:
Table: tblsampledata
create table tblsampledata
(
column1 varchar(50),
column2 varchar(50)
);
insert into tblsampledata values('Bob Frapples','Gail Forcewind');
insert into tblsampledata values('Paul Molive','Mario Speedwagon');
And I have column mapping table with table name:
Table: tblmapping
create table tblmapping
(
tblname varchar(100),
columnmap varchar(max)
);
insert into tblmapping values('tblsampledata','[column1]|[column2]');
Note: I want to split the column data which are exists in tblmapping of table name in column tblname and store it into temp table.
Expected Result: #TempTable
column1 column2
---------------------
Bob Gail
Frapples Forcewind
Paul Mario
Molive Speedwagon
You need to use dynamic query to acheive this.
You can try like following.
select #xml = Cast(( '<X>' + Replace(columnmap, '|', '</X><X>') + '</X>' ) AS XML)
from tblmapping where tblname =#tablename
DECLARE #query AS NVARCHAR(max) = 'select ' + Stuff((SELECT DISTINCT ', ' + value
FROM (
SELECT n.value('.', 'varchar(100)') AS value
FROM #xml.nodes('X') AS T(n)
)t
FOR xml path(''), type).value('.', 'NVARCHAR(MAX)'), 1, 1, '')
+ ' from ' + #tablename;
exec sp_executesql #query
Online Demo
To split the column 1 and Column 2 you can use query like following.
SELECT CASE
WHEN n = 1 THEN LEFT(column1, Charindex(' ', column1) - 1)
WHEN n = 2 THEN RIGHT(column1, Charindex(' ', Reverse(column1)) - 1)
END AS column1,
CASE
WHEN n = 1 THEN LEFT(column2, Charindex(' ', column2) - 1)
WHEN n = 2 THEN RIGHT(column2, Charindex(' ', Reverse(column2)) - 1)
END AS column2
FROM tblsampledata t1
CROSS JOIN ( VALUES(1),(2) )t(n)
Full Demo using dynamic query
I have the following table:
+----------------------------------------------------------------------+
| Student Class1 Class1_score Class2 Class2_score Class3.... |
+----------------------------------------------------------------------+
| Alex English 70 Maths 100 NULL |
| John Science 50 NULL NULL NULL |
| Bruce Maths 50 Science 50 English |
+----------------------------------------------------------------------+
Which i want to pivot to something like the following:
+--------------------------+
| Student Class Score |
+--------------------------+
| Alex English 70 |
| Alex Maths 100 |
| Alex Science NULL |
| Alex History NULL |
| John English NULL |
+--------------------------+
Which includes NULL for classes that are not in the original table for that particular student (e.g. Science for Alex)
How can I achieve this in SQL?
Assuming that class type occurs only once in the classX columns:
select student,
'English' class,
coaslesce(case when class1 = 'english' then Class1_score end,
case when class2 = 'english' then Class2_score end,
case when class3 = 'english' then Class3_score end,
case when class4 = 'english' then Class4_score end) score
from your_table
union
select student,
'Maths' class,
coaslesce(case when class1 = 'Maths' then Class1_score end,
case when class2 = 'Maths' then Class2_score end,
case when class3 = 'Maths' then Class3_score end,
case when class4 = 'Maths' then Class4_score end) score
from your_table
union
select student,
'Science' class,
coaslesce(case when class1 = 'Science' then Class1_score end,
case when class2 = 'Science' then Class2_score end,
case when class3 = 'Science' then Class3_score end,
case when class4 = 'Science' then Class4_score end) score
from your_table
union
select student,
'History' class,
coaslesce(case when class1 = 'History' then Class1_score end,
case when class2 = 'History' then Class2_score end,
case when class3 = 'History' then Class3_score end,
case when class4 = 'History' then Class4_score end) score
from your_table
If you want all the Class column values should come for each Student column value, then declare 2 table variables, one for storing unique values of Student column values and other for storing unique values of Class column values. Then do a full outer join between these two tables and use this result set as a subset and join it with another table variable which contains separate rows for each student each class and score. And all these can be done by executing dynamic SQL query.
Query
-- table variabe to store unique class values
declare #sql as varchar(max) = 'declare #tbl_class as table([class] varchar(100));'
+ 'insert into #tbl_class([class]) ';
select #sql += stuff((
select ' union select [' + [column_name] + '] from [' + [table_name] + '] '
+ 'where [' + [column_name] + '] is not null'
from information_schema.columns
where [table_name] = 'your_table_name'
and [column_name] like 'class%[0-9]'
order by [ordinal_position]
for xml path('')
)
, 1, 7, ''
) + ';';
-- table variabe to store unique student values
select #sql += 'declare #tbl_student as table([student] varchar(100));'
+ 'insert into #tbl_student([student]) '
+ 'select distinct [Student] from [your_table_name];';
-- table variable to store student score and class values one by one
select #sql += 'declare #tbl_scores as table'
+ '([student] varchar(100), [class] varchar(100), [score] int);'
+ 'insert into #tbl_scores([student], [class], [score]) ';
select #sql += stuff((
select ' union all select [Student], '
+ '[' + t.[col_1] + '] as [class],'
+ '[' + t.[col_2] + '] as [score] '
+ 'from [' + t.[table_name] + '] '
from (
select [column_name] as [col_1], [column_name] + '_score' as [col_2],
[ordinal_position], [table_name]
from information_schema.columns
where [table_name] = 'your_table_name'
and [column_name] like 'class%[0-9]'
) t
order by t.[ordinal_position]
for xml path('')
)
, 1, 10, ''
) + ';';
-- final result
select #sql += 'select t.[student], t.[Class], tsc.[Score] from('
+ 'select ts.[student], tc.[Class]'
+ ' from #tbl_student as ts '
+ ' full outer join #tbl_class as tc '
+ 'on 1 = 1 ) t '
+ 'left join #tbl_scores as tsc '
+ 'on t.[student] = tsc.[student] '
+ 'and t.[Class] = tsc.[Class];';
exec(#sql);
Find a demo here
You are actually looking for unpivot (wide to tall), rather than pivot. One approach which should work across almost any database uses a series of unions:
SELECT Student, Class, Score
FROM
(
SELECT Student, Class1 AS Class, Class1_score AS Score, 1 AS position
FROM yourTable
UNION ALL
SELECT Student, Class2, Class2_score, 2
FROM yourTable
UNION ALL
SELECT Student, Class3, Class3_score, 3
FROM yourTable
) t
ORDER BY
Student, position;
Sample data
IF OBJECT_ID('dbo.tempdata') IS NOT NULL
DROP TABLE tempdata
;With cte(Student , Class1 , Class1_score, Class2, Class2_score, Class3,Class3_score)
AS
(
SELECT 'Alex' ,'English', 70,'Maths' , 100 , NULL ,NULL UNION ALL
SELECT 'John' ,'Science', 50,NULL ,NULL , NULL ,NULL UNION ALL
SELECT 'Bruce','Maths' , 50,'Science' , 50 , 'English',NULL
)
SELECT * INTO tempdata FROM cte
SELECT * FROM tempdata
By Using Cross apply and dynamic SQL
DECLARE #DynamicColumns NVARCHAR(max)
,#Sql NVARCHAR(max)
;WITH cte
AS
(
SELECT COLUMN_NAME,
DENSE_RANK()OVER(ORDER BY SUBSTRING(COLUMN_NAME,0,7)) As BatchNo
FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='tempdata' AND COLUMN_NAME <>'Student'
)
SELECT #DynamicColumns=STUFF((SELECT ', '+ReqCol FROM
(
SELECT
DISTINCT '('+STUFF((SELECT ', '+COLUMN_NAME
FROM CTE i WHERE i.BatchNo=o.BatchNo
FOR XML PATH ('')),1,1,'') + ')' AS ReqCol
FROM cte o
)dt
FOR XML PATH ('')),1,1,'')
SET #Sql='SELECT Student
,Class
,Score
FROM tempdata
CROSS APPLY (VALUES '+#DynamicColumns+'
) As Dt (Class, Score)
WHERE Class IS NOT NULL
'
PRINT #Sql
EXEC (#Sql)
Result
Student Class Score
---------------------
Alex English 70
Alex Maths 100
John Science 50
Bruce Maths 50
Bruce Science 50
Bruce English NULL
I executed the below query and it executed well :-
SELECT table2id, stuff((select CHAR(13) + table1name from table1 where table1id=table2.table2id FOR XML PATH (''), TYPE
).value('.', 'varchar(max)')
, 1, 1, '')
from table2 where table2id=117 group by id;
But when I am using count(*) , like in below query :-
SELECT table2id, stuff((select CHAR(13) + count(*) from table1 where table1id=table2.table2id FOR XML PATH (''), TYPE
).value('.', 'varchar(max)')
, 1, 1, '')
from table2 where table2id=117 group by id;
I am getting the below error:
Msg 245, Level 16, State 1, Line 19
Conversion failed when converting the varchar value '
' to data type int.
Now how can I stuff all the columns in table1 ? could anyone help !
I want my result like below:-
table2id | table1name | table1id | table1color
------------------------------------------------------
117 | jon, jack | 117,117 | blue,red
( I am adding my sample data for table1 and table2) :-
table1:
table1id | table1name | table1color | table1city | table1animal |...(I
have 25 columns like this !)
--------------------------------------------------------------
117 | jon | blue | city1 | animal1
117 | jack | red | city2 | animal2
table2:
table2id | table2uniqueid
-------------------------
117 | asbn6383hhh3j3837
118 | kf9s8sujfu6df5d7j
This has nothing to do with stuff.
The reason you get the error is this:
count(*) returns an int. char(13) is a char. Whenever you try to do int + char SQL Server will try to implicitly convert the char to an int. Naturally, char(13) cannot be converted to an int.
What you need to to explicitly convert the count(*) to varchar:
SELECT table2id, stuff(
(
select CHAR(13) + cast(count(*) as varchar(10))
from table1
where table1id=table2.table2id
FOR XML PATH (''), TYPE).value('.', 'varchar(max)'), 1, 1, '')
from table2
where table2id=117
group by id;
Try this code it will helps you by using Dynamic Sql
Firstly i created Two physical Tables with sample data
CREATE TABLE Table1 (table1id INT , table1name Varchar(100) , table1color Varchar(100) , table1city Varchar(100) , table1animal Varchar(100))
INSERT INTO Table1
SELECT 117, 'jon' , 'blue' , 'city1' , 'animal1' UNION ALL
SELECT 117, 'jack', 'red' , 'city2' , 'animal2'
CREATE TABLE Table2( table2id INT, table2uniqueid nvarchar(1000))
INSERT INTO Table2
SELECT 117,'asbn6383hhh3j3837' Union all
SELECT 118,'kf9s8sujfu6df5d7j'
Dynamic Sql code to get the expected result
SET NOCOUNT ON
IF OBJECT_ID('Tempdb..#TEMP') IS NOT NULL
DROP TABLE #TEMP
CREATE TABLE #TEMP(ID INT IDENTITY ,Query nvarchar(max))
IF OBJECT_ID('Tempdb..#TEMP2') IS NOT NULL
DROP TABLE #TEMP2
CREATE TABLE #TEMP2(ID INT IDENTITY ,Query nvarchar(max))
DECLARE #MinID INT,
#MaxID INT,
#Sql nvarchar(max),
#Getcolumn nvarchar(max),
#Sql2 nvarchar(max),
#CteSql nvarchar(max),
#CteSql2 nvarchar(max),
#FullSql nvarchar(max)
DEClare #COlumn Table
(
ID INT IDENTITY,
COlumnname varchar(100)
)
INSERT into #COlumn(COlumnname)
SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS Where TABLE_NAME='Table1'
SELECT #MinID=MIn(Id),#MaxID=MAX(ID)FRom #COlumn
While (#MinID <=#MaxID)
Begin
SELECT #Getcolumn=COlumnname From #COlumn WHERE ID=#MinID
SET #Sql=N' STUFF((SELECT '', ''+ CAST('+#Getcolumn +' AS VARCHAR(5))
FROM cte AS i
WHERE i.table1id=o.table1id For XML PATH ('''')),1,1,'''') AS '+#Getcolumn
INSERT INTO #TEMP(Query)
SELECT #Sql
SET #MinID=#MinID+1
END
SELECT DISTINCT #Sql2=
STUFF((SELECT ', '+ CAST(Query AS nvarchar(max)) FROM #TEMP i For Xml Path(''), type
).value('.', 'nvarchar(max)')
, 1, 2, '')
FROM #TEMP o
SET #Sql2=#Sql2 +' FRom Cte o'
SET #CteSql= 'SELECT Top 1 '+''' ;With cte
AS
(SELECT T2.table2id,''+ STUFF((SELECT '', ''+''T1.''+COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS
Where TABLE_NAME=''Table1'' For XML PATH ('''')),1,1,'''') +'' From Table2 T2
LEFT JOIN Table1 T1
On T1.table1id=t2.table2id )''' +'+CHAR(13)+CHAR(10)+'+'''SELECT DISTINCT table2id,''
FROM INFORMATION_SCHEMA.COLUMNS Where TABLE_NAME=''Table1'''
INSERT INTO #TEMP2(Query)
EXECUTE(#CteSql)
SELECT #CteSql2= Query From #TEMP2
SET #FullSql=#CteSql2+#Sql2
PRINT #FullSql
EXEC(#FullSql)
SET NOCOUNT OFF
Result After Running the Query
table2id table1id table1name table1color table1city table1animal
---------------------------------------------------------------------------------------
117 117, 117 jon, jack blue, red city1, city2 anima, anima
118 NULL NULL NULL NULL NULL
I have a dynamic single row Table like:
PersonId|FirstName|LastName|Address|PhoneNumber
-----------------------------------------------
1 Anuj Tamrakar NY +525418
I want to pivot this table and want an output in temp table like:
PersonalDetails|Value
----------------------
PersonId 1
FirstName Anuj
LastName Tamrakar
Address NY
PhoneNumber +525418
The first Table is a dynamic single row temp table. For this example, I have 5 columns. I may have more or less columns depending on my criteria
You actually want to UNPIVOT:
SELECT PersonalDetails, Value
FROM
(SELECT CAST([PersonId] AS VARCHAR(MAX)) AS [PersonId],
CAST([FirstName] AS VARCHAR(MAX)) AS [FirstName],
CAST([LastName] AS VARCHAR(MAX)) AS [LastName],
CAST([Address] AS VARCHAR(MAX)) AS [Address],
CAST([PhoneNumber] AS VARCHAR(MAX)) AS [PhoneNumber]
FROM mytable) p
UNPIVOT
(Value FOR PersonalDetails IN
([PersonId], [FirstName], [LastName], [Address], [PhoneNumber])
) AS unpvt;
All 'to-be-unpivoted' fields have to be of the same type, hence the use of CAST.
Demo here
For a dynamic number of columns you have to use dynamic sql:
DECLARE #cols VARCHAR(MAX) = ''
DECLARE #cast_cols VARCHAR(MAX) = ''
DECLARE #qry VARCHAR(MAX)
SELECT #cols = #cols + ',[' + COLUMN_NAME + ']'
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'mytable' AND TABLE_SCHEMA='dbo'
SELECT #cast_cols = #cast_cols + ',CAST([' + COLUMN_NAME + '] AS VARCHAR(MAX)) AS [' + COLUMN_NAME + ']'
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'mytable' AND TABLE_SCHEMA='dbo'
SET #cols = STUFF(#cols, 1, 1, '')
SET #cast_cols = STUFF(#cast_cols, 1, 1, '')
SET #qry = 'SELECT PersonalDetails, Value FROM ('
+ #cast_cols +
'FROM mytable) p
UNPIVOT
(Value FOR PersonalDetails IN (' + #cols + ')
) AS unpvt'
EXEC (#qry)
If you really have a single row in the original table, then you can use a series of UNION operations to get your output:
SELECT 'PersonId' AS PersonalDetails, PersonId AS Value
FROM yourTable
UNION ALL
SELECT 'FirstName' AS PersonalDetails, FirstName AS Value
FROM yourTable
UNION ALL
SELECT 'LastName' AS PersonalDetails, LastName AS Value
FROM yourTable
UNION ALL
SELECT 'Address' AS PersonalDetails, Address AS Value
FROM yourTable
UNION ALL
SELECT 'PhoneNumber' AS PersonalDetails, PhoneNumber AS Value
FROM yourTable
SELECT * FROM sys.columns WHERE object_id = OBJECT_ID('dbo.yourTableName')
This gives you your column names. You simply insert the column names in your new table and use ``, insert into temp(PersonalDetails, Value) values(column1,select column1 from SingleRowTable
I have a table like this:
Product_Id Description
---------------------------
BX01 Desc 1
BxX1 Desc 2
Dss3 Desc 3
HHXY Desc 4
I want the result to be exactly:
1 - BX01, 2 - BxX1, 3 - Dss3, 4 - HHXY
I have this query:
DECLARE #ProID VARCHAR(8000)
SELECT #ProID = COALESCE(#ProID + ', - ', '') + Product_Id FROM TABLE
SELECT #ProID
but the return values is only :
BX01,- BxX1,- Dss3,- HHXY
The counting is missing.
How to do that?
Thanks
Assuming the numbers are the row numbers, you should be using ROW_NUMBER() (more info):
DECLARE #tmp TABLE (ID varchar(300), [Desc] varchar(300))
INSERT INTO #tmp (ID, [Desc])
SELECT 'BX01', 'Desc 1'
UNION
SELECT 'BxX1', 'Desc 2'
UNION
SELECT 'Dss3', 'Desc 3'
UNION
SELECT 'HHXY', 'Desc 4'
DECLARE #ProID VARCHAR(8000)
SELECT #ProID = COALESCE(#ProID + ', ', '') + CAST(t.row AS varchar(20))+ ' - ' + t.ID
FROM (
SELECT ROW_NUMBER() OVER (ORDER BY ID) AS row, ID
FROM #tmp) AS t
SELECT #ProID
Try something like this using row_nu,mber function
SELECT CAST(row_number AS VARCHAR(5)) +' - '+ Product_Id +', '
from
(select Product_Id, ROW_NUMBER()OVER(order by Product_Id) as row_number
FROM TABLE ) as derived_tbl
FOR XML PATH('')