【问题标题】:Convert Rows to Columns in MySQL Dynamically在 MySQL 中动态将行转换为列
【发布时间】:2014-01-02 00:07:25
【问题描述】:

我遇到了 MySQL 的问题。我想有基于行的动态列。以下是详细信息

SELECT `marks`.`id` , `marks`.`studentID` , `marks`.`subjectID` , `marks`.`mark`
FROM `Mark` `marks`
LEFT OUTER JOIN `Student` `students` ON ( `students`.`id` = `marks`.`studentID` )
WHERE (
`students`.`classID` =1
)
LIMIT 0 , 30

My Output is 
+----+-----------+-----------+------+
| id | studentID | subjectID | mark |
+----+-----------+-----------+------+
|  1 |         1 |         1 |   20 |
|  2 |         1 |         2 |   36 |
|  3 |         2 |         1 |   47 |
|  4 |         2 |         2 |   43 |
+----+-----------+-----------+------+
4 rows in set (0.00 sec)


Output I need is 

+----+-----------+-----------+-----------+
| id | studentID | subject_1 | subject_2 |
+----+-----------+-----------+-----------+
|  1 |         1 |        20 |        36 |
|  2 |         2 |        47 |        43 |
+----+-----------+-----------+-----------+
4 rows in set (0.00 sec)

主题的数量取决于主题表中的条目。我只需要每个用户一行显示所有标记。这是我使用的表结构。

--
-- Table structure for table `Mark`
--

CREATE TABLE IF NOT EXISTS `Mark` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `studentID` int(11) NOT NULL,
  `subjectID` int(11) NOT NULL,
  `mark` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB

--
-- Table structure for table `Student`
--

CREATE TABLE IF NOT EXISTS `Student` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(45) NOT NULL,
  `classID` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB

--
-- Table structure for table `Subject`
--

CREATE TABLE IF NOT EXISTS `Subject` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(45) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB

先谢谢了。

【问题讨论】:

    标签: mysql sql yii pivot


    【解决方案1】:

    您不能拥有动态列,至少不能动态生成 SQL。您可以按照此答案在存储过程中构建 SQL

    MySQL pivot table query with dynamic columns

    或者,在您的应用程序代码中执行此操作可能更简单,方法是在一个查询中选择不同的主题并使用该结果集构建检索您所追求的结果集的 SQL。至少通过应用程序代码中的逻辑,您对您将在结果集中看到多少列有所了解。

    【讨论】:

      【解决方案2】:

      根据您想要的输出,我假设每个 studentID 有 2 行,并且 2 行中的每一行都有 subjectID 1 或 2。

      你能试试这个吗?

      使用 JOIN

      SELECT t1.studentID, t1.mark AS subject_1, t2.mark AS subject_2
      FROM
      (
          SELECT `marks`.`studentID` , `marks`.mark
          FROM `Mark` as `marks`
          LEFT OUTER JOIN `Student` AS `students`
              ON ( `students`.`id` = `marks`.`studentID` AND subjectID = 1)
          WHERE `students`.`classID` =1
      ) t1 INNER JOIN 
      (
      
          SELECT `marks`.`studentID` , `marks`.mark
          FROM `Mark` as `marks`
          LEFT OUTER JOIN `Student` AS `students`
              ON ( `students`.`id` = `marks`.`studentID` AND subjectID = 2)
          WHERE `students`.`classID` =1
      ) t2 ON t1.studentID = t2.studentID;
      

      使用交叉制表

      SELECT `marks`.`studentID`, 
          SUM(IF(subjectID = 1, mark, 0)) AS subject_1,
          SUM(IF(subjectID = 2, 0, mark)) AS subject_2
      FROM `Mark` as `marks`
      LEFT OUTER JOIN `Student` AS `students`
          ON ( `students`.`id` = `marks`.`studentID`)
      WHERE `students`.`classID` =1
      GROUP BY marks.studentID
      

      JOINCROSS TABULATION 是将垂直结果转换为水平结果的一般形式(我的知识有限)

      【讨论】:

        【解决方案3】:

        试试这个 sqlFiddle demo

        SELECT `marks`.`studentID` ,
          GROUP_CONCAT(if(`marks`.`subjectID`=1,`marks`.`mark`,NULL)) AS subject_1,
          GROUP_CONCAT(if(`marks`.`subjectID`=2,`marks`.`mark`,NULL)) AS subject_2
        FROM `Mark` `marks`
        LEFT OUTER JOIN `Student` `students` ON ( `students`.`id` = `marks`.`studentID` )
        WHERE (
        `students`.`classID` =1
        )
        GROUP BY `marks`.`studentID`
        

        如果有更多主题,只需添加更多带有不同subjectIDGROUP_CONCAT

        下面是一个使用存储过程的示例,并使用游标动态构建查询 sqlFiddle

        DROP PROCEDURE IF EXISTS  getMarks//
        CREATE PROCEDURE getMarks (IN INclassID INT)
        BEGIN
          -- First we declare all the variables we will need
          DECLARE loopSubjectId INT;
          DECLARE dynamicSql VARCHAR(5000);
          DECLARE finalSql VARCHAR(5000);
          -- flag which will be set to true, when cursor reaches end of table
          DECLARE exit_loop BOOLEAN;         
        
          -- Declare the sql for the cursor
          DECLARE example_cursor CURSOR FOR
            SELECT DISTINCT subjectID
            FROM Student s, Mark m
            WHERE s.id = m.studentID
            AND s.classID = INclassID;
        
          -- Let mysql set exit_loop to true, if there are no more rows to iterate
          DECLARE CONTINUE HANDLER FOR NOT FOUND SET exit_loop = TRUE;
          SET dynamicSql = '';
          SET finalSql = '';
          -- open the cursor
          OPEN example_cursor;
        
          -- marks the beginning of the loop
          example_loop: LOOP
        
            -- read the name from next row into the variable l_name
            FETCH  example_cursor INTO loopSubjectId;
            -- check if the exit_loop flag has been set by mysql, 
            -- if it has been set we close the cursor and exit 
            -- the loop
            IF exit_loop THEN
                CLOSE example_cursor;
                LEAVE example_loop;
            END IF;
            SET DynamicSql = CONCAT(DynamicSql,',GROUP_CONCAT(if(marks.subjectID=',loopSubjectId,',marks.mark,NULL)) AS subject_',loopSubjectId);
          END LOOP example_loop;
          SET finalSql = CONCAT('SELECT students.name,marks.studentID',DynamicSql,
                                 ' FROM Mark marks
                                   LEFT OUTER JOIN Student students ON (students.id = marks.studentID)
                                   WHERE (students.classID=?)
                                   GROUP BY marks.studentID');
          -- now we run set some variables and run the dynamically built query
          SET @finalSql = finalSql;
          SET @INClassId = INClassID;
          PREPARE stmt1 FROM @finalSql;
          EXECUTE stmt1 USING @INclassID;
        END//
        

        此代码结构来自this example

        【讨论】:

        • 你是对的动态做你必须编写一些存储过程来根据返回的 subjectId 的数量动态构建 sql。
        猜你喜欢
        • 2021-06-20
        • 2022-01-21
        相关资源
        最近更新 更多