SQL语句分类
- DDL:数据定义语言
- TPL:事务处理语言
- DCL:数据控制语言-grant
- DML:数据操作语言-crud
join从句
操作表数据:
|
|
内连接 inner
基于连接谓词将两张表的列组合在一起,产生新的结果表,交集。
|
|
全外连接 full outer
并集,MySQL中不支持直接的full join。用union all来实现。
|
|
左外连接 left outer
以左表为准。
|
|
右外连接 right outer
以右表为准。
|
|
交叉连接 cross
实际中需要避免。
|
|
如何更新过滤条件中包括自身的表
直接更新是不可以的:
|
|
先join,更新join之后的表方法:
|
|
join优化子查询
低效的子查询:
|
|
left join优化后:
|
|
join优化聚合子查询
|
|
优化后:
|
|
如何实现分组选择
每一组数据中选出前几名的情景,可以分组逐个进行查询,这种情况下,分组比较多,需要执行同一次查询很多次,而且增加了应用程序与数据库的交互次数,增加了数据库执行查询的次数,不符合批处理的原则,也会增加网络流量,因此实际中不使用这种方式。
优化方法:
|
|
如何进行行列转换
行转列
报表统计,汇总显示会经常使用行转列的操作。
打印流水单:
123456789101112131415161718mysql> select a.user_name, b.kills from user1 a join user_kills b on a.id=b.user_id;+-----------+-------+| user_name | kills |+-----------+-------+| 猪八戒 | 10 || 猪八戒 | 2 || 猪八戒 | 12 || 沙僧 | 3 || 猪八戒 | 6 || 孙悟空 | 50 || 孙悟空 | 12 || 孙悟空 | 5 || 猪八戒 | 15 || 沙僧 | 123 || 沙僧 | 14 || 沙僧 | 10 |+-----------+-------+12 rows in set (0.00 sec)汇总通过sum和group by
123456789mysql> 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;+-----------+--------------+| user_name | sum(b.kills) |+-----------+--------------+| 孙悟空 | 67 || 沙僧 | 150 || 猪八戒 | 45 |+-----------+--------------+3 rows in set (0.00 sec)通过cross join来实现行转列, 存在性能问题。
1234567mysql> select * from (select sum(b.kills) as '猪八戒' from user1 a join user_kills b on a.id=b.user_id and user_name='猪八戒') a cross join (select sum(b.kills) as '孙悟空' from user1 a join user_kills b on a.id=b.user_id and user_name='孙悟空') b cross join (select sum(b.kills) as '沙僧' from user1 a join user_kills b on a.id=b.user_id and user_name='沙僧') c;+--------+--------+------+| 猪八戒 | 孙悟空 | 沙僧 |+--------+--------+------+| 45 | 67 | 150 |+--------+--------+------+1 row in set (0.00 sec)使用case语句来实现行转列
1234567mysql> 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;+--------+--------+------+| 孙悟空 | 猪八戒 | 沙僧 |+--------+--------+------+| 67 | 45 | 150 |+--------+--------+------+1 row in set (0.00 sec)
列转行
属性拆分,比如权限分隔。
利用序列表实现列转行
1234567891011121314151617181920212223242526272829303132333435mysql> SELECTuser_name,REPLACE(substring(SUBSTRING_INDEX(mobile,',',a.id),CHAR_LENGTH(SUBSTRING_INDEX(mobile,',',a.id-1))+1),',','') AS mobileFROM tb_sequence AS aCROSS JOIN(SELECT user_name, CONCAT(mobile,',') AS mobile, (LENGTH(mobile)-LENGTH(REPLACE(mobile,',',''))+1) AS size FROM user1) AS bON a.id <= b.size;+-----------+----------+| user_name | mobile |+-----------+----------+| 唐僧 | 12123123 || 唐僧 | 123 || 唐僧 | 123432 || 唐僧 | 2131 || 唐僧 | 4213 || 唐僧 | 3421 || 猪八戒 | 12123123 || 猪八戒 | 123 || 猪八戒 | 123432 || 猪八戒 | 2131 || 猪八戒 | 4213 || 猪八戒 | 3421 || 孙悟空 | 12123123 || 孙悟空 | 123 || 孙悟空 | 123432 || 孙悟空 | 2131 || 孙悟空 | 4213 || 孙悟空 | 3421 || 沙僧 | 12123123 || 沙僧 | 123 || 沙僧 | 123432 || 沙僧 | 2131 || 沙僧 | 4213 || 沙僧 | 3421 |+-----------+----------+24 rows in set (0.00 sec)使用union join实现行列转换
原数据
12345678910
mysql> select user_name, arms, clothing, shoe from user1 a join user1_equipment b on a.id = b.user_id;+-----------+----------+------------+------------+| user_name | arms | clothing | shoe |+-----------+----------+------------+------------+| 唐僧 | 九环锡杖 | 袈裟 | 僧鞋 || 猪八戒 | 九齿钉耙 | 僧衣 | 僧鞋 || 孙悟空 | 金箍棒 | 梭子黄金甲 | 藕丝云步履 || 沙僧 | 降妖宝杖 | 僧衣 | 僧鞋 |+-----------+----------+------------+------------+4 rows in set (0.00 sec)
结果集:
1234567891011121314151617181920212223
mysql> select user_name, 'arms' as equipment, arms from user1 a join user1_equipment b on a.id = b.user_id -> union all -> select user_name, 'clothing' as equipment, clothing from user1 a join user1_equipment b on a.id = b.user_id -> union all -> select user_name, 'shoe' as equipment, shoe from user1 a join user1_equipment b on a.id = b.user_id -> order by user_name;+-----------+-----------+------------+| user_name | equipment | arms |+-----------+-----------+------------+| 唐僧 | clothing | 袈裟 || 唐僧 | arms | 九环锡杖 || 唐僧 | shoe | 僧鞋 || 孙悟空 | arms | 金箍棒 || 孙悟空 | shoe | 藕丝云步履 || 孙悟空 | clothing | 梭子黄金甲 || 沙僧 | shoe | 僧鞋 || 沙僧 | clothing | 僧衣 || 沙僧 | arms | 降妖宝杖 || 猪八戒 | shoe | 僧鞋 || 猪八戒 | clothing | 僧衣 || 猪八戒 | arms | 九齿钉耙 |+-----------+-----------+------------+12 rows in set (0.00 sec)
使用序列号进行列转行
12345678910111213141516171819202122232425262728mysql> select user_name,-> case when c.id=1 then 'arms'-> when c.id=2 then 'clothing'-> when c.id=3 then 'shoe'-> end as 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) as 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;+-----------+-----------+------------+| user_name | equipment | eq_name |+-----------+-----------+------------+| 唐僧 | shoe | 僧鞋 || 唐僧 | arms | 九环锡杖 || 唐僧 | clothing | 袈裟 || 孙悟空 | shoe | 藕丝云步履 || 孙悟空 | arms | 金箍棒 || 孙悟空 | clothing | 梭子黄金甲 || 沙僧 | clothing | 僧衣 || 沙僧 | shoe | 僧鞋 || 沙僧 | arms | 降妖宝杖 || 猪八戒 | clothing | 僧衣 || 猪八戒 | shoe | 僧鞋 || 猪八戒 | arms | 九齿钉耙 |+-----------+-----------+------------+12 rows in set (0.00 sec)
如何生成唯一序列号
需要的场景:
- 数据库主键,聚集索引方式
- 业务序列号如发票号,车票号,订单号
生成序列号的方法:
- MySQL:auto_increment
- Oracle:sequence
- SQLServer:identity/sequence
如何选择生成序列号的方式:
优先选择系统提供的序列号生成方式,需要注意数据空洞的问题:
123456789101112131415161718192021222324252627282930313233343536373839mysql> create table t (id int auto_increment not null, primary key(id));Query OK, 0 rows affected (0.04 sec)mysql> insert into t value(),();Query OK, 2 rows affected (0.01 sec)Records: 2 Duplicates: 0 Warnings: 0mysql> select * from t;+----+| id |+----+| 1 || 2 |+----+2 rows in set (0.00 sec)mysql> begin;Query OK, 0 rows affected (0.00 sec)mysql> insert into t values();Query OK, 1 row affected (0.00 sec)mysql> select * from t;+----+| id |+----+| 1 || 2 || 3 |+----+3 rows in set (0.00 sec)mysql> rollback;Query OK, 0 rows affected (0.01 sec)mysql> insert into t values();Query OK, 1 row affected (0.00 sec)mysql> select * from t;+----+| id |+----+| 1 || 2 || 4 |+----+3 rows in set (0.00 sec)使用SQL方式生成序列号,特殊情况下使用。如生成订单序列号,要求序列号的格式为YYYYMMDDNNNNNNN
12345678910111213141516171819202122232425262728293031323334CREATE PROCEDURE usp_seqnum()BEGIN#定义变量并获取相关值DECLARE v_cnt INT;DECLARE v_timestr INT;SET v_timestr = DATE_FORMAT(NOW(),'%Y%m%d');SELECT ROUND(RAND()*100,0)+1 INTO v_cnt;#新建表DROP TABLE IF EXISTS im_orderseq;CREATE TABLE im_orderseq(timestr NVARCHAR(8) NOT NULL ,ordersn INT(3));START TRANSACTION;#更新表的最值UPDATE im_orderseq SET ordersn = ordersn + v_cnt WHERE timestr = v_timestr;IF ROW_COUNT() = 0 THEN#插入数据INSERT INTO im_orderseq(timestr,ordersn) VALUES(v_timestr,v_cnt);END IF;SELECT CONCAT(v_timestr,LPAD(ordersn,7,0))AS ordersnFROM im_orderseq WHERE timestr = v_timestr;COMMIT;END;mysql> call usp_seqnum();+-----------------+| ordersn |+-----------------+| 201804270000009 |+-----------------+1 row in set (0.04 sec)Query OK, 0 rows affected (0.05 sec)
如何删除重复元素
产生原因:
* 人为原因,重复录入数据,重复提交数据
* 系统原因,由于系统升级或设计的原因使原来可以重复的数据变为不能重复了。
查询数据是否重复:group by + having
12345678910111213141516171819202122232425
mysql> select * from user1_test;+----+-----------+----------+------------------------------------+| id | user_name | over | mobile |+----+-----------+----------+------------------------------------+| 1 | 唐僧 | 唐僧成佛 | 12123123,123,123432,2131,4213,3421 || 2 | 猪八戒 | 净坛使者 | 12123123,123,123432,2131,4213,3421 || 3 | 孙悟空 | 齐天大圣 | 12123123,123,123432,2131,4213,3421 || 4 | 沙僧 | 金身罗汉 | 12123123,123,123432,2131,4213,3421 || 8 | 唐僧 | 唐僧成佛 | 12123123,123,123432,2131,4213,3421 || 9 | 猪八戒 | 净坛使者 | 12123123,123,123432,2131,4213,3421 || 10 | 孙悟空 | 齐天大圣 | 12123123,123,123432,2131,4213,3421 || 11 | 沙僧 | 金身罗汉 | 12123123,123,123432,2131,4213,3421 |+----+-----------+----------+------------------------------------+8 rows in set (0.00 sec)mysql> select user_name, count(*) from user1_test group by user_name having count(*)>1;+-----------+----------+| user_name | count(*) |+-----------+----------+| 唐僧 | 2 || 孙悟空 | 2 || 沙僧 | 2 || 猪八戒 | 2 |+-----------+----------+4 rows in set (0.00 sec)
删除重复数据,相同数据保留ID最大的:
12345678910111213141516171819202122232425262728293031323334
mysql> select * from user1_test;+----+-----------+----------+------------------------------------+| id | user_name | over | mobile |+----+-----------+----------+------------------------------------+| 1 | 唐僧 | 唐僧成佛 | 12123123,123,123432,2131,4213,3421 || 2 | 猪八戒 | 净坛使者 | 12123123,123,123432,2131,4213,3421 || 3 | 孙悟空 | 齐天大圣 | 12123123,123,123432,2131,4213,3421 || 4 | 沙僧 | 金身罗汉 | 12123123,123,123432,2131,4213,3421 || 8 | 唐僧 | 唐僧成佛 | 12123123,123,123432,2131,4213,3421 || 9 | 猪八戒 | 净坛使者 | 12123123,123,123432,2131,4213,3421 || 10 | 孙悟空 | 齐天大圣 | 12123123,123,123432,2131,4213,3421 || 11 | 沙僧 | 金身罗汉 | 12123123,123,123432,2131,4213,3421 |+----+-----------+----------+------------------------------------+8 rows in set (0.00 sec)mysql> 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;Query OK, 4 rows affected (0.01 sec)mysql> select * from user1_test;+----+-----------+----------+------------------------------------+| id | user_name | over | mobile |+----+-----------+----------+------------------------------------+| 8 | 唐僧 | 唐僧成佛 | 12123123,123,123432,2131,4213,3421 || 9 | 猪八戒 | 净坛使者 | 12123123,123,123432,2131,4213,3421 || 10 | 孙悟空 | 齐天大圣 | 12123123,123,123432,2131,4213,3421 || 11 | 沙僧 | 金身罗汉 | 12123123,123,123432,2131,4213,3421 |+----+-----------+----------+------------------------------------+4 rows in set (0.00 sec)
如何在子查询中匹配两个值
- 子查询:当一个查询是另一个查询的条件时,成为子查询。