SQL query with dynamic columns (PIVOT) - mysql

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)

Related

Mysql attendence report creates syntax error on prepare stmt

I have two tables: One with just a list of dates called date_range (column name is Date) and one called wp_wpdatatable_1 in which all the date is stored (after each practice, a row for every player is created with the name of the player (player), date of practice (date), duration (sporttrainingduration), what practice group (practiceheldby) etc...
Now I want to create a report.
I want every day of the month across, the players names in the first column and on every day a player was at a practice I want to list which practice he attended (wp_wpdatatable_1.practiceheldby)
-- 1. Create an expression that builds the columns
set #sql = (
select group_concat(distinct
concat(
"max(case when date_range.`date`='", date_range.`Date`, "' then `practiceheldby` end) as `", date_range.`Date`, "`"
)
)
from wp_wpdatatable_1, date_range
where date_range.`Date`>=2019-06-01
and date_range.`Date` <= 2019-06-07
);
-- 2. Complete the SQL instruction
set #sql = concat("select `player`, ", #sql , " from wp_wpdatatable_1 group by `player`");
-- 3. Create a prepared statement
PREPARE stmt from #sql;
-- 4. Execute the prepared statement
execute stmt;
DEALLOCATE PREPARE stmt;
I'm not a pro and I've played with this for 3 or four days now. I think I'm very close, but I get this error message:
PREPARE stmt from #sql
MySQL meldet: Dokumentation
#1064 - Fehler in der SQL-Syntax. Bitte die korrekte Syntax im Handbuch nachschlagen bei 'NULL' in Zeile 1
What am I missing?
thank you!
You're very close. Besides the missing quotes on dates, the case when date_range.date should be case when date in line 5 of your code.
-- 0. Toy data
CREATE TABLE date_range (
`Date` datetime
);
CREATE TABLE wp_wpdatatable_1 (
`player` VARCHAR(5),
`Date` DATE,
`sporttrainingduration` FLOAT,
`practiceheldby` VARCHAR(10)
);
INSERT INTO date_range
VALUES
('2019-06-01'),
('2019-06-02'),
('2019-06-03')
;
INSERT INTO wp_wpdatatable_1
VALUES
('AAA','2019-06-01','1','group1'),
('AAA','2019-06-02','2','group2'),
('AAA','2019-06-03','3','group3'),
('AAA','2019-06-04','1','gorup1'),
('BBB','2019-06-02','2','group2'),
('CCC','2019-06-03','3','group3')
;
select * from date_range;
select * from wp_wpdatatable_1;
date_range
===================
Date
===================
2019-06-01 00:00:00
2019-06-02 00:00:00
2019-06-03 00:00:00
wp_wpdatatable_1
=======================================================
player Date sporttrainingduration practiceheldby
=======================================================
AAA 2019-06-01 1 group1
AAA 2019-06-02 2 group2
AAA 2019-06-03 3 group3
AAA 2019-06-04 1 gorup1
BBB 2019-06-02 2 group2
CCC 2019-06-03 3 group3
The updated code below:
-- 1. Create an expression that builds the columns
set #sql = (
select group_concat(distinct
concat(
-- "max(case when date_range.`date`='", date_range.`Date`, "' then `practiceheldby` end) as `", date_range.`Date`, "`"
"max(case when `date`='", date_range.`Date`, "' then `practiceheldby` end) as `", date_range.`Date`, "`"
)
)
from wp_wpdatatable_1, date_range
-- where date_range.`Date`>=2019-06-01
-- and date_range.`Date` <= 2019-06-07
where date_range.`Date`>='2019-06-01'
and date_range.`Date` <= '2019-06-07'
);
Output:
===========================================================================
player 2019-06-01 00:00:00 2019-06-02 00:00:00 2019-06-03 00:00:00
===========================================================================
AAA group1 group2 group3
BBB null group2 null
CCC null null group3
Code here

Single Table MySQL Pivot with Dynamic Column

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;

pivot with dynamic date columns in SqlServer 2012 & MySQL

I have below data.
create table #temp
(
date date primary key
,registrations int not null default(0)
,orders int not null default(0)
)
insert into #temp
(
date ,
registrations
,orders
)values('2017-05-01',30,40),('2017-05-02',60,30),('2017-05-03',109,98)
select * from #temp
for each date the data will keep on add. It is very dynamic. Is there anyway to pivot the data with dynamic dates if #from_dt and #to_dt provided as paramters. Output should look like below.
2017-05-01 2017-05-02 2017-05-03
registrations 30 60 109
orders 40 30 98
I have same data in SQLServer and MySQL databases.
any help is appreciated. Thanks in advance.
Declare #Date1 date = '2017-05-01'
Declare #Date2 date = '2017-05-02' -- Notice only Two Days
Declare #SQL varchar(max) = Stuff((Select Distinct ',' + QuoteName([Date]) From #temp Where [Date] between #Date1 and #Date2 Order by 1 For XML Path('')),1,1,'')
Select #SQL = '
Select *
From (
select [Date]
,B.*
From #temp
Cross Apply (values (''Orders'',orders)
,(''Registrations'',registrations)
) B(Item,Value)
Where [Date] between '''+convert(varchar(10),#Date1,120)+''' and '''++convert(varchar(10),#Date2,120)+'''
) A
Pivot (sum(Value) For [Date] in (' + #SQL + ') ) p'
Exec(#SQL);
Returns
Item 2017-05-01 2017-05-02
Orders 40 30
Registrations 30 60

MySQL loop for total score each week

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.

How to combine multiple rows into one row for a field in MS SQL 2008 R2 without using temp table or variable?

How do I accomplish my goal without using temp table or variable?
Table:
ID ModelNum Qty
123 ABC 4
123 DEF 4
Expected Result:
ID Models Qty
123 ABC | DEF 4
Thanks in advance!
DECLARE #T TABLE (ID INT,ModelNum CHAR(3),Qty INT)
INSERT INTO #T
VALUES
(123,'ABC',4),
(123,'DEF',4),
(123,'GLK',4)
SELECT DISTINCT ID, STUFF(C.List, 1, 2, '') Models, Qty
FROM #T t
CROSS APPLY (
SELECT '| ' + ModelNum
FROM #T
WHERE ID = t.ID
FOR XML PATH('')
)C(List)
Result Set
ID Models Qty
123 ABC| DEF| GLK 4
Hi how about this Query below:
I have did the same example with some different logic and different attribute.
I can get the expected OP, please response if you have any suggestions for me on btechit#hotmail.com.
Declare:
#ConcatTable table (Ename varchar(30), Empno int)
Insert into #ConcatTable values ('Steve', 100),('mathew', 100),('jon', 101),('tom', 101)
--select * from #ConcatTable
--select ROW_NUMBER()over(order by Empno)Row2,* from
--(select distinct Empno from #ConcatTable)C
declare #p varchar(100) = ''
select #p = #p+ ' '+Ename from (
select DENSE_RANK()over(order by Empno)as dens, * from #ConcatTable )A
where A.dens = 1
declare #q varchar(100) = ''
select #q = #q+ ' '+Ename from (
select DENSE_RANK()over(order by Empno)as dens, * from #ConcatTable )A
where A.dens = 2
--SELECT #p
--SELECT #q
declare #M table (Name varchar(30))
insert into #M
select * from(
select #p as v
union all
select #q as vv
)G
--SELECT ROW_NUMBER()over (order by Name desc)Rown1,* from #M
SELECT A.Name,CC.Empno FROM(
SELECT ROW_NUMBER()over (order by Name desc)Rown1,* FROM #M)A
inner join
(select ROW_NUMBER()over(order by Empno)Row2,* from
(select distinct Empno from #ConcatTable)C
)CC
on A.Rown1 = CC.Row2