赞
踩
SQL-结构化查询语言(Structured Query Language)
DML:select、insert、update、delete
1. 增加数据库处理效率,减少应用响应时间
2. 减少数据库服务器负载,增加服务器稳定性
3. 减少服务器间通讯的网络流量
SQL JOIN 子句用于把来自两个或多个表的行结合起来,基于这些表之间的共同字段。最常见的 JOIN 类型:SQL INNER JOIN(简单的 JOIN)。 SQL INNER JOIN 从多个表中返回满足 JOIN 条件的所有行
CREATE DATABASE IF NOT EXISTS `join_test` CHARACTER SET = utf8mb4;
USE `join_test`;
CREATE TABLE IF NOT EXISTS `user1` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
`user_name` varchar(30) NOT NULL DEFAULT '' COMMENT '用户名',
`over` varchar(30) NOT NULL DEFAULT '' COMMENT '最终结果',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
INSERT INTO `user1`(`user_name`,`over`) VALUES ('唐僧','旃檀功德佛');
INSERT INTO `user1`(`user_name`,`over`) VALUES ('猪八戒','净坛使者');
INSERT INTO `user1`(`user_name`,`over`) VALUES ('孙悟空','斗战圣佛');
INSERT INTO `user1`(`user_name`,`over`) VALUES ('沙僧','金身罗汉');
CREATE TABLE IF NOT EXISTS `user2` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
`user_name` varchar(30) NOT NULL DEFAULT '' COMMENT '用户名',
`over` varchar(30) NOT NULL DEFAULT '' COMMENT '最终结果',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
INSERT INTO `user2`(`user_name`,`over`) VALUES ('孙悟空','斗战胜佛');
INSERT INTO `user2`(`user_name`,`over`) VALUES ('牛魔王','被降服');
INSERT INTO `user2`(`user_name`,`over`) VALUES ('蛟魔王','被降服');
INSERT INTO `user2`(`user_name`,`over`) VALUES ('鹏魔王','被降服');
INSERT INTO `user2`(`user_name`,`over`) VALUES ('狮驼王','被降服');
SELECT a.`user_name`,a.`over`,b.`user_name`,b.`over` FROM `user1` a INNER JOIN `user2` b ON a.`user_name` = b.`user_name`;
等价
SELECT a.`user_name`,a.`over`,b.`user_name`,b.`over` FROM `user1` a ,`user2` b WHERE a.`user_name` = b.`user_name`;
SELECT a.`user_name`,a.`over`,b.`user_name`,b.`over` FROM `user1` a LEFT JOIN `user2` b ON a.`user_name` = b.`user_name`;
SELECT a.`id`,a.`user_name`,a.`over` FROM `user1` a LIMIT 0,4;
SELECT b.`id`,b.`user_name`,b.`over` FROM `user2` b LIMIT 0,5;
SELECT a.`user_name`,a.`over`,b.`user_name`,b.`over` FROM `user1` a LEFT JOIN `user2` b ON a.`user_name` = b.`user_name` WHERE b.`user_name` IS NOT NULL;
SELECT b.`user_name` 'b_username',b.`over` 'b_over',a.`user_name` 'a_username',a.`over` 'a_over' FROM `user2` b RIGHT JOIN `user1` a ON b.`user_name` = a.`user_name`;
SELECT b.`user_name` 'b_username',b.`over` 'b_over',a.`user_name` 'a_username',a.`over` 'a_over' FROM `user2` b RIGHT JOIN `user1` a ON b.`user_name` = a.`user_name` WHERE b.`user_name` IS NOT NULL;
SELECT a.`user_name` 'a_username',a.`over` 'a_over',b.`user_name` 'b_username',b.`over` 'b_over' FROM `user1` a LEFT JOIN `user2` b ON a.`user_name` = b.`user_name`
UNION ALL
SELECT b.`user_name` 'b_username',b.`over` 'b_over',a.`user_name` 'a_username',a.`over` 'a_over' FROM `user2` b RIGHT JOIN `user1` a ON b.`user_name` = a.`user_name`;
交叉连接(cross join),又称笛卡尔集连接(cartesian join)或叉乘(product);
如果A和B是两个集合,他们的交叉连接记为:A x B;
笛卡尔集中,没有ON从句(没有连接关键词);
SELECT a.`id`,a.`user_name`,a.`over`,b.`id`,b.`user_name`,b.`over` FROM `user1` a CROSS JOIN `user2` b;
情景:
把同时存在 于user1表和user2表中的记录的人在user1表中的over字段更新为齐天大圣。
UPDATE `user1` SET `over` = '齐天大圣' WHERE `user1`.`user_name` IN (SELECT b.`user_name` FROM `user1` a INNER JOIN `user2` b ON a.`user_name` = b.`user_name`);
通过查询MySQL手册可知,MySQL不支持这种更新操作
既然报错提示待更新的表不允许出现在from从句中,那么就不让待更新的表出现在from从句中
UPDATE `user1` a JOIN (
SELECT b.`user_name` FROM `user1` a INNER JOIN `user2` b ON a.`user_name`=b.`user_name`
) b ON a.`user_name` = b.`user_name` SET a.`over` ='齐天大圣';
先将user1别名为a的表,和括号里边的内连接Inner join查询的结果表别名为b的表,进行JOIN,条件时a和b表的共有字段,然后进行update
SELECT a.`id`,a.`user_name`,a.`over` FROM `user1` a LIMIT 0,4;
技巧:
更新【自身在from从句中】的表的方式
子查询
SELECT a.`user_name`,a.`over`,(SELECT `over` FROM `user2` b WHERE a.`user_name` = b.`user_name`) AS over2 FROM `user1` a;
如果查询的两张表的记录非常多,子查询相当于对a表(user1表)中的每一条记录都要进行子查询,查询的效率较低。
使用JOIN优化子查询
SELECT a.`user_name`,a.`over`,b.`over` AS over2
FROM `user1` a
LEFT JOIN `user2` b ON
a.`user_name`=b.`user_name`;
先查出user1和user2进行左外连接LEFT JOIN查询的结结果,然后再进行最终的查询
# CREATE table CREATE TABLE IF NOT EXISTS `user_kills`( `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID', `user_id` int(11) unsigned NOT NULL DEFAULT 0 COMMENT '用户id', `timestr` timestamp NOT NULL DEFAULT now() ON UPDATE CURRENT_TIMESTAMP COMMENT '打怪时间', `kills` int(11) unsigned NOT NULL DEFAULT 0 COMMENT '打怪数量', FOREIGN KEY (`user_id`) REFERENCES `user1` (`id`),# 给从表做外键约束,映射主表的id PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; INSERT INTO `user_kills`(`user_id`,`timestr`,`kills`) VALUES (2,'2013-01-10 00:00:00',10); INSERT INTO `user_kills`(`user_id`,`timestr`,`kills`) VALUES (2,'2013-02-01 00:00:00',2); INSERT INTO `user_kills`(`user_id`,`timestr`,`kills`) VALUES (2,'2013-02-05 00:00:00',12); INSERT INTO `user_kills`(`user_id`,`timestr`,`kills`) VALUES (4,'2013-01-10 00:00:00',3); INSERT INTO `user_kills`(`user_id`,`timestr`,`kills`) VALUES (2,'2013-02-11 00:00:00',5); INSERT INTO `user_kills`(`user_id`,`timestr`,`kills`) VALUES (2,'2013-02-06 00:00:00',1); INSERT INTO `user_kills`(`user_id`,`timestr`,`kills`) VALUES (3,'2013-01-11 00:00:00',20); INSERT INTO `user_kills`(`user_id`,`timestr`,`kills`) VALUES (2,'2013-02-12 00:00:00',10); INSERT INTO `user_kills`(`user_id`,`timestr`,`kills`) VALUES (2,'2013-02-07 00:00:00',17); # SELECT all SELECT c.`id`,c.`user_id`,c.`timestr`,c.`kills` FROM `user_kills` c LIMIT 0,9;
情景:
如何查询出四人组中打怪最多的日期?
SELECT a.`user_name`,b.`timestr`,b.`kills`
FROM `user1` a JOIN `user_kills` b
ON a.`id` = b.`user_id`
WHERE b.`kills` = (SELECT MAX(c.`kills`) FROM `user_kills` c WHERE c.`user_id` = b.`user_id`);
如何查询出四人组中打怪最多的日期?(避免子查询)
优化子查询使用用HAVING从句
SELECT a.`user_name`,b.`timestr`,b.`kills`
FROM `user1` a
JOIN `user_kills` b ON a.`id` = b.`user_id`
JOIN `user_kills` c ON c.`user_id` = b.`user_id`
GROUP BY a.`user_name`,b.`timestr`,b.`kills`
HAVING b.`kills` = MAX(c.`kills`);
情景
SELECT a.`user_name`,b.`timestr`,b.`kills`
FROM `user1` a JOIN `user_kills` b
ON a.`id` = b.`user_id`
WHERE a.`user_name` = '猪八戒'
ORDER BY b.`kills` DESC
LIMIT 0,2;
SELECT a.`user_name`,b.`timestr`,b.`kills`
FROM `user1` a JOIN `user_kills` b
ON a.`id` = b.`user_id`
WHERE a.`user_name` = '孙悟空'
ORDER BY b.`kills` DESC
LIMIT 0,2;
SELECT a.`user_name`,b.`timestr`,b.`kills`
FROM `user1` a JOIN `user_kills` b
ON a.`id` = b.`user_id`
WHERE a.`user_name` = '沙僧'
ORDER BY b.`kills` DESC
LIMIT 0,2;
问题
1.如果分类或是用户很多的情况下,则需要多次执行同一查询
2.增加应用程序同数据库交互的次数
3.增加了数据库执行查询的次数,不符合批处理的原则
4.增加了网络流量,网路的稳定性得不到稳定
优化
**使用ROW_NUMBER()**函数来实现分区排序–MySQL支持的不好,宝宝也没办法
#使用ROW_NUMBER()函数来实现分区排序--mysql没有很好的支持ROW_NUMBER()
WITH tmp AS(
SELECT a.`user_name`,b.`timestr`,b.`kills`,
ROW_NUMBER() OVER(PARTITION BY a.`user_name`
ORDER BY b.`kills`)cnt
FROM `user1` a JOIN `user_kills` b
ON a.`id` = b.`user_id`)
SELECT *
FROM tmp WHERE cnt <=2;
优化方式2:
通用优化方式
SELECT d.`user_name`,c.`timestr`,`kills`
FROM (
SELECT `user_id`,`timestr`,`kills`,
(SELECT COUNT(*)FROM `user_kills` b WHERE
b.`user_id` = a.`user_id` AND a.`kills` <=b.`kills`)AS cnt
FROM `user_kills` as a
GROUP BY `user_id`,`timestr`,`kills`)c
JOIN `user1` d ON c.`user_id` = d.`id`
WHERE cnt <=2;
分类聚合的情景下尽量使用通用查询方式进行优化,尽可能多的一次查询到,且SQL通用性要普遍支持
情景
SELECT a.`user_name`,SUM(b.`kills`) FROM `user1` a JOIN `user_kills` b ON a.`id` = b.`user_id` GROUP BY `user_name`;
行转列【多行转单列】:使用CROSS JOIN的方式实现
# 行转列实现:使用cross join
SELECT * FROM(
SELECT SUM(`kills`) as '孙悟空' FROM `user1` a JOIN `user_kills` b ON a.`id` = b.`user_id` WHERE a.`user_name` = '孙悟空'
) a CROSS JOIN(
SELECT SUM(`kills`) as '猪八戒' FROM `user1` a JOIN `user_kills` b ON a.`id` = b.`user_id` WHERE a.`user_name` = '猪八戒'
) b CROSS JOIN(
SELECT SUM(`kills`) as '沙僧' FROM `user1` a JOIN `user_kills` b ON a.`id` = b.`user_id` WHERE a.`user_name` = '沙僧'
) c;
虽然实现了效果,但是非常的不方便,而且SQL显得过于冗余和繁琐
行转列:使用CASE 语句的方式实现
# 使用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`;
场景1.
# 实现单列转多行
# 1.对user1表中添加一列mobile
ALTER TABLE `user1` ADD COLUMN `mobile` VARCHAR(100) COMMENT '手机号';
UPDATE `user1` SET `mobile`='121123456,141123456,161123456' WHERE `id` = 1;
UPDATE `user1` SET `mobile`='121654321,141654321' WHERE `id` = 2;
UPDATE `user1` SET `mobile`='121666666,141666666,161666666,181666666' WHERE `id` = 3;
UPDATE `user1` SET `mobile`='121987654,141987654' WHERE `id` = 4;
SELECT `id`,`user_name`,`mobile` FROM `user1` LIMIT 0,4;
# 1.创建序列表
CREATE TABLE IF NOT EXISTS `tb_sequence`(
`id` int auto_increment not null,
primary key(`id`)
);
# 初始化序列表的数据
INSERT INTO `tb_sequence` values(),(),(),(),(),(),(),(),(),(),(),(),();
SELECT * FROM `tb_sequence`;
# 利用序列表处理列转行的数据 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;
SQL释义
# 此子查询实现的是保证user1表中的mobile列是以逗号,结尾,然后通过计算mobile列有多少个逗号,用size标识
SELECT `user_name`,CONCAT(`mobile`,',')AS mobile,lENGTH(mobile)-lENGTH(REPLACE(mobile,',',''))+1 size FROM `user1` b;
执行查询
2.使用序列表进行交叉连接,生成笛卡尔集,关联条件a.id<=b.size,实现有多少个逗号,也就是有多少个手机号,最终的结果就会显示成多少行
# 使用序列表进行交叉连接,生成笛卡尔集,关联条件a.id<=b.size,实现有多少个逗号,也就是有多少个手机号,最终的结果就会显示成多少行
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;
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;
REPLACE (
SUBSTRING(
SUBSTRING_INDEX(`mobile`, ',', a.`id`),
CHAR_LENGTH(
SUBSTRING_INDEX(`mobile`, ',', a.`id` - 1)
) + 1
),
',',
''
) AS mobile
# CREATE database CREATE DATABASE IF NOT EXISTS `join_test` CHARACTER SET = utf8mb4; # SWITCH database USE `join_test`; # CREATE Table CREATE TABLE IF NOT EXISTS `user1` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID', `user_name` varchar(30) NOT NULL DEFAULT '' COMMENT '用户名', `over` varchar(30) NOT NULL DEFAULT '' COMMENT '最终结果', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; INSERT INTO `user1`(`user_name`,`over`) VALUES ('唐僧','旃檀功德佛'); INSERT INTO `user1`(`user_name`,`over`) VALUES ('猪八戒','净坛使者'); INSERT INTO `user1`(`user_name`,`over`) VALUES ('孙悟空','斗战圣佛'); INSERT INTO `user1`(`user_name`,`over`) VALUES ('沙僧','金身罗汉'); # SELECT all SELECT a.`id`,a.`user_name`,a.`over` FROM `user1` a LIMIT 0,4; # CREATE Table CREATE TABLE IF NOT EXISTS `user2` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID', `user_name` varchar(30) NOT NULL DEFAULT '' COMMENT '用户名', `over` varchar(30) NOT NULL DEFAULT '' COMMENT '最终结果', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; INSERT INTO `user2`(`user_name`,`over`) VALUES ('孙悟空','斗战胜佛'); INSERT INTO `user2`(`user_name`,`over`) VALUES ('牛魔王','被降服'); INSERT INTO `user2`(`user_name`,`over`) VALUES ('蛟魔王','被降服'); INSERT INTO `user2`(`user_name`,`over`) VALUES ('鹏魔王','被降服'); INSERT INTO `user2`(`user_name`,`over`) VALUES ('狮驼王','被降服'); # SELECT all SELECT b.`id`,b.`user_name`,b.`over` FROM `user2` b LIMIT 0,5; # Inner Join SELECT a.`user_name`,a.`over`,b.`user_name`,b.`over` FROM `user1` a INNER JOIN `user2` b ON a.`user_name` = b.`user_name`; SELECT a.`user_name`,a.`over`,b.`user_name`,b.`over` FROM `user1` a ,`user2` b WHERE a.`user_name` = b.`user_name`; # LEFT Outer Join SELECT a.`user_name`,a.`over`,b.`user_name`,b.`over` FROM `user1` a LEFT JOIN `user2` b ON a.`user_name` = b.`user_name`; SELECT a.`user_name`,a.`over`,b.`user_name`,b.`over` FROM `user1` a LEFT JOIN `user2` b ON a.`user_name` = b.`user_name` WHERE b.`user_name` IS NOT NULL; # RIGTH Outer Join SELECT b.`user_name` 'b_username',b.`over` 'b_over',a.`user_name` 'a_username',a.`over` 'a_over' FROM `user2` b RIGHT JOIN `user1` a ON b.`user_name` = a.`user_name`; SELECT b.`user_name` 'b_username',b.`over` 'b_over',a.`user_name` 'a_username',a.`over` 'a_over' FROM `user2` b RIGHT JOIN `user1` a ON b.`user_name` = a.`user_name` WHERE b.`user_name` IS NOT NULL; # Full Join SELECT a.`user_name` 'a_username',a.`over` 'a_over',b.`user_name` 'b_username',b.`over` 'b_over' FROM `user1` a LEFT JOIN `user2` b ON a.`user_name` = b.`user_name` UNION ALL SELECT b.`user_name` 'b_username',b.`over` 'b_over',a.`user_name` 'a_username',a.`over` 'a_over' FROM `user2` b RIGHT JOIN `user1` a ON b.`user_name` = a.`user_name`; # Cross JOIN SELECT a.`id`,a.`user_name`,a.`over`,b.`id`,b.`user_name`,b.`over` FROM `user1` a CROSS JOIN `user2` b; # 情景:把**同时存在** 于user1表和user2表中的记录的人在user1表中的over字段更新为齐天大圣。 # 会报错 UPDATE `user1` SET `over` = '齐天大圣' WHERE `user1`.`user_name` IN (SELECT b.`user_name` FROM `user1` a INNER JOIN `user2` b ON a.`user_name` = b.`user_name`); UPDATE `user1` a JOIN ( SELECT b.`user_name` FROM `user1` a INNER JOIN `user2` b ON a.`user_name`=b.`user_name` ) b ON a.`user_name` = b.`user_name` SET a.`over` ='齐天大圣'; #验证结果 SELECT a.`id`,a.`user_name`,a.`over` FROM `user1` a LIMIT 0,4; # 子查询 SELECT a.`user_name`,a.`over`,(SELECT `over` FROM `user2` b WHERE a.`user_name` = b.`user_name`) AS over2 FROM `user1` a; # 使用JOIN优化子查询 SELECT a.`user_name`,a.`over`,b.`over` AS over2 FROM `user1` a LEFT JOIN `user2` b ON a.`user_name`=b.`user_name`; # 使用JOIN优化聚合子查询 # 引入新表 ,按照日期记录user1表中的每个人打怪的数量 # CREATE table CREATE TABLE IF NOT EXISTS `user_kills`( `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID', `user_id` int(11) unsigned NOT NULL DEFAULT 0 COMMENT '用户id', `timestr` timestamp NOT NULL DEFAULT now() ON UPDATE CURRENT_TIMESTAMP COMMENT '打怪时间', `kills` int(11) unsigned NOT NULL DEFAULT 0 COMMENT '打怪数量', FOREIGN KEY (`user_id`) REFERENCES `user1` (`id`),# 给从表做外键约束,映射主表的id PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; INSERT INTO `user_kills`(`user_id`,`timestr`,`kills`) VALUES (2,'2013-01-10 00:00:00',10); INSERT INTO `user_kills`(`user_id`,`timestr`,`kills`) VALUES (2,'2013-02-01 00:00:00',2); INSERT INTO `user_kills`(`user_id`,`timestr`,`kills`) VALUES (2,'2013-02-05 00:00:00',12); INSERT INTO `user_kills`(`user_id`,`timestr`,`kills`) VALUES (4,'2013-01-10 00:00:00',3); INSERT INTO `user_kills`(`user_id`,`timestr`,`kills`) VALUES (2,'2013-02-11 00:00:00',5); INSERT INTO `user_kills`(`user_id`,`timestr`,`kills`) VALUES (2,'2013-02-06 00:00:00',1); INSERT INTO `user_kills`(`user_id`,`timestr`,`kills`) VALUES (3,'2013-01-11 00:00:00',20); INSERT INTO `user_kills`(`user_id`,`timestr`,`kills`) VALUES (2,'2013-02-12 00:00:00',10); INSERT INTO `user_kills`(`user_id`,`timestr`,`kills`) VALUES (2,'2013-02-07 00:00:00',17); # SELECT all SELECT c.`id`,c.`user_id`,c.`timestr`,c.`kills` FROM `user_kills` c LIMIT 0,9; # 如何查询出四人组中打怪最多的日期? SELECT a.`user_name`,b.`timestr`,b.`kills` FROM `user1` a JOIN `user_kills` b ON a.`id` = b.`user_id` WHERE b.`kills` = (SELECT MAX(c.`kills`) FROM `user_kills` c WHERE c.`user_id` = b.`user_id`); # 优化子查询:使用Having子句 SELECT a.`user_name`,b.`timestr`,b.`kills` FROM `user1` a JOIN `user_kills` b ON a.`id` = b.`user_id` JOIN `user_kills` c ON c.`user_id` = b.`user_id` GROUP BY a.`user_name`,b.`timestr`,b.`kills` HAVING b.`kills` = MAX(c.`kills`); # 如何实现分组选择 # 选择出每一个人杀怪最多的前两天头两个日期是? SELECT a.`user_name`,b.`timestr`,b.`kills` FROM `user1` a JOIN `user_kills` b ON a.`id` = b.`user_id` WHERE a.`user_name` = '猪八戒' ORDER BY b.`kills` DESC LIMIT 0,2; SELECT a.`user_name`,b.`timestr`,b.`kills` FROM `user1` a JOIN `user_kills` b ON a.`id` = b.`user_id` WHERE a.`user_name` = '孙悟空' ORDER BY b.`kills` DESC LIMIT 0,2; SELECT a.`user_name`,b.`timestr`,b.`kills` FROM `user1` a JOIN `user_kills` b ON a.`id` = b.`user_id` WHERE a.`user_name` = '沙僧' ORDER BY b.`kills` DESC LIMIT 0,2; #优化 #使用ROW_NUMBER()函数来实现分区排序--mysql没有很好的支持ROW_NUMBER() WITH tmp AS( SELECT a.`user_name`,b.`timestr`,b.`kills`, ROW_NUMBER() OVER(PARTITION BY a.`user_name` ORDER BY b.`kills`)cnt FROM `user1` a JOIN `user_kills` b ON a.`id` = b.`user_id`) SELECT * FROM tmp WHERE cnt <=2; # 通用优化方式 SELECT d.`user_name`,c.`timestr`,`kills` FROM ( SELECT `user_id`,`timestr`,`kills`, (SELECT COUNT(*)FROM `user_kills` b WHERE b.`user_id` = a.`user_id` AND a.`kills` <=b.`kills`)AS cnt FROM `user_kills` as a GROUP BY `user_id`,`timestr`,`kills`)c JOIN `user1` d ON c.`user_id` = d.`id` WHERE cnt <=2; #行转列 SELECT a.`user_name`,SUM(b.`kills`) FROM `user1` a JOIN `user_kills` b ON a.`id` = b.`user_id` GROUP BY `user_name`; # 行转列实现:使用cross join SELECT * FROM( SELECT SUM(`kills`) as '孙悟空' FROM `user1` a JOIN `user_kills` b ON a.`id` = b.`user_id` WHERE a.`user_name` = '孙悟空' ) a CROSS JOIN( SELECT SUM(`kills`) as '猪八戒' FROM `user1` a JOIN `user_kills` b ON a.`id` = b.`user_id` WHERE a.`user_name` = '猪八戒' ) b CROSS JOIN( SELECT SUM(`kills`) as '沙僧' FROM `user1` a JOIN `user_kills` b ON a.`id` = b.`user_id` WHERE a.`user_name` = '沙僧' ) c; # 使用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`; # 实现单列转多行 # 1.对user1表中添加一列mobile ALTER TABLE `user1` ADD COLUMN `mobile` VARCHAR(100) COMMENT '手机号'; UPDATE `user1` SET `mobile`='121123456,141123456,161123456' WHERE `id` = 1; UPDATE `user1` SET `mobile`='121654321,141654321' WHERE `id` = 2; UPDATE `user1` SET `mobile`='121666666,141666666,161666666,181666666' WHERE `id` = 3; UPDATE `user1` SET `mobile`='121987654,141987654' WHERE `id` = 4; SELECT `id`,`user_name`,`mobile` FROM `user1` LIMIT 0,4; # 利用序列表实现列转行的数据 # 1.创建序列表 CREATE TABLE IF NOT EXISTS `tb_sequence`( `id` int auto_increment not null, primary key(`id`) ); # 初始化序列表的数据 INSERT INTO `tb_sequence` values(),(),(),(),(),(),(),(),(),(),(),(),(); SELECT * FROM `tb_sequence`; # 利用序列表处理列转行的数据 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表中的mobile列是以逗号,结尾,然后通过计算mobile列有多少个逗号,用size标识 SELECT `user_name`,CONCAT(`mobile`,',')AS mobile,lENGTH(mobile)-lENGTH(REPLACE(mobile,',',''))+1 size FROM `user1` b; # 使用序列表进行交叉连接,生成笛卡尔集,关联条件a.id<=b.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; # 实现了字符串的截取 #REPLACE ( # SUBSTRING( # SUBSTRING_INDEX(`mobile`, ',', a.`id`), # CHAR_LENGTH( # SUBSTRING_INDEX(`mobile`, ',', a.`id` - 1) # ) + 1 # ), # ',', # '' # ) AS mobile
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。