英文:
Unpivot and align rows for each field
问题
如何将表中每个字段的行和属性对齐,使其在同一行中显示,以给定的表作为示例?请注意,此表中的所有字段均为varchar,以确保一致性。
STD | SUBJECT | MARKS | COMMENT | TDATE |
---|---|---|---|---|
ST1 | MATH | 25% | POOR | 1/02/2021 |
ST1 | ENGLISH | 88% | DIST | 2/02/2021 |
ST1 | SCIENCE | 56% | PASS | 4/02/2021 |
在将[Subject]中的内容解除枢轴操作后,表格应该如下所示:
STD | MATH | COMMENT | TDATE | ENGLISH | COMMENT | TDATE | SCIENCE | COMMENT | TDATE |
---|---|---|---|---|---|---|---|---|---|
ST1 | 25% | POOR | 1/02/2021 | 88% | DIST | 2/02/2021 | 56% | PASS | 4/02/2021 |
我尝试了我的代码,并出现了许多错误,所以也许我可以得到一些帮助来实现所需的结果。
英文:
How can I align the rows and attributes of each field in a table to appear in the same line, using the given table as an example?. Mark this all fields in this table are varchar so as to enable uniformity.
STD | SUBJECT | MARKS | COMMENT | TDATE |
---|---|---|---|---|
ST1 | MATH | 25% | POOR | 1/02/2021 |
ST1 | ENGLISH | 88% | DIST | 2/02/2021 |
ST1 | SCIENCE | 56% | PASS | 4/02/2021 |
The table should appear like this after unpivoting the contents in [Subject]
:
STD | MATH | COMMENT | TDATE | ENGLISH | COMMENT | TDATE | SCIENCE | COMMENT | TDATE |
---|---|---|---|---|---|---|---|---|---|
ST1 | 25% | POOR | 1/02/2021 | 88% | DIST | 2/02/2021 | 56% | PASS | 4/02/2021 |
I tried with my code and got lots of errors, so maybe I could get some help to achieve the desired result.
SELECT
[MATH],
[ENGLISH],
[SCIENCE]
FROM (
SELECT
STD, stdn,
cont,
x,
SUBJECT
FROM
[dbo].[Exam]
UNPIVOT (
x
for cont in (COMMENT, TDATE)
) a
) a
PIVOT (
MAX(x)
FOR SUBJECT IN (
[MATH],
[ENGLISH],
[SCIENCE],
)
) p
WHERE p.stdn IN (SELECT STD FROM [dbo].[exam])
答案1
得分: 1
我认为当我使用MAX(CASE)时解决了它
SELECT [STD],
MAX(CASE WHEN SUBJECT = 'MATH' THEN MARKS ELSE '' END) MATH,
MAX(CASE WHEN SUBJECT = 'MATH' THEN COMMENT ELSE '' END) COMMENT,
MAX(CASE WHEN SUBJECT = 'MATH' THEN TDATE ELSE '' END) TDATE ,
MAX(CASE WHEN SUBJECT = 'ENGLISH' THEN MARKS ELSE '' END) ENGLISH,
MAX(CASE WHEN SUBJECT = 'ENGLISH' THEN COMMENT ELSE '' END) COMMENT,
MAX(CASE WHEN SUBJECT = 'ENGLISH' THEN TDATE ELSE '' END) TDATE ,
MAX(CASE WHEN SUBJECT = 'SCIENCE' THEN MARKS ELSE '' END) SCIENCE,
MAX(CASE WHEN SUBJECT = 'SCIENCE' THEN COMMENT ELSE '' END) COMMENT,
MAX(CASE WHEN SUBJECT = 'SCIENCE' THEN TDATE ELSE '' END) TDATE
FROM [dbo].[EXAM] GROUP BY STD
英文:
i think i solved it when i used MAX(CASE)
SELECT [STD],
MAX(CASE WHEN SUBJECT = 'MATH' THEN MARKS ELSE '' END) MATH,
MAX(CASE WHEN SUBJECT = 'MATH' THEN COMMENT ELSE '' END) COMMENT,
MAX(CASE WHEN SUBJECT = 'MATH' THEN TDATE ELSE '' END) TDATE ,
MAX(CASE WHEN SUBJECT = 'ENGLISH' THEN MARKS ELSE '' END) ENGLISH,
MAX(CASE WHEN SUBJECT = 'ENGLISH' THEN COMMENT ELSE '' END) COMMENT,
MAX(CASE WHEN SUBJECT = 'ENGLISH' THEN TDATE ELSE '' END) TDATE ,
MAX(CASE WHEN SUBJECT = 'SCIENCE' THEN MARKS ELSE '' END) SCIENCE,
MAX(CASE WHEN SUBJECT = 'SCIENCE' THEN COMMENT ELSE '' END) COMMENT,
MAX(CASE WHEN SUBJECT = 'SCIENCE' THEN TDATE ELSE '' END) TDATE
FROM [dbo].[EXAM] GROUP BY STD
答案2
得分: 0
用于演示的数据透视在SQL中并不太适合,通常最好由“前端”或“报表工具”来执行,尽管如此,仍然可以使用T-SQL来进行数据透视。
我假设一个未明示的要求是要按特定的从左到右的“块序列”“科目-分数-评论-日期”的顺序呈现列,因此需要一种方法来组织这些列的顺序。另一个可能的要求是透视要适应未知数量的总列数,为此,您需要使用“动态SQL”。
注意:由于涉及不同的数据类型,这个“逆透视”变得复杂,一种避免这个问题的方法是将所有数据转换为字符串或sql_variant
(下面是这种方法)。另一种方法(我更喜欢的方法)是使用JSON 在这里解释,但我也无法控制此查询所需的“rnk”值,以制定列的数字顺序。
因此,提出以下解决方案:
DECLARE @cols AS NVARCHAR(MAX), @query AS NVARCHAR(MAX);
SELECT @cols = STUFF((SELECT ',' + quotename(concat(c.column_name, cj.rn))
from INFORMATION_SCHEMA.COLUMNS c
cross join (
select row_number() over(order by subject) rn
from (select distinct subject from exam) e
) cj
WHERE c.TABLE_NAME = 'Exam'
AND c.column_name <> 'STD'
order by cj.rn, c.ORDINAL_POSITION
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'');
SET @query = 'SELECT STD, ' + @cols + '
from
(
SELECT STD,
concat(COLUMN_NAME,rnk) as COLUMN_NAME,
VALUE
FROM
(
SELECT STD,
CAST(SUBJECT AS sql_variant) AS SUBJECT,
CAST(MARKS AS sql_variant) AS MARKS,
CAST(COMMENT AS sql_variant) AS COMMENT,
CAST(convert(varchar(10), TDATE ,120) AS sql_variant) AS TDATE,
dense_rank() over(partition by STD order by MARKS) as rnk
FROM Exam
) AS subquery
UNPIVOT
(
VALUE
FOR COLUMN_NAME IN (SUBJECT, MARKS, COMMENT, TDATE)
) AS unpvt
) x
pivot
(
max(VALUE)
for COLUMN_NAME in (' + @cols + ')
) p ';
EXECUTE(@query);
STD | SUBJECT1 | MARKS1 | COMMENT1 | TDATE1 | SUBJECT2 | MARKS2 | COMMENT2 | TDATE2 | SUBJECT3 | MARKS3 | COMMENT3 | TDATE3 |
---|---|---|---|---|---|---|---|---|---|---|---|---|
ST1 | MATH | 25% | POOR | 2021-02-01 | SCIENCE | 56% | PASS | 2021-02-04 | ENGLISH | 88% | DIST | 2021-02-02 |
注意:由于我最终将日期列转换为字符串以控制格式,您可以将“as sql_variant”替换为“as varchar(100)”(100是一个猜测的长度)。
英文:
Pivots for presentation purposes are not well suited to SQL and often better performed by a "front end" or "reporting tool" despite this, it is possible to pivot this data using T-SQL.
I assume that an unstated requirement is that the columns are presented in a specific left-to-right "block sequence" of "subject-mark-comment-tdate" so there needs to be a way to organize the columns in that sequence. A further probable requirement is that the pivot cater for an unknown number of total columns, for this you need "dynamic sql".
Note: This "unpivot" is complicated by the fact that there are different data types involved, one way to avoid this is to cast all data to strings or sql_variant
(which is the approach seen below). Another (that I prefer) is to use JSON explained here but I wasn't also able to control the the "rnk" value as I needed for this query to formulate the numeric sequence of columns.
So, this is proposed:
DECLARE @cols AS NVARCHAR(MAX), @query AS NVARCHAR(MAX);
SELECT @cols = STUFF((SELECT ',' + quotename(concat(c.column_name, cj.rn))
from INFORMATION_SCHEMA.COLUMNS c
cross join (
select row_number() over(order by subject) rn
from (select distinct subject from exam) e
) cj
WHERE c.TABLE_NAME = 'Exam'
AND c.column_name <> 'STD'
order by cj.rn, c.ORDINAL_POSITION
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'');
SET @query = 'SELECT STD, ' + @cols + '
from
(
SELECT STD,
concat(COLUMN_NAME,rnk) as COLUMN_NAME,
VALUE
FROM
(
SELECT STD,
CAST(SUBJECT AS sql_variant) AS SUBJECT,
CAST(MARKS AS sql_variant) AS MARKS,
CAST(COMMENT AS sql_variant) AS COMMENT,
CAST(convert(varchar(10), TDATE ,120) AS sql_variant) AS TDATE,
dense_rank() over(partition by STD order by MARKS) as rnk
FROM Exam
) AS subquery
UNPIVOT
(
VALUE
FOR COLUMN_NAME IN (SUBJECT, MARKS, COMMENT, TDATE)
) AS unpvt
) x
pivot
(
max(VALUE)
for COLUMN_NAME in (' + @cols + ')
) p ';
EXECUTE(@query);
STD | SUBJECT1 | MARKS1 | COMMENT1 | TDATE1 | SUBJECT2 | MARKS2 | COMMENT2 | TDATE2 | SUBJECT3 | MARKS3 | COMMENT3 | TDATE3 |
---|---|---|---|---|---|---|---|---|---|---|---|---|
ST1 | MATH | 25% | POOR | 2021-02-01 | SCIENCE | 56% | PASS | 2021-02-04 | ENGLISH | 88% | DIST | 2021-02-02 |
nb: As I ended up converting the date column into a string to control the format, you could replace the "as sql_variant" with "as varchar(100)"(length of 100 is a guess)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论