I've read previous posts regarding pivot table and swapping rows to columns, but I couldn't find correct answer for my question. I have below table in MySQL:
+--------+--------+
| Userid | gname |
+--------+--------+
| 12 | AVBD |
| 12 | ASD |
| 12 | AVFD |
| 12 | Aew1 |
| 12 | AVBD32 |
| 12 | ASD23 |
| 12 | AVBDe |
| 12 | ASDer |
| 45 | AVBD |
| 45 | ASD444 |
| 45 | AVBD44 |
| 45 | ASD44 |
| 453 | AVBD22 |
| 453 | ASD1 |
+--------+--------+
I want to produce an ordered pivot table:
+--------+--------+--------+--------+--------+--------+--------+--------+--------+
| Userid | gname1 | gname2 | gname3 | gname4 | gname5 | gname6 | gname7 | gname8 |
+--------+--------+--------+--------+--------+--------+--------+--------+--------+
| 12 | AVBD | ASD | AVFD | Aew1 | AVBD32 | ASD23 | AVBDe | ASDer |
| 45 | AVBD | ASD444 | AVBD44 | ASD44 | | | | |
| 453 | AVBD22 | ASD1 | | | | | | |
+--------+--------+--------+--------+--------+--------+--------+--------+--------+
the name of gname is dynamic and has no limit.
Here is the data setup along with a SQLFiddle http://sqlfiddle.com/#!2/65fec
CREATE TABLE gnames
(`Userid` int, `gname` varchar(6))
;
INSERT INTO gnames
(`Userid`, `gname`)
VALUES
(12, 'AVBD'),
(12, 'ASD'),
(12, 'AVFD'),
(12, 'Aew1'),
(12, 'AVBD32'),
(12, 'ASD23'),
(12, 'AVBDe'),
(12, 'ASDer'),
(45, 'AVBD'),
(45, 'ASD444'),
(45, 'AVBD44'),
(45, 'ASD44'),
(453, 'AVBD22'),
(453, 'ASD1')
;
This would be significantly easier if MySQL supported windowing functions because it's not necessarily the easiest to create a row number for each userid. I'll show you two ways to do this with hard-coded queries (limited number of results) and then I'll include one version that is using dynamic SQL.
In order to get the final result, you need some sequential number for each gname within the userid. This can be done a few different ways.
First, you can use a correlated subquery to count the number of gnames per user, you'd then use this sequence to create your new columns via an aggregate function with CASE expression:
select
userid,
max(case when gnameNum = 1 then gname else '' end) gname1,
max(case when gnameNum = 2 then gname else '' end) gname2,
max(case when gnameNum = 3 then gname else '' end) gname3,
max(case when gnameNum = 4 then gname else '' end) gname4,
max(case when gnameNum = 5 then gname else '' end) gname5,
max(case when gnameNum = 6 then gname else '' end) gname6,
max(case when gnameNum = 7 then gname else '' end) gname7,
max(case when gnameNum = 8 then gname else '' end) gname8
from
(
select userid,
gname,
(select count(*)
from gnames d
where g.userid = d.userid
and g.gname <= d.gname) as gnameNum
from gnames g
) src
group by userid;
See SQL Fiddle with Demo. Inside the subquery you are creating a row number for each gname, you then use this new value in the column creation. The problem with correlated subqueries is you could suffer from performance issues on larger data sets.
A second method would be to include user variables to create the row number. This code uses 2 variables to compare the previous row to the current row and increases the row number, if the userid is the same as the previous row. Again, you'd use the row number created to convert the data to new columns:
select
userid,
max(case when rownum = 1 then gname else '' end) gname1,
max(case when rownum = 2 then gname else '' end) gname2,
max(case when rownum = 3 then gname else '' end) gname3,
max(case when rownum = 4 then gname else '' end) gname4,
max(case when rownum = 5 then gname else '' end) gname5,
max(case when rownum = 6 then gname else '' end) gname6,
max(case when rownum = 7 then gname else '' end) gname7,
max(case when rownum = 8 then gname else '' end) gname8
from
(
select
g.userid,
g.gname,
#row:=case when #prev=userid then #row else 0 end + 1 as rownum,
#prev:=userid
from gnames g
cross join
(
select #row:=0, #prev:=null
) r
order by userid, gname
) src
group by userid;
See SQL Fiddle with Demo.
Now, in order to do this dynamically you will need to use a prepared statement. This process will create a sql string that you'll execute to get the final result. For this example, I used the user variable query above:
SET #sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'max(case when rownum = ',
rownum,
' then gname else '''' end) AS `gname',
rownum, '`'
)
) INTO #sql
from
(
select
g.userid,
g.gname,
#row:=case when #prev=userid then #row else 0 end + 1 as rownum,
#prev:=userid
from gnames g
cross join
(
select #row:=0, #prev:=null
) r
order by userid, gname
) src;
SET #sql = CONCAT('SELECT userid, ', #sql, '
from
(
select
g.userid,
g.gname,
#row:=case when #prev=userid then #row else 0 end + 1 as rownum,
#prev:=userid
from gnames g
cross join
(
select #row:=0, #prev:=null
) r
order by userid, gname
) src
group by userid');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
See SQL Fiddle with Demo. All three versions will give a result of:
| USERID | GNAME1 | GNAME2 | GNAME3 | GNAME4 | GNAME5 | GNAME6 | GNAME7 | GNAME8 |
|--------|--------|--------|--------|--------|--------|--------|--------|--------|
| 12 | Aew1 | ASD | ASD23 | ASDer | AVBD | AVBD32 | AVBDe | AVFD |
| 45 | ASD44 | ASD444 | AVBD | AVBD44 | | | | |
| 453 | ASD1 | AVBD22 | | | | | | |
One thing to consider when using the dynamic SQL is MySQL has a set length for the group_concat_max_len, if you have a lot of columns being created you might run into issues. You'll want to account for that. Here is another question that deals with this MySQL and GROUP_CONCAT() maximum length.
Related
I want to get the total maximum number of column CODE which the maximum is defined by the last five digits from mybarcode column.
mybarcode | code | judge | create_date |
-------------+------+--------+-------------+
M71X400001 | 7 | pass |
M71X400002 | 7 | pass |
M71X400005 | 7 | pass |
M71X400010 | 7 | pass |
M81X400001 | 8 | pass |
M81X400002 | 8 | pass |
M81X400007 | 8 | pass |
M91X400001 | 9 | pass |
M91X400003 | 9 | pass |
```
Example:
>The maximum value of 7 from CODE column is 10 ( from M71X4'00010')
>The maximum value of 8 from CODE column is 7 ( from M81X4'00007')
>The maximum value of 9 from CODE column is 3 ( from M91X4'00003')
The result should be 10+7+3=20.
And want display in the result table below.
```
SELECT DAY,
SUM(CASE WHEN judge = 'pass' then 1 else 0 end) pass,
SUM(CASE WHEN judge = 'fail' then 1 else 0 end) fail
**??? as number**
from MYTABLE
where MONTH(create_date) = '04' and YEAR(create_date) = '2019'
GROUP BY DAY
Result Table
day | pass | fail | number |
--------+------+--------+----------+
1 | 9 | 0 | 20 |
2 | 9 | 0 | ?? |
3 | 9 | 0 | ?? |
I think you need to do group by two times. Please try below code -
For MySQL -
SELECT
DAY,
SUM(pass),
SUM(fail),
SUM(max_barcode)
FROM (
SELECT
DAY,
SUM(CASE WHEN judge = 'pass' then 1 else 0 end) pass,
SUM(CASE WHEN judge = 'fail' then 1 else 0 end) fail,
Code,
CAST(MAX(SUBSTRING(mybarcode, 5)) AS SIGNED) AS max_barcode
FROM MYTABLE
WHERE MONTH(create_date) = '%s' and YEAR(create_date) = '%s'
GROUP BY DAY, Code
) AS CTE
GROUP BY DAY;
FOR MS SQL Server -
;WITH CTE AS (
SELECT
DAY,
SUM(CASE WHEN judge = 'pass' then 1 else 0 end) pass,
SUM(CASE WHEN judge = 'fail' then 1 else 0 end) fail,
Code,
max_barcode = cast(max(right(mybarcode, 5)) as int)
FROM MYTABLE
WHERE MONTH(create_date) = '%s' and YEAR(create_date) = '%s'
GROUP BY DAY, Code
)
SELECT
DAY,
SUM(pass),
SUM(fail),
SUM(max_barcode)
FROM CTE
GROUP BY DAY;
For example we have this ServiceRequests source table in database:
| Id | ClientDepartment | ServiceType | DateTimeServiced |
|----|------------------|-------------|------------------|
| 1 | Sales | Networking | 15.01.17 |
| 2 | Development | Networking | 14.02.17 |
| 3 | Development | Networking | 09.04.17 |
| 4 | Sales | Software | 11.03.17 |
| 5 | Sales | Hardware | 30.03.17 |
| 6 | Development | Hardware | 15.04.17 |
Need to make SQL select query and get result:
|Client\Service| Networking | Software | Hardware | Total |
|--------------|------------|----------|----------|-------|
| Sales | 1 | 1 | 1 | 3 |
| Development | 2 | 0 | 1 | 3 |
Where numbers are count of intersections between ServiceType and ClientDepartment (services per department).
Trying something like this gives very wrong result:
select ClientDepartment,
(select count(t1.ClientDepartment)
from ServiceRequests t1
where t1.ServiceType = 'Networking'
) as Networking,
(select count(t1.ClientDepartment)
from ServiceRequests t1
where t1.ServiceType = 'Software'
) as Software,
(select count(t1.ClientDepartment)
from ServiceRequests t1
where t1.ServiceType = 'Hardware'
) as Hardware,
(select count(t1.ClientDepartment)
from ServiceRequests t1
) as Total
from ServiceRequests
group by ClientDepartment
|Client\Service| Networking | Software | Hardware | Total |
|--------------|------------|----------|----------|-------|
| Sales | 3 | 1 | 2 | 6 |
| Development | 3 | 1 | 2 | 6 |
Hope for help, create example table code
You could use sum on CASE WHEN and group by
in this way you dn't need subselect for each count
select
ClientDepartment
, sum(case when ServiceType = 'Networking' then 1 else 0 end )as Networking
, sum(case when ServiceType = 'Software' then 1 else 0 end ) as Software
, sum(case when ServiceType = 'Hardware' then 1 else 0 end ) as Hardware
, sum(case when ServiceType = 'Networking' then 1 else 0 end ) +
sum(case when ServiceType = 'Software' then 1 else 0 end ) +
sum(case when ServiceType = 'Hardware' then 1 else 0 end ) as Total
from ServiceRequests
group by ClientDepartment
this should work for both the DB ..
We Can Achieve Desired result using Dynamic Sql
IF OBJECT_ID('Tempdb..#Temp') IS NOT NULL
DROP TABLE #Temp
;With cte( Id , ClientDepartment , ServiceType , DateTimeServiced )
AS
(
SELECT 1 , 'Sales' , 'Networking' , '15.01.17' Union all
SELECT 2 , 'Development' , 'Networking' , '14.02.17' Union all
SELECT 3 , 'Development' , 'Networking' , '09.04.17' Union all
SELECT 4 , 'Sales' , 'Software' , '11.03.17' Union all
SELECT 5 , 'Sales' , 'Hardware' , '30.03.17' Union all
SELECT 6 , 'Development' , 'Hardware' , '15.04.17'
)
SELECT ClientDepartment,
ServiceType,
COUNT(ServiceType)OVER(Partition by ClientDepartment,ServiceType Order by Id) AS Cnt INTO #Temp FROM cte --Sample data created here
DECLARE #Column1 nvarchar(max),
#Sql nvarchar(max),
#Column2 nvarchar(max)
SELECT #Column1=STUFF((SELECT DISTINCT ', '+ 'ISNULL('+ServiceType +',''0'') AS '+ServiceType FROM #Temp
FOR XML PATH ('')),1,1,'')
SELECT #Column2=STUFF((SELECT DISTINCT ', '+ +QUOTENAME(ServiceType) FROM #Temp
FOR XML PATH ('')),1,1,'')
SET #Sql='
SELECT *,'+REPLACE(#Column2,',','+')+' AS Total FROM
(
SELECT ClientDepartment AS ''Client\Service'','+#Column1 +' FROM
(
SELECT * FROm #Temp
)AS SRc
PIVOT
(
MAX(Cnt) FOR ServiceType IN ('+#Column2+')
)AS PVT
)DT
Order by 1 desc
'
PRINT #Sql
EXEC(#Sql)
OutPut
Client\Service Hardware Networking Software Total
---------------------------------------------------------
Sales 1 1 1 3
Development 1 2 0 3
If I have a table of cases:
CASE_NUMBER | CASE_ID | STATUS | SUBJECT |
----------------------------------------------------------------
3108 | 123456 | Closed_Billable | Something Interesting
3109 | 325124 | Closed_Billable | Broken printer
3110 | 432432 | Open_Assigned | Email not working
And a table of calls:
PARENT_ID | STATUS | DUR(H) | DUR(M) | SUBJECT
---------------------------------------------------------------
123456 | Held | 1 | 30 | Initial discussion
123456 | Cancelled | 0 | 0 | Walk user through
123456 | Held | 0 | 45 | Remote debug session
325124 | Held | 1 | 0 | Consultation
325124 | Held | 1 | 15 | Needs assessment
432432 | Held | 1 | 30 | Support call
And a table of meetings:
PARENT_ID | STATUS | DUR(H) | DUR(M) | SUBJECT
-------------------------------------------------------
123456 | Held | 3 | 15 | On-site work
325124 | Held | 2 | 0 | Un-jam printer
432432 | Held | 1 | 0 | Reconnect network
How do I do a select with these parameters (this is not working code, obviously):
SELECT cases.case_number, cases.subject, calls.subject, meetings.subject
WHERE cases.status="Closed_Billable" AND (calls.status="Held" OR meetings.status="Held)
LEFT JOIN cases
ON cases.case_id = calls.parent_id
LEFT JOIN cases
ON cases.case_id = meetings.parent_id
and end up with a "faked" nested table like:
CASE_NUMBER | CASE SUBJECT | # CALLS | # MEETINGS | CALL SUBJECT | MEETING SUBJECT | DURATION (H) | DURATION (M) | TOTAL
-----------------------------------------------------------------------------------------------------------------------------------------
3108 | Something Interesting | 2 | 1 | | | | | 5.5H
| | | | Initial Discussion | | 1 | 30 |
| | | | Remote Debug Session | | 0 | 45 |
| | | | | On-site work | 3 | 15 |
3109 | Broken printer | 2 | 1 | | | | | 4.25H
| | | | Consultation | | 1 | 0 |
| | | | Needs assessment | | 1 | 15 |
| | | | | Un-jam printer | 2 | 0 |
I've tried joins and subqueries the best I can figure out, but I get repeated entries - for example, each Meeting in a Case will show say 3 times, once for each Call in that case.
I'm stumped! Obviously there's other fields I'm pulling here, and doing COUNTs of Calls and Meetings, and SUMs of their durations, but I'd be happy just to show a table/sub-table like this.
Is it even possible?
Thanks,
David.
Assembling a query result in the exact format you want is .. somewhat of a pain. It can be done, but presentation stuff like that is best left to the application.
That said, this will do what you want:
select case when case_id > floor(case_id) then ''
else case_number
end case_number,
coalesce(q1.c, '') calls,
coalesce(q2.c, '') meetings,
coalesce(calls.subject, '') `call subject`,
coalesce(meetings.subject, '') `meeting subject`,
case when calls.subject is not null then calls.dhour
when meetings.subject is not null then meetings.dhour
else ''
end dhour,
case when calls.subject is not null then calls.dmin
when meetings.subject is not null then meetings.dmin
else ''
end dhour,
coalesce(q3.total, '') total
from
(
select case_number, case_id
from cases where status = 'Closed_Billable'
union select case_number, concat(case_id, '.1')
from cases where status = 'Closed_Billable'
union select case_number, concat(case_id, '.2')
from cases where status = 'Closed_Billable'
) main
left join
(select parent_id, count(*) c
from calls
where status != 'Cancelled'
group by parent_id ) q1
on q1.parent_id = case_id
left join
(select parent_id, count(*) c
from meetings
group by parent_id) q2
on q2.parent_id = case_id
left join
(select parent_id, sum(dhour + m) total
from
(select parent_id, dhour, dmin / 60 m
from calls
where status != 'Cancelled'
union all
select parent_id, dhour, dmin / 60 m
from meetings
) qq
group by parent_id
) q3
on q3.parent_id = case_id
left join calls
on concat(calls.parent_id, '.1') = main.case_id
left join meetings
on concat(meetings.parent_id, '.2') = main.case_id
order by case_id asc
Note, i've renamed your duration fields because i dislike the parenthesis in them.
We have to mangle the case_id a little bit inside the query in order to be able to get you your blank rows / fields - those are what makes the query cumbersome
There's a demo here: http://sqlfiddle.com/#!9/d59d4/21
edited code to work with different schema in comment fiddle
select case when case_id > floor(case_id) then ''
else case_number
end case_number,
coalesce(q1.c, '') calls,
coalesce(q2.c, '') meetings,
coalesce(calls.name, '') `call subject`,
coalesce(meetings.name, '') `meeting subject`,
case when calls.name is not null then calls.duration_hours
when meetings.name is not null then meetings.duration_hours
else ''
end duration_hours,
case when calls.name is not null then calls.duration_minutes
when meetings.name is not null then meetings.duration_minutes
else ''
end duration_hours,
coalesce(q3.total, '') total
from
(
select case_number, id as case_id
from cases where status = 'Closed_Billable'
union select case_number, concat(id, '.1') as case_id
from cases where status = 'Closed_Billable'
union select case_number, concat(id, '.2') as case_id
from cases where status = 'Closed_Billable'
) main
left join
(select parent_id, count(*) c
from calls
where status != 'Cancelled'
group by parent_id ) q1
on q1.parent_id = case_id
left join
(select parent_id, count(*) c
from meetings
group by parent_id) q2
on q2.parent_id = case_id
left join
(select parent_id, sum(duration_hours + m) total
from
(select parent_id, duration_hours, duration_minutes / 60 m
from calls
where status != 'Cancelled'
union all
select parent_id, duration_hours, duration_minutes / 60 m
from meetings
) qq
group by parent_id
) q3
on q3.parent_id = case_id
left join calls
on concat(calls.parent_id, '.1') = main.case_id
left join meetings
on concat(meetings.parent_id, '.2') = main.case_id
order by case_id asc
You can't really get final results like that without some seriously ugly "wrapper" queries, of this sort:
SET #prevCaseNum := 'blahdyblahnowaythisshouldmatchanything';
SET #prevCaseSub := 'seeabovetonotmatchanything';
SELECT IF(#prevCaseNum = CASE_NUMBER, '', CASE_NUMBER) AS CASE_NUMBER
, IF(#prevCaseNum = CASE_NUMBER AND #prevCaseSubject = CASE_SUBJECT, '', CASE_SUBJECT) AS CASE_SUBJECT
, etc.....
, #prevCaseNum := CASE_NUMBER AS prevCaseNum
, #prevCaseSubject = CASE_SUBJECT AS prevCaseSub
, etc....
FROM ( [the real query] ORDER BY CASE_NUMBER, etc....) AS trq
;
And then wrap all that with another select to strip the prevCase fields.
And even this still won't give you the blanks you want on the "upper right".
I got table orders and order_comments. Each order can have from 0 to n comments. I would like to get list of all orders with their comments in a sepcific order.
Table orders:
order_id | order_nr
1 | 5252
4 | 6783
5 | 6785
Table order_comments
id_order_comments | order_fk | created_at | email | content
1 | 4 | 2015-01-12 | jack | some text here
2 | 5 | 2015-01-13 | marta | some text here
3 | 5 | 2015-01-14 | beata | some text here
4 | 4 | 2015-01-16 | julia | some text here
As a result, I would like to get 1 row for each order. Comments should be shown in separate columns, starting from the oldest comment. So desired output in this case is:
order_id | 1_comment_created_at | 1_comment_author | 1_comment_content | 2_comment_created_at | 2_comment_author | 2_comment_content
1 | NULL | NULL | NULL | NULL | NULL | NULL
4 | 2015-01-12 | jack | some text here | 2015-01-16 | Julia | some text here
5 | 2015-01-13 | marta | some text here | 2015-01-14 | beata | some text here
I found this: MySQL - Rows to Columns - but I cannot use 'create view'.
I found this: http://dev.mysql.com/doc/refman/5.5/en/while.html - but I cannot create procedure in this db.
What I got:
SELECT #c := (SELECT count(*) FROM order_comments GROUP BY order_fk ORDER BY count(*) DESC LIMIT 1);
SET #rank=0;
SET #test=0;
SELECT
CASE WHEN #test < #c AND temp.comment_id = #test THEN temp.created_at END AS created,
CASE WHEN #test < #c AND temp.comment_id = #test THEN temp.author END AS author,
CASE WHEN #test < #c AND temp.comment_id = #test THEN temp.content END AS content
/*But I cannot set #test as +1. And I cannot name column with variable - like CONCAT(#test, '_created')*/
FROM (
SELECT #rank := #rank +1 AS comment_id, created_at, author, content
FROM order_comments
WHERE order_fk = 4
ORDER BY created_at
) AS temp
Problem: I would like to search more than 1 order. I should get orders with no comments too.
What can I do?
You can use variables for this type of pivot, but the query is a bit more complicated, because you need to enumerate the values for each order:
SELECT o.order_id,
MAX(case when rank = 1 then created_at end) as created_at_1,
MAX(case when rank = 1 then email end) as email_1,
MAX(case when rank = 1 then content end) as content_1,
MAX(case when rank = 2 then created_at end) as created_at_2,
MAX(case when rank = 2 then email end) as email_2,
MAX(case when rank = 2 then content end) as content_2,
FROM orders o LEFT JOIN
(SELECT oc.*,
(#rn := if(#o = order_fk, #rn + 1,
if(#o := order_fk, 1, 1)
)
) as rank
FROM order_comments oc CROSS JOIN
(SELECT #rn := 0, #o := 0) vars
ORDER BY order_fk, created_at
) oc
ON o.order_id = oc.order_fk
GROUP BY o.order_id;
For the following Table, is it possible, to create an SQL Statement to create a data Matrix or view?
Table:
TeamA|TeamB|Won|Lost
--------------------
A | B | 5 | 3
A | C | 2 | 4
A | D | 9 | 1
B | E | 5 | 5
C | A | 2 | 4
Result-Matrix:
| A | B | C | D | E
----------------------------
A | 0 | 2 | -2 | 8 | 0
B | 0 | 0 | 0 | 0 | 0
C | -2 | 0 | 0 | 0 | 0
There are two ways that you can pivot data in MySQL. If you know the values ahead of time (teams) then you will hard-code the values or you can use a prepared statement to generate dynamic sql.
A static version would be:
select TeamA,
max(case when TeamB = 'A' then won - lost else 0 end) as A,
max(case when TeamB = 'B' then won - lost else 0 end) as B,
max(case when TeamB = 'C' then won - lost else 0 end) as C,
max(case when TeamB = 'D' then won - lost else 0 end) as D,
max(case when TeamB = 'E' then won - lost else 0 end) as E
from yourtable
group by TeamA;
See SQL Fiddle with Demo
If you want to use a dynamic version with a prepared statement, the code would be:
SET #sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'MAX(CASE WHEN TeamB = ''',
TeamB,
''' THEN won - lost else 0 END) AS `',
TeamB, '`'
)
) INTO #sql
from
(
select *
from yourtable
order by teamb
) x;
SET #sql
= CONCAT('SELECT TeamA, ', #sql, '
from yourtable
group by TeamA');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
See SQL Fiddle with Demo.
Edit #1, after thinking about this I would actually do this a slight bit different. I would generate a true matrix os the data where the teams appeared in both the row and the column. To do this you would first use a UNION ALL query to get all teams in two columns:
select teama Team1, teamb Team2,
won-lost Total
from yourtable
union all
select teamb, teama,
won-lost
from yourtable
See SQL Fiddle with Demo. Once that is done, then you would pivot the data:
select Team1,
coalesce(max(case when Team2 = 'A' then Total end), 0) as A,
coalesce(max(case when Team2 = 'B' then Total end), 0) as B,
coalesce(max(case when Team2 = 'C' then Total end), 0) as C,
coalesce(max(case when Team2 = 'D' then Total end), 0) as D,
coalesce(max(case when Team2 = 'E' then Total end), 0) as E
from
(
select teama Team1, teamb Team2,
won-lost Total
from yourtable
union all
select teamb, teama,
won-lost
from yourtable
) src
group by Team1;
See SQL Fiddle with Demo. Which gives a more detailed result of:
| TEAM1 | A | B | C | D | E |
-------------------------------
| A | 0 | 2 | -2 | 8 | 0 |
| B | 2 | 0 | 0 | 0 | 0 |
| C | -2 | 0 | 0 | 0 | 0 |
| D | 8 | 0 | 0 | 0 | 0 |
| E | 0 | 0 | 0 | 0 | 0 |
You cannot create a SQL statement or view that has a variable number of columns. In standard SQL, you can pivot the data by doing something like:
select TeamA,
max(case when TeamB = 'A' then won - lost end) as A,
max(case when TeamB = 'B' then won - lost end) as B,
max(case when TeamB = 'C' then won - lost end) as C,
max(case when TeamB = 'D' then won - lost end) as D,
max(case when TeamB = 'E' then won - lost end) as E
from t
group by TeamA
order by 1
Some databases support a pivot statement.
To do this generically, you would have to create a SQL statement as a string, and then execute it (often called dynamic SQL). Such a statement could be produced by SQL, by a stored procedure, in Excel, or some other programming tool. It then would need to be executed.
Let me repeat: any given SQL statement has a pre-defined set of columns. You cannot vary the number of columns.