SQL row and column conversion

SQL row and column conversion is used more frequently, and there are often such topics in interviews. Several common methods of mysql and sql server are summarized. Please correct me if there are any mistakes.

 

MYSQL row to column

CREATE TABLE `tbl_grade` (
  `id` bigint(10) NOT NULL AUTO_INCREMENT,
  `name` varchar(10) DEFAULT NULL,
  `course` varchar(10) DEFAULT NULL,
  `score` decimal(5,0) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

 

INSERT INTO tbl_grade (`name`, `course`, `score`) VALUES ('Zhang San', 'language', '74');
INSERT INTO tbl_grade (`name`, `course`, `score`) VALUES ('Zhangsan', 'Math', '55');
INSERT INTO tbl_grade (`name`, `course`, `score`) VALUES ('Zhangsan', 'English', '66');
INSERT INTO tbl_grade (`name`, `course`, `score`) VALUES ('Li Si', 'language', '88');
INSERT INTO tbl_grade (`name`, `course`, `score`) VALUES ('Li Si', 'Math', '76');
INSERT INTO tbl_grade (`name`, `course`, `score`) VALUES ('Li Si', 'English', '69');
INSERT INTO tbl_grade (`name`, `course`, `score`) VALUES ('Wang Wu', 'language', '11');
INSERT INTO tbl_grade (`name`, `course`, `score`) VALUES ('Wang Wu', 'Math', '45');
INSERT INTO tbl_grade (`name`, `course`, `score`) VALUES ('Wang Wu', 'English', '68');

 

 

 

Method 1: Use table links

 

SELECT DISTINCT a.name,
(SELECT score FROM tbl_grade b WHERE a.name = b.name AND b.course = 'language') AS 'language',
(SELECT score FROM tbl_grade b WHERE a.name = b.name AND b.course = 'math') AS 'math',
(SELECT score FROM tbl_grade b WHERE a.name = b.name AND b.course = 'English') AS 'English',
SUM(score) AS 'total score',
cast(avg(score*1.0) as decimal(18,2)) as 'average score'
FROM tbl_grade a
GROUP BY a.name

 

 

Method 2: Use grouping and process each group separately

 

SELECT DISTINCT a.name,
SUM(CASE course WHEN 'language' THEN score END) AS 'language',
SUM(CASE course WHEN 'Math' THEN score END) AS 'Math',
SUM(CASE course WHEN 'English' THEN score END) AS 'English',
SUM(score) AS 'total score',
cast(avg(score*1.0) as decimal(18,2)) as 'average score'
FROM tbl_grade a
GROUP BY a.name

You can also replace case when then with if

 

 

SELECT DISTINCT a.name,
SUM(IF (course = 'language' , score , null ) ) as 'language',
SUM(IF (course = 'math' , score , null ) ) as 'math',
SUM(IF (course = 'English' , score , null ) ) as 'English',
SUM(score) AS 'total score',
cast(avg(score*1.0) as decimal(18,2)) as 'average score'
FROM tbl_grade a
GROUP BY a.name

 

 

Method 4: The stored procedure is used, which is actually the dynamization of the second method. First, the number of all courses is calculated, and then the course query is performed for each group.
Obviously, the first two methods are hard-coded, and the SQL needs to be modified after adding courses. The third does not need to modify the SQL. 

 

#You cannot delete a stored procedure in another stored procedure in MySQL
#DROP PROCEDURE IF EXISTS sp_count();
DELIMITER &&
CREATE PROCEDURE sp_count()
BEGIN
#Course Title
DECLARE course_name VARCHAR(20);
#Number of all courses
DECLARE count INT;
#counter
DECLARE i INT DEFAULT 0;
# Concatenate SQL strings
SET count = (SELECT  COUNT(distinct course) FROM tbl_grade);
SET @s = 'SELECT name';
WHILE i < count DO
SET course_name = (SELECT course FROM tbl_grade LIMIT i,1);
SET @s = CONCAT(@s,',SUM(CASE course WHEN ','\'',course_name,'\'',' THEN score END )',' AS ','\'',course_name,'\'');
SET i = i+1;
END WHILE;
SET @s = CONCAT(@s,',sum(score) as total score,cast(avg(score*1.0) as decimal(18,2)) as average score FROM tbl_grade GROUP BY name');
PREPARE stmt FROM @s;
EXECUTE stmt;
END &&

call sp_count();

 

  The results of the above methods are the same as shown in the figure below

 



 MYSQL column to row

 

 

CREATE TABLE tbl_course(
	`id` BIGINT(10) not NULL AUTO_INCREMENT,
	`name` VARCHAR(10) DEFAULT NULL,
	`language` DECIMAL(5,0) DEFAULT null,
	`math` DECIMAL(5,0) DEFAULT null,
	`English` DECIMAL(5,0) DEFAULT null,
	PRIMARY KEY (`id`)
) ENGINE = INNODB AUTO_INCREMENT = 1 DEFAULT CHARSET=utf8

 

INSERT INTO tbl_course (`name`,`Chinese`,`math`,`English`) VALUES ('Zhang San','77','44','22');
INSERT INTO tbl_course (`name`,`Chinese`,`math`,`English`) VALUES ('Li Si','55','12','85');
INSERT INTO tbl_course (`name`,`Chinese`,`math`,`English`) VALUES ('Wang Wu','85','45','96');

 


 
Method 1: Manually enumerate the columns, then union the data

SELECT tepTable.* FROM (
SELECT name, "language", language as score FROM tbl_course
union all
SELECT name, "math", math as score FROM tbl_course
union all
SELECT name, "English", English as score FROM tbl_course
) tepTable
ORDER BY tepTable.`name`

 

Method 2: Use a stored procedure to obtain the latest table fields of the tablefrom MySQL schema , and splicing SQL. In fact, it is a dynamic extension of method one.

 

DELIMITER $$
CREATE PROCEDURE sp_trans()
BEGIN
#Course Title
DECLARE course_n VARCHAR(20);
#Number of all courses
DECLARE count INT;
#counter
DECLARE i INT DEFAULT 0;
# Concatenate SQL strings
SET @s = 'SELECT * FROM (';
SET count = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA ='test' AND TABLE_NAME='tbl_course' AND COLUMN_NAME <> 'name' AND COLUMN_NAME <> 'id');
WHILE i < count DO
SET course_n = (SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA ='test' AND TABLE_NAME='tbl_course' AND COLUMN_NAME <> 'name' AND COLUMN_NAME <> 'id' LIMIT i,1);
SET @s = CONCAT(@s, ' SELECT name, "');
SET @s = CONCAT(@s, course_n);
SET @s = CONCAT(@s, '", ');
SET @s = CONCAT(@s, course_n);
SET @s = CONCAT(@s,' as score FROM tbl_course ');
IF i <> count-1 THEN
SET @s = CONCAT(@s, 'union all');
END IF;
SET i = i+1;
END WHILE;
SET @s = CONCAT(@s, ' ) tepTable ORDER BY tepTable.`name`');

PREPARE stmt FROM @s;
EXECUTE stmt;
END
$$

CALL sp_trans

 

 The query results of the two methods are the same, as shown in the following figure



 

MSSQL row to column

 

if exists
(select * from sysobjects where id = object_id('tbl_grade') and type = 'U')
   drop table tbl_grade
go

CREATE TABLE tbl_grade (
  id bigint NOT NULL identity,
  name varchar(10) DEFAULT NULL,
  course varchar(10) DEFAULT NULL,
  score decimal(5,0) DEFAULT NULL,
  PRIMARY KEY (id)
)

insert into tbl_grade VALUES ('Zhangsan','language',74)
insert into tbl_grade VALUES ('Zhangsan','Math',83)
insert into tbl_grade VALUES ('Zhangsan','English',93)
insert into tbl_grade VALUES ('Li Si','language',74)
insert into tbl_grade VALUES ('Li Si','Math',84)
insert into tbl_grade VALUES ('Li Si','English',94)
insert into tbl_grade VALUES ('Wang Wu','language',54)
insert into tbl_grade VALUES ('Wang Wu','Math',63)
insert into tbl_grade VALUES ('Wang Wu','English',72)

SELECT * FROM tbl_grade

 

SELECT name,SUM(language) AS Chinese,SUM(math) AS math,SUM(English) AS English
FROM tbl_grade g pivot( MAX(score) FOR course IN (Chinese, Math, English)) a
GROUP BY name

 

 

 

MSSQL column to row

 

if exists   
(select * from sysobjects where id = object_id('tbl_grade') and type = 'U')  
   drop table tbl_grade  
go  
  
CREATE TABLE tbl_grade (  
  id bigint NOT NULL identity,  
  name varchar(10) DEFAULT NULL,  
  language decimal(5,0) DEFAULT NULL,  
  math decimal(5,0) DEFAULT NULL,  
  English decimal(5,0) DEFAULT NULL,  
  PRIMARY KEY (id)  
)

INSERT INTO tbl_grade (name, Chinese, math, English) VALUES ('Zhang San', 99, 44, 22)
INSERT INTO tbl_grade (name, Chinese, math, English) VALUES ('Li Si', 55, 12, 85)
INSERT INTO tbl_grade (name, Chinese, math, English) VALUES ('Wang Wu', 85, 45, 96)

 

Method 1: Static union all

 

 

SELECT * FROM

(

 SELECT name,course='language',score = language FROM tbl_grade

 UNION ALL

 SELECT name,course='math',score = math FROM tbl_grade

 UNION ALL

 SELECT name,course='English',score = English FROM tbl_grade

) t

ORDER BY name,CASE course WHEN '语文' THEN 1 WHEN '数学' THEN 2 WHEN '英语' THEN 3 end
  Method 2: Obtaining the column name dynamically is the dynamization of method 1. The quotename method is used here, which is the escape identifier.

 

 

DECLARE @sql VARCHAR(8000)

SELECT @sql = isnull(@sql + ' union all ','') + ' select name, [course]='

+ quotename(Name,'''') + ' , [score] = ' + quotename(Name) + ' from tbl_grade'

FROM syscolumns

WHERE Name != 'name' AND Name != 'id' AND ID = object_id('tbl_grade')

exec(@sql + ' order by name')

go
 

 

Method 3: Use unpivot

 

SELECT name,course,score FROM tbl_grade

unpivot (score FOR course IN(Chinese, Math, English)) t

 

Method 4: Dynamic using unpivot

 

DECLARE @sql NVARCHAR(4000)

SELECT @sql = isnull(@sql + ',','') + quotename(Name)

FROM syscolumns

WHERE ID = object_id('tbl_grade') AND Name NOT IN ('name','id')

SET @sql = 'select name,[course],[score] from tbl_grade unpivot ([score] for [course] in(' + @sql + ')) b'

exec(@sql)

 

The results of the above four methods are the same, as shown in the following figure

 

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326348159&siteId=291194637