sql - 使用动态行和列创建 PIVOT - SQL Server 2016

标签 sql asp.net sql-server vb.net tsql

首先,让我们定义表:

CREATE TABLE dbo.Reviews (
  id int IDENTITY(1,1) NOT NULL PRIMARY KEY,
  reviewer char(64) NOT NULL,
  reviewee char(64) NOT NULL,
  review_date datetime NOT NULL,
)

CREATE TABLE dbo.Questions (
  id int IDENTITY(1,1) NOT NULL PRIMARY KEY,
  question_name varchar(255) NOT NULL,
  question_description varchar(500) NOT NULL,
  input_type_id int NOT NULL,
  option_group_id INT NULL
)

CREATE TABLE dbo.Input_Types (
  id int IDENTITY(1,1) NOT NULL PRIMARY KEY,
  input_type_name varchar(80) NOT NULL
)

CREATE TABLE dbo.Option_Groups (
  id int IDENTITY(1,1) NOT NULL PRIMARY KEY,
  option_group_name varchar(45) NOT NULL
)

CREATE TABLE dbo.Option_Choices (
  id int IDENTITY(1,1) NOT NULL PRIMARY KEY,
  option_group_id int NOT NULL,
  option_choice_name varchar(45) NULL,
  option_choice_value int NULL
)

CREATE TABLE dbo.Answers (
  id int IDENTITY(1,1) NOT NULL PRIMARY KEY,
  review_id int NOT NULL,
  question_id int NULL,
  answer_numeric int NULL,
  answer_text varchar(max) NULL,
  answer_yn bit NULL
)

ALTER TABLE Answers WITH CHECK ADD FOREIGN KEY(question_id) REFERENCES Questions (id)
ALTER TABLE Answers WITH CHECK ADD FOREIGN KEY(review_id) REFERENCES Reviews (id)
ALTER TABLE Option_Choices WITH CHECK ADD FOREIGN KEY(option_group_id) REFERENCES Option_Groups (id)
ALTER TABLE Questions WITH CHECK ADD FOREIGN KEY(input_type_id) REFERENCES Input_Types (id)
ALTER TABLE Questions WITH CHECK ADD FOREIGN KEY(option_group_id) REFERENCES Option_Groups (id)

以下查询获取特定评论者的所有问题答案:

SELECT r.review_date, s1.q_id, s1.question_description, s1.answer_numeric 
  FROM Reviews r 
       LEFT JOIN (SELECT a.review_id, q.id q_id ,q.question_description, a.answer_numeric
                    FROM Answers a 
                         LEFT JOIN Questions q 
                         ON a.question_id=q.id
                    WHERE a.answer_numeric IS NOT NULL
                          AND a.question_id IS NOT NULL) s1
       ON r.id=s1.review_id
   WHERE r.reviewee = 'SOMEUNIQUEIDGOESHERE'

输出看起来差不多像这样:

review_date              q_id question_description answer_numeric  
-----------------------------------------------------------------
2020-06-15 09:59:21.677  27   Role models blah     2
2020-06-15 09:59:21.677  29   Looks for blah       3
2020-06-15 09:59:21.677  30   Consistently blah    2
2020-06-26 13:58:58.420  27   Role models blah     5
2020-06-26 13:58:58.420  29   Looks for blah       4
2020-06-26 13:58:58.420  30   Consistently blah    4

我想要的是这样的输出:

q_id  question_description   2020-06-15  2020-06-26
---------------------------------------------------
27    Role models blah       2           5
29    Looks for blah         3           4
30    Consistently blah      2           4

直到运行时才知道问题和评论/评论日期的数量。

我最终是为 VB/ASP 前端编写这个查询,所以如果通过 VB 更容易处理一些动态,那是可行的。它不一定是纯 SQL 解决方案。

编辑 我尝试了以下查询:

 DECLARE @cols AS VARCHAR(MAX), @query AS VARCHAR(MAX)
 SELECT @cols = STUFF((SELECT ',', QUOTENAME(review_date) FROM Reviews WHERE reviewee = '9AA1D3BAE1E0A9FA27B4857992548665C252931263B4D407CA8BBF21DC08D800' FOR XML PATH (''), TYPE).value('.', 'VARCHAR(MAX)'),1,1,'')
 SET @query = 'SELECT q_id, question_description, ' + @cols + ' FROM
  (SELECT r.review_date, s1.q_id, s1.question_description, s1.answer_numeric 
  FROM Reviews r 
       LEFT JOIN (SELECT a.review_id, q.id q_id ,q.question_description, a.answer_numeric
                    FROM Answers a 
                         LEFT JOIN Questions q 
                         ON a.question_id=q.id
                    WHERE a.answer_numeric IS NOT NULL
                          AND a.question_id IS NOT NULL) s1
       ON r.id=s1.review_id
 WHERE r.reviewee = ''UNIQUEIDGOESHERE''
 ) x 
 PIVOT
 (
   SUM(answer_numeric)
   FOR review_date IN (' + @cols + ')
   ) P
   ORDER BY q_id'
execute(@query);

它创建了正确的列,但日期列中的结果为 NULL

最佳答案

好吧,事实证明最后一个查询是在正确的轨道上,问题是从 QUOTENAME 隐式发生的自动日期到字符串转换导致 IN 子句中没有匹配项。

最终正确查询:

 DECLARE @cols AS VARCHAR(MAX), @query AS VARCHAR(MAX)
 SELECT @cols = STUFF((SELECT ',', QUOTENAME(FORMAT(review_date, 'dd-MMM-yyyy')) FROM Reviews WHERE reviewee = '9AA1D3BAE1E0A9FA27B4857992548665C252931263B4D407CA8BBF21DC08D800' FOR XML PATH (''), TYPE).value('.', 'VARCHAR(MAX)'),1,1,'')
 SET @query = 'SELECT q_id, question_description, ' + @cols + ' FROM
  (SELECT FORMAT(r.review_date, ''dd-MMM-yyyy'') rd, s1.q_id, s1.question_description, s1.answer_numeric 
  FROM Reviews r 
       LEFT JOIN (SELECT a.review_id, q.id q_id ,q.question_description, a.answer_numeric
                    FROM Answers a 
                         LEFT JOIN Questions q 
                         ON a.question_id=q.id
                    WHERE a.answer_numeric IS NOT NULL
                          AND a.question_id IS NOT NULL) s1
       ON r.id=s1.review_id
 WHERE r.reviewee = ''UNIQUEIDGOESHERE''
 ) x 
 PIVOT
 (
   SUM(answer_numeric)
   FOR rd IN (' + @cols + ')
   ) P
   ORDER BY q_id'
execute(@query);

关于sql - 使用动态行和列创建 PIVOT - SQL Server 2016,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62629498/

相关文章:

asp.net - 使用 IIS 的 COM 接口(interface)部署网站会导致类未注册错误

mysql - 按 SUM 值范围分组 SQL

c# - SQL 服务器 2008 : update query

MySQL - 返回执行值的函数

SQL:列出用户及其电话号码和电子邮件地址

sql - 在 Access vba 中执行参数化的 T-SQL 存储过程

sql-server - 链接服务器插入选择性能

MySQL Group By 和 Sum 其它列的总值

asp.net - 如何从字符串中获取日期

asp.net - asp.net 用户登录信息存放在什么地方