I want to transform row of mysql table to column, through mysql pivot table
My input table.
My input table which has data in below format.
Area Status User
-----------------------
1 Active user1
1 Failed user2
1 Success user4
2 Active user2
2 Failed user3
2 Success user4
My Desired Output Format is below
Status user1 user2 user3 user4
-----------------------------------------
Active 1 1 0 0
Failed 0 1 1 0
Success 0 0 0 2
Since i do not know the exact number of users i want to pivot it through dynamic column only.
in your case, if you have a separate user table, you can easily make a Cartesian Product between your user table and status table, and make pivoting.. if you need further helps let me know..
have look on one of my following blog post about a pivoting schenerio for sales report, I am using a dynamic calendar table to produce Cartesian Product with Order Category table..
Sample Pivoting and Cartesian Product
I have another runnable example for you, from
product_id supplier_id number price
p1 s1 2 2.12
p1 s2 3 3.12
p2 s1 4 4.12
to
product_id supplier_id1 supplier_id2 number1 number2 price1 price2
p1 s1 s2 2 3 2.12 3.12
p2 s1 NULL 4 NULL 4.12 NULL
here the "supplier_id" is dynamic, it could be one little data set from a 1 million big set.so there could be supplier1,or supplier99,or supplier999,depends on whats in the source table.
first, lets create the source table:
CREATE TABLE test
(
product_id varchar(10),
supplier_id varchar(10),
number int(11),
price decimal(10,2)
);
INSERT INTO test (product_id, supplier_id, number, price) VALUES ('p1', 's1', 2, 2.12);
INSERT INTO test (product_id, supplier_id, number, price) VALUES ('p1', 's2', 3, 3.12);
INSERT INTO test (product_id, supplier_id, number, price) VALUES ('p2', 's1', 4, 4.12);
I don't think one select will do it, so the temp table and column are needed, this code is what you need:
DROP TABLE IF EXISTS final_data;
DROP TABLE IF EXISTS temp_data;
CREATE TEMPORARY TABLE temp_data
SELECT
product_id,
IF(#productid = product_id, #rownum := #rownum + 1, #rownum := 1) seller_number,
#productid := product_id,
supplier_id,
number,
price
FROM
test
CROSS JOIN (SELECT #rownum := 0) r
CROSS JOIN (SELECT #productid:="") n
ORDER BY
product_id ASC;
ALTER TABLE temp_data ADD PRIMARY KEY(product_id, seller_number);
ALTER TABLE temp_data ADD INDEX (seller_number);
#Dynamic Pivot starts via prepared statements
#Step 1: Dynamily create colum names for sql
#Field supplier_id
SET #sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
' MAX(IF( seller_number= ''',
seller_number,
''', supplier_id, NULL)) AS ',
CONCAT("supplier_id", seller_number)
)
) INTO #sql
FROM temp_data;
#Field number
SELECT
CONCAT(#sql, ', ',
GROUP_CONCAT(DISTINCT
CONCAT(
' MAX(IF( seller_number= ''',
seller_number,
''', number, NULL)) AS ',
CONCAT("number", seller_number)
)
) )INTO #sql
FROM temp_data;
#Field price
SELECT
CONCAT(#sql, ', ',
GROUP_CONCAT(DISTINCT
CONCAT(
' MAX(IF( seller_number= ''',
seller_number,
''', price, NULL)) AS ',
CONCAT("price", seller_number)
)
) )INTO #sql
FROM temp_data;
#Step 2: Add fields to group by query
SET #sql = CONCAT(' create table final_data as (SELECT
product_id,
', #sql, '
FROM
temp_data
GROUP BY
product_id) ');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DROP TABLE IF EXISTS temp_data;
Related
I wonder how to replace such a table
(the table is the result of 3x LEFT JOIN)
SELECT *
FROM users
LEFT JOIN items on users.id = items.id
LEFT JOIN items_additional on users.id = items_additional.items_id
LEFT JOIN items_ask_user on users.id = items_ask_user.items_id';
ID
item_id
name
surname
addition
question
amount
1
1
Gladys
Warner
hot-dog
mayo
14
2
1
Gladys
Warner
pizza
chilli
11
3
2
Harrison
Croft
pizza
4
2
Harrison
Croft
burger
chilli
11
5
2
Harrison
Croft
hod-dog
mayo
14
to somthing like
ID
item_id
name
surname
addition
addition2
addition3
question1
question2
question3
amount
1
1
Gladys
Warner
hot-dog
pizza
-
mayo
chilli
-
25
2
2
Harrison
Croft
pizza
burger
hod-dog
chilli
mayo
-
25
the number of additions or questions may increase or decrease, depending on person.
Edit
SET #sql = NULL;
WITH cte AS(
SELECT DISTINCT ROW_NUMBER() OVER(PARTITION BY user_id) AS idx
FROM users
LEFT JOIN items on users.id = items.id
LEFT JOIN items_additional on users.id = items_additional.items_id
LEFT JOIN items_ask_user on users.id = items_ask_user.items_id
)
SELECT GROUP_CONCAT(
CONCAT('MAX(IF(rn_add = ', cte.idx, ', additional_option_name, NULL)) AS additional_option_name', cte.idx, ','
'MAX(IF(rn_qst = ', cte.idx, ', ask_user, NULL)) AS ask_user', cte.idx
)) INTO #sql
FROM cte;
SET #cte = 'WITH cte AS(SELECT *, ROW_NUMBER() OVER(PARTITION BY name, surname ORDER BY IF(additional_option_name IS NULL, 1, 0), `event_items`.`id`) AS rn_add, ROW_NUMBER() OVER(PARTITION BY name, surname ORDER BY IF(ask_user IS NULL, 1, 0), `event_items`.`id`) AS rn_qst
FROM users
LEFT JOIN event_items on users.id = event_items.id
LEFT JOIN event_items_additional on users.id = event_items_additional.items_id
LEFT JOIN event_items_ask_user on users.id = event_items_ask_user.items_id';
SET #sql = CONCAT(#cte,
'SELECT `event_items`.`id`, user_id, name, surname,',
#sql,
',SUM(additional_option_price) AS additional_option_price FROM cte GROUP BY user_id, name, surname'
);
SELECT #sql;
PREPARE stmt1 FROM #sql;
EXECUTE stmt1;
DEALLOCATE PREPARE stmt1;
Edit2
Schema demo here
Will just throw this here as a possibility - it won't give you dynamic columns but may be of use depending on how you intend to consume the data.
It's certaintly less faff and more performant if you can.
select
item_Id, name, surname,
group_concat(addition separator ', ') Additions,
group_concat(question separator ', ') Questions,
Sum(amount) amount
from t
group by item_Id, name, surname;
As long as the dynamic solution is based on the static one, for reasons of clarity I'll first explain the static one by assuming that, as in the example your provided, there are exactly 3 fields at max, for addition and question fields.
Let's look at the static solution first, by assuming that we have specifically 3 fields. In this case what you can do is compute a row number for each addition and question, which will be used to match the specific value at the required index for each of the three fields addition1, addition2 and addition3 (same goes for question), using an IF statement. In order to remove the NULL values that are generated by this statement, we can select the maximum value and aggregate over item_id, name and surname
WITH cte AS(
SELECT *,
ROW_NUMBER() OVER(
PARTITION BY name, surname
ORDER BY IF(addition IS NULL, 1, 0),
ID ) AS rn_add,
ROW_NUMBER() OVER(
PARTITION BY name, surname
ORDER BY IF(question IS NULL, 1, 0),
ID ) AS rn_qst
FROM tab
)
SELECT item_id AS ID,
item_id,
name,
surname,
MAX(IF(rn_add = 1, addition, NULL)) AS addition1,
MAX(IF(rn_add = 2, addition, NULL)) AS addition2,
MAX(IF(rn_add = 3, addition, NULL)) AS addition3,
MAX(IF(rn_qst = 1, question, NULL)) AS question1,
MAX(IF(rn_qst = 2, question, NULL)) AS question2,
MAX(IF(rn_qst = 3, question, NULL)) AS question3,
SUM(amount) AS amount
FROM cte
GROUP BY item_id,
name,
surname
Check the demo here.
The dynamic solution aims at reproducing that exact same query as a prepared statement (which is essentially a string that you first build and then ask MySQL to execute over the database), with the only difference that it needs to generalize on the amount of fields to extract:
MAX(IF(rn_add = 1, addition, NULL)) AS addition1,
MAX(IF(rn_qst = 1, addition, NULL)) AS question1,
...
...
MAX(IF(rn_add = <n>, addition, NULL)) AS addition<n>,
MAX(IF(rn_qst = <n>, addition, NULL)) AS question<n>,
And we need to reproduce these instructions n times with n equals to the item_id's highest amount of both addition and question values. In order to generate this piece of query, we get the longest list of indices:
WITH cte AS(
SELECT DISTINCT ROW_NUMBER() OVER(PARTITION BY item_id) AS idx
FROM tab
)
and cycle over it to generate all MAX rows as a string where, in place of the specific number (as in the static query), we will use all the numbers stored inside cte.idx:
SELECT GROUP_CONCAT(
CONCAT('MAX(IF(rn_add = ', cte.idx, ', addition, NULL)) AS addition', cte.idx, ','
'MAX(IF(rn_qst = ', cte.idx, ', question, NULL)) AS question', cte.idx
)) INTO #sql
FROM cte;
Once we have the generalized amonut of MAX rows, we can just use this together with the rest of the static query, which does not depend on the number of addition or question values.
SET #cte = 'WITH cte AS(SELECT *, ROW_NUMBER() OVER(PARTITION BY name, surname ORDER BY IF(addition IS NULL, 1, 0), ID) AS rn_add, ROW_NUMBER() OVER(PARTITION BY name, surname ORDER BY IF(question IS NULL, 1, 0), ID) AS rn_qst FROM tab)';
SET #sql = CONCAT(#cte,
'SELECT item_id AS ID, item_id, name, surname,',
#sql,
',SUM(amount) AS amount FROM cte GROUP BY item_id, name, surname'
);
Once we have the static query generated as a string in a dynamic way, we can ask MySQL to prepare, execute and deallocate it.
PREPARE stmt1 FROM #sql;
EXECUTE stmt1;
DEALLOCATE PREPARE stmt1;
The execution will show you your desired output.
Here's the full code for the dynamic query:
SET #sql = NULL;
WITH cte AS(
SELECT DISTINCT ROW_NUMBER() OVER(PARTITION BY item_id) AS idx
FROM tab
)
SELECT GROUP_CONCAT(
CONCAT('MAX(IF(rn_add = ', cte.idx, ', addition, NULL)) AS addition', cte.idx, ','
'MAX(IF(rn_qst = ', cte.idx, ', question, NULL)) AS question', cte.idx
)) INTO #sql
FROM cte;
SET #cte = 'WITH cte AS(SELECT *, ROW_NUMBER() OVER(PARTITION BY name, surname ORDER BY IF(addition IS NULL, 1, 0), ID) AS rn_add, ROW_NUMBER() OVER(PARTITION BY name, surname ORDER BY IF(question IS NULL, 1, 0), ID) AS rn_qst FROM tab)';
SET #sql = CONCAT(#cte,
'SELECT item_id AS ID, item_id, name, surname,',
#sql,
',SUM(amount) AS amount FROM cte GROUP BY item_id, name, surname'
);
SELECT #sql;
PREPARE stmt1 FROM #sql;
EXECUTE stmt1;
DEALLOCATE PREPARE stmt1;
Check the demo here.
Side Note: if you want to store the output of this query, you may require to create a view inside the prepared statement. In that case you should change the #sql assignment to:
SET #sql = CONCAT('CREATE VIEW my_view AS ',
#cte,
'SELECT item_id AS ID, item_id, name, surname,',
#sql,
',SUM(amount) AS amount FROM cte GROUP BY item_id, name, surname'
);
hence select the content of the view whenever you need it, for example to export it to Excel.
I have a mysql table which has for simplified purposes sku, and that sku has an unknown set of attribute_values
skus
------
id, sku, qty
1, abc, 10
2, bvcc, 20
variantvalues
-------------
id, sku_id, variant_id, value
1, 1, 1, red
1, 1, 2, medium
variants
--------
id, name
1, color
2, size
.....
EXPECTED RESULT
id, sku, color, size, qty
-------------------------
1, abc, red, medium, 10
the challenge I'm having is, can I do a query that gives me the data as a list of skus, and then columns for each of their variants and variant value... such that, if I added a new variant option... say, gender, to the variants table, and it was attributed to a sku, then there would be a new column for that?
The question is derived from this question: Modeling Product Variants
You are describing a dynamic pivot. Basically you need to dynamically generate the query string, using a sql query, then execute it.
Here is one way to do it in MySQL, using a prepared statement:
set #sql = null;
select group_concat(distinct
'max(case when v.name = ''', name, ''' then vv.value end) as `', name, '`'
) into #sql
from variants;
set #sql = concat(
'select s.id, s.sku, ',
#sql,
' from skus s
inner join variantvalues vv on vv.sku_id = s.id
inner join variants v on v.id = vv.variant_id
group by s.id, s.sku'
);
prepare stmt from #sql;
execute stmt;
deallocate prepare stmt;
The following statement outputs the userName and week1Score. I would like it to loop through 17 times, to get the score for each of the 17 weeks.
SELECT userName, (totalWins+(totalPushs*.5)) AS week1Score FROM (
SELECT *, SUM(win) AS totalWins, SUM(lost) AS totalLost, SUM(push) AS totalPushs FROM (
SELECT *, (finalResult = 'win') AS win, (finalResult = 'loss') AS lost, (finalResult = 'push') AS push FROM (
SELECT userName, IF (pickID=visitorID, visitorResult, homeResult) AS finalResult
FROM table_users
JOIN table_picks
ON table_users.userID = table_picks.userID
JOIN table_schedule
ON table_picks.gameID = table_schedule.gameID
WHERE weekNum = 1
) x
) x GROUP BY userName
) x ORDER BY userName
The above statement outputs the following.
+-----------------------+
| userName | week1Score |
+-----------------------+
I would like it to loop through 17 times to to output the following.
+------------------------------------------------------------------------+
| userName | week1Score | week2Score | week3Score | week4Score | week... |
+------------------------------------------------------------------------+
How would I use MySQL loop to do this?
I think your query is a bit complex. However, there's a better approach: a Pivot Query.
MySQL does not have a "pivot" instruction, but an expression can be built to get the output you need.
I'll build a temp table to make things a bit easier to read (I am using user variables to make things a bit clearer):
-- This first table will compute the score
drop table if exists temp_step01;
create temporary table temp_step01
select userId
, userName
, weekNum
, #finalResult := if(pickId=visitorId, visitorResult, homeResult) as finalResult
, #w := #finalResult = 'win' as win
, #l := #finalResult = 'loss' as lost
, #p := #finalResult = 'push' as push
, #w + (#p * 0.5) as score
from
table_users as tu
join table_picks as tp on tu.userId = tp.userId
join table_schedule as ts on tp.gameId = ts.gameId;
alter table temp_step01
add index uid(userId),
add index wn(weekNum);
Now, the fun part: build the pivot table
-- First, build the expression for each column
select
group_concat(
concat(
'sum(case weekNum when ', weekNum, ' then score end) as week', weekNum, 'score'
)
)
into #sql
from (select distinct weekNum from temp_step01) as a;
-- Then, create a complete SELECT statement
set #sql = concat('select userId, userName, ', #sql, ' from temp_step01 group by userId');
-- OPTIONAL: Check that the sql statement is well written:
select #sql;
-- Now, prepare a statement, and execute it
prepare stmt from #sql;
execute stmt;
-- When you're done, don't forget to deallocate the statement
deallocate prepare stmt;
A bit laborious, but I think this will give you what you need. Hope it helps.
Please take a look at this Fiddle Example
I want to convert each field into a row from this table:
CREATE TABLE product
(`ID` int, `name` varchar(1), `category` varchar(11), `price` int,`year`int)
;
INSERT INTO product
(`ID`, `name`, `category`, `price`,`year`)
VALUES
(1, 'A', 'Outdoor', 25,2010),
(2, 'A', 'Doll', 34,2009),
(3, 'C', 'Doll', 25,2008),
(4, 'D', 'Outdoor', 20,2010),
(5, 'E', 'Brainteaser', 22,2010),
(6, 'E', 'Brainteaser', 22,2009),
(7, 'G', 'Brainteaser', 30,2009),
(8, 'G', 'Brainteaser', 30,2009)
;
Here's the output I'm trying to get:
field value
name A,C,D,E,G
category Brainteaser,Doll,Outdoor
price 20,22,25,30,34
year 2008,2009,2010
I read a thread about pivoting table with UNION and MAX but I was lost at using MAX with GROUP_CONCAT
SELECT
MAX(CASE WHEN ... GROUP_CONCAT(DISTINCT (value) SEPARATOR '|')) as value
from(
select id,name value, 'name' field
from product
union all
select id,category value, 'category' field
from product
union all
select id,price value, 'price' field
from product
union all
select id,year value, 'year' field
from product
)
GROUP BY field
order by value
Can anyone show me how to get that output?
This will give you expected output:
SELECT 'name' AS `field`. GROUP_CONCAT(DISTINCT `name` ORDER BY `name`) AS `value`
FROM product
UNION ALL
SELECT 'category' AS `field`. GROUP_CONCAT(DISTINCT `category` ORDER BY `category`) AS `value`
FROM product
UNION ALL
SELECT 'price' AS `field`. GROUP_CONCAT(DISTINCT `price` ORDER BY `price`) AS `value`
FROM product
UNION ALL
SELECT 'year' AS `field`. GROUP_CONCAT(DISTINCT `year` ORDER BY `year`) AS `value`
FROM product
Added ORDER BY because looks like you need sorted output
something like this?? Using union all for better proformance and incase there are any dupilcates.
SELECT 'name' field, group_concat(DISTINCT name ORDER BY name SEPARATOR '|') value FROM product
UNION ALL
SELECT 'category' field, group_concat(DISTINCT category ORDER BY category SEPARATOR '|') value FROM product
UNION ALL
SELECT 'price' field, group_concat(DISTINCT price ORDER BY price SEPARATOR '|') value FROM product
UNION ALL
SELECT 'year' field, group_concat(DISTINCT year ORDER BY year SEPARATOR '|') value FROM product;
EDIT:
If you would like to do this with just one query you can achieve it this way.
SELECT
#uName := group_concat(DISTINCT name ORDER BY name SEPARATOR '|'),
#uCat := group_concat(DISTINCT category ORDER BY category SEPARATOR '|') uCat,
#uPrice := group_concat(DISTINCT price ORDER BY price SEPARATOR '|') uPrice,
#uYear := group_concat(DISTINCT year ORDER BY year SEPARATOR '|') uYear
FROM product;
SELECT 'name' field, #uName value
UNION ALL
SELECT 'category' field, #uCat value
UNION ALL
SELECT 'price' field, #uPrice value
UNION ALL
SELECT 'year' field, #uYear value;
NOTE: you can do ORDER BY inside the GROUP_CONCAT
There is a way to complete your request without having to query your table with many queries. In fact, method below is a way to turn any table to pivot. It will use mysql prepared statements for prepare SQL and later execute it:
SELECT
GROUP_CONCAT(f SEPARATOR ' UNION ALL ')
FROM
(SELECT
CONCAT(
'SELECT "',
column_name,
'" AS `field`, GROUP_CONCAT(DISTINCT ',
column_name,
') AS `value` FROM `',
#table_name,
'`'
) AS f
FROM
((SELECT
column_name
FROM
INFORMATION_SCHEMA.COLUMNS
WHERE
table_name=#table_name &&
table_schema=#schema_name
) AS fields
CROSS JOIN
(SELECT
#table_name := 'product',
#schema_name:= 'test'
) AS init)
) AS sqldata
The sql above will result in a string, which can be used to assign a variable, like
mysql> SET #sql:=(SELECT GROUP_CONCAT(f SEPARATOR ' UNION ALL ') FROM (SELECT CONCAT('SELECT "', column_name,'" AS `field`, GROUP_CONCAT(DISTINCT ', column_name,') AS `value` FROM `', #table_name, '`') AS f FROM ((SELECT column_name FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name=#table_name && table_schema=#schema_name) AS fields CROSS JOIN (SELECT #table_name:='product', #schema_name:='test') AS init)) AS sqldata);
Query OK, 0 rows affected (0.03 sec)
next, prepare:
mysql> PREPARE stmt FROM #sql;
Query OK, 0 rows affected (0.00 sec)
Statement prepared
finally, execute it:
mysql> EXECUTE stmt;
+----------+--------------------------+
| field | value |
+----------+--------------------------+
| ID | 1,2,3,4,5,6,7,8 |
| name | A,C,D,E,G |
| category | Outdoor,Doll,Brainteaser |
| price | 25,34,20,22,30 |
| year | 2010,2009,2008 |
+----------+--------------------------+
5 rows in set (0.00 sec)
The benefit is that you are independent from table structure, field names, e tc. It's a general way - so you may use it to create such pivots for any table.
Few words about variables #table_name and #schema_name. They specify which table and in which schema would be pivoted. In sample above, I used CROSS JOIN to set them in-place, but you may set them separately, to maintain different tables for pivots.
I am trying to write a SQL query that will turn this table:
Start_time End_time Instructor Student
9:00 9:35 Joe Bob Andrew
9:00 9:35 Joe Bob Smith
9:00 9:35 Roberto Andy
10:00 10:35 Joe Bob Angelica
11:00 11:20 Roberto Bob
Into something like this:
Instructor 9:00 10:00 11:00
Joe Bob Andrew, Smith Angelica NULL
Roberto Andy NULL Bob
I think that this is some sort of PIVOT command but I am not sure how I should go about writing the SQL query. The times are all dynamically generated so I would prefer it if the query would generate the column names in the second table dynamically (for example, if the original table contained an additional start time of 11:30, there should be a new column for 11:30 in the result).
Thank you in advance, I've been playing with this for a while but couldn't get it to work on my own. I can provide the SQL INSERT commands to give you the full data if necessary.
EDIT: It would be particularly helpful to get the result of this select statement as a VIEW. Thanks!
EDIT 2:
The code that is generating the view that makes the first table is:
CREATE VIEW schedule_view AS SELECT RTRIM(SUBSTRING(students.schedule_first_choice, 1, 5)) AS start_time, RTRIM(SUBSTRING(students.schedule_first_choice, -10, 5) AS end_time, CONCAT(instructors.first_name, ' ', instructors.last_name) AS instructor_name,
CONCAT(students.first_name, ' ', students.last_name) AS student_name , students.swim_america_level as class
FROM students, instructors WHERE students.instructor_id = instructors.instructor_id AND students.season =
(SELECT constant_value FROM constants WHERE constant_name = 'season') AND students.year =
(SELECT constant_value FROM constants WHERE constant_name = 'year')
SET #sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'GROUP_CONCAT(case when Start_time = ''',
Start_time,
''' then Student ELSE NULL end) AS ',
CONCAT('`',Start_time,'`')
)
) INTO #sql
FROM Table1;
SET #sql = CONCAT('SELECT Instructor, ', #sql, '
FROM Table1
GROUP BY Instructor');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SQLFiddle Demo
IN THESE ALL QUERY EXECUTED AND TESTED IN SQL SEREVR
USING STATIC COLUMNS IN PIVOT
Select * from
(
select Instructor,Start_time, STUFF((select ',' + student from Table1 a where
a.Start_time = b.Start_time and
a.Instructor=b.Instructor for xml path('')),1,1,'') as student
from table1 b ) x
PIVOT
(
max(Student)
for start_time IN ([9:00],[10:00], [11:00])
) p
DYNAMICALLY CREATE PIVOT TABLE Using Temp Table
Declare #tab nvarchar(max)
Declare #pivottab nvarchar(max)
Create table #Table2 (
instructor varchar(100),
student varchar(100),
start_time varchar(10)
);
insert into #Table2 (instructor,student,start_time)
select
Instructor,
STUFF((Select ','+student
from Table1 as a
Where
a.Start_time = b.Start_time and
a.Instructor=b.Instructor
for xml path('')),1,1,''),
Start_time
from table1 as b
select #tab = coalesce(#tab + ',['+start_time+']' ,'['+start_time+']')
from #Table2
group by Start_time
set #pivottab =
N' select * from (
select Instructor, Start_time, student from #Table2
) x
PIVOT (
max(Student) for start_time IN ('+#tab+')
) p'
execute(#pivottab)
if exists(select * from #table2)
Begin
Drop table #table2
End
DYNAMICALLY CREATE PIVOT TABLE
Declare #tab nvarchar(max)
Declare #pivottab nvarchar(max)
Select #tab = coalesce(#tab + ',['+start_time+']' , '['+start_time+']') from Table1
group by Start_time
set #pivottab = N'
select *
from
(
select Instructor,Start_time,STUFF((select '+ char(39)+char(44)+char(39) + '+ a.student from Table1 as a
where a.Start_time = b.Start_time and a.Instructor=b.Instructor for xml path('''')),1,1,'''')
as Student from table1 as b
) x
PIVOT
(
max(Student)
for start_time IN ('+#tab+')
) p'
execute(#pivottab)