MySQL开发技巧(二)

MySQL-行转列

在这里插入图片描述

MySQL-行转列场景介绍

表user1:

CREATE TABLE `user1` (
  `id` int(11) NOT NULL,
  `user_name` varchar(3) DEFAULT NULL,
  `over` varchar(5) DEFAULT NULL,
  `mobile` varchar(100) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

在这里插入图片描述
在这里插入图片描述
表user2:

CREATE TABLE `user2` (
  `id` int(11) NOT NULL,
  `user_name` varchar(3) DEFAULT NULL,
  `over` varchar(5) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

在这里插入图片描述
在这里插入图片描述
表user_kills:

CREATE TABLE `user_kills` (
  `id` int(11) NOT NULL,
  `user_id` int(11) DEFAULT NULL,
  `Timestr` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
  `kills` decimal(11,0) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

在这里插入图片描述
在这里插入图片描述

MySQL-使用自连接的方法实现多行转多列

实例:由姓名将打怪数进行汇总
在这里插入图片描述
由左表生成右表

  1. 生成带姓名的杀怪数目表
SELECT
	a.user_name,
	b.kills
FROM
	user1 a
JOIN user_kills b ON a.id = b.user_id

在这里插入图片描述

  1. 生成杀怪总和表
SELECT
	a.user_name,
	sum(b.kills)
FROM
	user1 a
JOIN user_kills b ON a.id = b.user_id
GROUP BY a.user_name

在这里插入图片描述
3. 行显转化成列显

3.1 只显示孙悟空的打怪数

SELECT
	a.user_name,
	sum(b.kills)
FROM
	user1 a
JOIN user_kills b ON a.id = b.user_id and a.user_name = '孙悟空'

在这里插入图片描述

3.2 只显示猪八戒的打怪数

SELECT
	a.user_name,
	sum(b.kills)
FROM
	user1 a
JOIN user_kills b ON a.id = b.user_id and a.user_name = '猪八戒'

在这里插入图片描述
3.3 只显示沙僧的打怪数

SELECT
	a.user_name,
	sum(b.kills)
FROM
	user1 a
JOIN user_kills b ON a.id = b.user_id and a.user_name = '沙僧'

在这里插入图片描述
3.4 把多个sql合并成一个sql

SELECT
	*
FROM
	(
		SELECT
			sum(b.kills) AS '孙悟空'
		FROM
			user1 a
		JOIN user_kills b ON a.id = b.user_id
		AND a.user_name = '孙悟空'
	) a
CROSS JOIN (
	SELECT
		sum(b.kills) AS '猪八戒'
	FROM
		user1 a
	JOIN user_kills b ON a.id = b.user_id
	AND a.user_name = '猪八戒'
) b
CROSS JOIN (
	SELECT
		sum(b.kills) AS '沙僧'
	FROM
		user1 a
	JOIN user_kills b ON a.id = b.user_id
	AND a.user_name = '沙僧'
) c

在这里插入图片描述

MySQL-使用CASE方法实现行转列

实例:由姓名将打怪数进行汇总
在这里插入图片描述
由左表生成右表

SELECT
	SUM(
		CASE
		WHEN
			user_name = '孙悟空' THEN
				kills
			END
	) AS '孙悟空',
	SUM(
		CASE
		WHEN
			user_name = '猪八戒' THEN
				kills
			END
	) AS '猪八戒',
	SUM(
		CASE
		WHEN
			user_name = '沙僧' THEN
				kills
			END
	) AS '沙僧'
FROM
	user1 a
JOIN user_kills b ON a.id = b.user_id;

在这里插入图片描述

MySQL-单列转多行

MySQL-使用序列化表的方法实现单列转多行

单列转成多行
在这里插入图片描述
左表转右表
在这里插入图片描述

CREATE TABLE `tb_sequence` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;

在这里插入图片描述

SELECT
	user_name,
	REPLACE (
		substring(
			substring_index(mobile, ',', a.id),
			char_length(
				substring_index(mobile, ',', a.id - 1)
			) + 1
		),
		',',
		''
	) AS mobile
FROM
	tb_sequence a
CROSS JOIN (
	SELECT
		user_name,
		concat(mobile, ',') AS mobile,
		length(mobile) - length(REPLACE(mobile, ',', '')) + 1 size
	FROM
		user1 b
) b ON a.id <= b.size

在这里插入图片描述

1.1 子查询:在mobile结尾处增加一个逗号,size计算mobile中有多少个逗号

SELECT
		user_name,
		concat(mobile, ',') AS mobile,
		length(mobile) - length(REPLACE(mobile, ',', '')) + 1 size
	FROM
		user1 b

在这里插入图片描述
1.2 利用序列表和cross join,每一行都生成size数目的行

SELECT
	*
FROM
	tb_sequence a
CROSS JOIN (
	SELECT
		user_name,
		concat(mobile, ',') AS mobile,
		length(mobile) - length(REPLACE(mobile, ',', '')) + 1 size
	FROM
		user1 b
) b ON a.id <= b.size

在这里插入图片描述
1.3 字符串截取得到结果

SELECT
	user_name,
	REPLACE (
		substring(
			substring_index(mobile, ',', a.id),
			char_length(
				substring_index(mobile, ',', a.id - 1)
			) + 1
		),
		',',
		''
	) AS mobile
FROM
	tb_sequence a
CROSS JOIN (
	SELECT
		user_name,
		concat(mobile, ',') AS mobile,
		length(mobile) - length(REPLACE(mobile, ',', '')) + 1 size
	FROM
		user1 b
) b ON a.id <= b.size

替换user1的表就可以完成列转多行的工作了
在这里插入图片描述
mysql 函数substring_index()

MySQL-多列转多行

MySQL-使用UNION的方法实现多列转多行

多列转多行
在这里插入图片描述
左表转右表

取经四人组的装备表user1_equipment:
在这里插入图片描述

CREATE TABLE `user1_equipment` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL,
  `arms` varchar(10) DEFAULT NULL,
  `clothing` varchar(10) DEFAULT NULL,
  `shoe` varchar(10) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

在这里插入图片描述

1.1 装备表user1_equipment与user1表关联得到左表
在这里插入图片描述

SELECT user_name,arms,clothing,shoe from user1 a join user1_equipment b on a.id=b.user_id

在这里插入图片描述
1.2 只显示装备列

SELECT
	user_name,
	arms
FROM
	user1 a
JOIN user1_equipment b ON a.id = b.user_id

在这里插入图片描述

1.3 增加一个分类项

SELECT
	user_name,
	'arms' AS equipment,
	arms AS eq_name
FROM
	user1 a
JOIN user1_equipment b ON a.id = b.user_id

在这里插入图片描述
1.4 用union进行合并

SELECT
	user_name,
	'arms' AS equipment,
	arms AS eq_name
FROM
	user1 a
JOIN user1_equipment b ON a.id = b.user_id
UNION ALL
	SELECT
		user_name,
		'clothing' AS equipment,
		clothing AS eq_name
	FROM
		user1 a
	JOIN user1_equipment b ON a.id = b.user_id
	UNION ALL
		SELECT
			user_name,
			'eq_name' AS equipment,
			shoe AS eq_name
		FROM
			user1 a
		JOIN user1_equipment b ON a.id = b.user_id
ORDER BY user_name

在这里插入图片描述

MySQL-使用序列化表的方法实现多列转多行

多列转多行
在这里插入图片描述
左表转右表

1.1 装备表user1_equipment与user1表关联得到左表
在这里插入图片描述

SELECT user_name,arms,clothing,shoe from user1 a join user1_equipment b on a.id=b.user_id

在这里插入图片描述
1.2 利用序列表tb_sequence和cross join将一行转成三行

SELECT
	user_name,
	arms,
	clothing,
	shoe
FROM
	user1 a
JOIN user1_equipment b ON a.id = b.user_id
CROSS JOIN tb_sequence c
WHERE
	c.id <= 3
ORDER BY
	user_name

在这里插入图片描述
1.3 利用case when then end当序列表id=1时取arms值,当序列表id=2时取clothing值,当序列表id=3时取shoe值

SELECT
	user_name,
	CASE
WHEN c.id = 1 THEN
	arms
END,
 CASE
WHEN c.id = 2 THEN
	clothing
END,
 CASE
WHEN c.id = 3 THEN
	shoe
END
FROM
	user1 a
JOIN user1_equipment b ON a.id = b.user_id
CROSS JOIN tb_sequence c
WHERE
	c.id <= 3
ORDER BY
	user_name

在这里插入图片描述
1.4 利用coalesce取不为空的项

SELECT
	user_name,
	COALESCE (
		CASE
		WHEN c.id = 1 THEN
			arms
		END,
		CASE
	WHEN c.id = 2 THEN
		clothing
	END,
	CASE
WHEN c.id = 3 THEN
	shoe
END
	) eq_name
FROM
	user1 a
JOIN user1_equipment b ON a.id = b.user_id
CROSS JOIN tb_sequence c
WHERE
	c.id <= 3
ORDER BY
	user_name

在这里插入图片描述
1.5 利用case when then end显示装备类型列

SELECT
	user_name,
	COALESCE (
		CASE
		WHEN c.id = 1 THEN
			'arms'
		END,
		CASE
	WHEN c.id = 2 THEN
		'clothing'
	END,
	CASE
WHEN c.id = 3 THEN
	'shoe'
END
	) equipment,
	COALESCE (
		CASE
		WHEN c.id = 1 THEN
			arms
		END,
		CASE
	WHEN c.id = 2 THEN
		clothing
	END,
	CASE
WHEN c.id = 3 THEN
	shoe
END
	) eq_name
FROM
	user1 a
JOIN user1_equipment b ON a.id = b.user_id
CROSS JOIN tb_sequence c
WHERE
	c.id <= 3
ORDER BY
	user_name

在这里插入图片描述

如何生成唯一序列号

MySQL-在数据库中生成唯一序列号的常用方法

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
使用系统自增的方式生成序列号会产生序列号的空洞,序列号会在删除3这一行后缺少3,变为序列号1,2,4

如何使用SQL语句建立特殊需求的序列号

在这里插入图片描述
前面八位是年月日,后年是随机序列,在一天内随机序列不能重复
order_seq表:

CREATE TABLE `order_seq` (
  `timestr` varchar(11) DEFAULT '',
  `order_sn` varchar(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

在这里插入图片描述

DECLARE v_cnt INT;
DECLARE v_timestr INT;
DECLARE rowcount BIGINT;
SET v_timestr=DATE_FORMAT(NOW(),'%Y%m%d');
SELECT ROUND(RAND()*100,0)+1 INTO v_cnt;
START TRANSACTION;
UPDATE order_seq SET order_sn = order_sn + v_cnt WHERE timestr=v_timestr;
if ROW_COUNT()=0 THEN
	INSERT INTO order_seq(timestr,order_sn) VALUES(v_timestr,v_cnt);
end if;
SELECT CONCAT(v_timestr,LPAD(order_sn,7,0)) as order_sn
FROM order_seq WHERE timestr=v_timestr;
COMMIT;

MySQL-利用主键删除重复数据

在这里插入图片描述
在这里插入图片描述
表user1_test:

CREATE table user1_test (
	id INT auto_increment NOT NULL,
	user_name VARCHAR (3),
	over VARCHAR (5),
	mobile VARCHAR (100),
	PRIMARY KEY (id)
);

INSERT INTO user1_test (user_name, over, mobile) SELECT
	user_name,
	over,
	mobile
FROM
	user1;

INSERT INTO user1_test (user_name, over, mobile) SELECT
	user_name,
	over,
	mobile
FROM
	user1
LIMIT 2;

在这里插入图片描述

删除表user1_test中重复的行

1.1 列出重复的数据
在这里插入图片描述

SELECT
	user_name,
over,
	count(*)
FROM
	user1_test
GROUP BY
	user_name,over
HAVING
	count(*) > 1

在这里插入图片描述
在这里插入图片描述

DELETE a
FROM
	user1_test a
JOIN (
	SELECT
		user_name,
		count(*),
		max(id) AS id
	FROM
		user1_test
	GROUP BY
		user_name
	HAVING
		count(*) > 1
) b ON a.user_name = b.user_name
WHERE
	a.id < b.id

select * from user1_test;

在这里插入图片描述

MySQL-如何处理复杂的重复数据删除

在这里插入图片描述
1.1 准备好要用的表

create table user1_practice(id int not null auto_increment primary key,

user_name varchar(3),

over varchar(5),

mobile varchar(100));

insert into user1_practice(user_name,over,mobile) values ('唐僧','旃檀功德佛','12112345678,14112345678,12112345678');

insert into user1_practice(user_name,over,mobile) values ('猪八戒','净坛使者','12144643321,14144643321');

insert into user1_practice(user_name,over,mobile) values ('孙悟空','斗战胜佛','12166666666,14166666666,18166666666,18166666666');

insert into user1_practice(user_name,over,mobile) values ('沙僧','金身罗汉','12198343214,14198343214');

1.2 建一个序列表

create tb_sequence(id int not null auto_increment primary key);

insert into tb_sequence values(),(),(),(),(),(),(),(),();

1.3 列转行后的表user1_trans1

create table user1_trans1 as 

select a.id,user_name,over,

replace(substring(substring_index(mobile,',',a.id),char_length(substring_index(mobile,',',a.id-1))+1),',','') as mobile 

from tb_sequence a cross join(

select user_name,over,

concat(mobile,',') as mobile,

length(mobile)-length(replace(mobile,',',''))+1 as size

from user1_practice b) b on a.id <= b.size;

1.4 删除user1_trans1表中的重复记录

delete a from user1_trans1 a join (

select user_name,over,mobile,count(*),max(id) as id 

from user1_trans1  group by user_name,over,mobile having count(*) > 1  ) b

on a.user_name = b.user_name 

and a.over = b.over

and a.mobile = b.mobile

where a.id < b.id;

1.5 用group_concat函数将mobile转化成以逗号分隔的字符串

create table user1_trans2 as

select user_name,over,group_concat(mobile) as mobile from user1_trans1

group by user_name,over;

1.6 对原表user1_practice进行关联更新

update user1_practice a inner join user1_trans2 b on a.user_name = b.user_name

set a.mobile = b.mobile;