查询mysql中执行效率低的sql语句的方法
一些小技巧
1. 如何查出效率低的语句?
在mysql下,在启动参数中设置 --log-slow-queries=[文件名],就可以在指定的日志文件中记录执行时间超过long_query_time(缺省为10秒)的sql语句。你也可以在启动配置文件中修改long query的时间,如:
# set long query time to 8 seconds
long_query_time=8
2. 如何查询某表的索引?
可使用show index语句,如:
show index from [表名]
3. 如何查询某条语句的索引使用情况?
可用explain语句来看一下某条select语句的索引使用情况。如果是update或delete语句,需要先转换为select语句。
4. 如何把导出innodb引擎的内容到错误日志文件中?
我们可以使用show innodb status命令来查看innodb引擎的很多有用的信息,如当前进程、事务、外键错误、死锁问题和其它一些统计数据。如何让该信息能记录在日志文件中呢?只要使用如下语句创建innodb_monitor表,mysql就会每15秒钟把该系统写入到错误日志文件中:
create table innodb_monitor (a int) engine=innodb;
如果你不再需要导出到错误日志文件,只要删除该表即可:
drop table innodb_monitor;
5. 如何定期删除庞大的日志文件?
只要在启动配置文件中设置日志过期时间即可:
expire_logs_days=10
经验教训
1. 重点关注索引
下面以表tsk_task表为例说明sql语句优化过程。tsk_task表用于保存系统监测任务,相关字段及索引如下:
id:主键;
mon_time:监测时间;建了索引;
status_id:任务状态;与sys_hier_info.id建立了外键关系。
注mysql自动会为外键建立索引,在本次优化过程中,发现这些自动建立的外键索引会对sql语句的效率产生不必要的干扰,需要特别注意!
首先,我们在日志文件中查到下面语句的执行比较慢,超过10秒了:
# query_time: 18 lock_time: 0 rows_sent: 295 rows_examined: 88143
select * from tsk_task where status_id = 1064 and mon_time >= '2007-11-22' and mon_time < '2007-11-23';
哦,原来在88143条记录中要查出符合条件的295条记录,那当然慢了。赶紧用explain语句看一下索引使用情况吧:
+----+-------------+----------+------+----------------------------------------------------------+------------------------------------+---------+-------+--------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | extra |
+----+-------------+----------+------+----------------------------------------------------------+------------------------------------+---------+-------+--------+-------------+
| 1 | simple | tsk_task | ref | fk_task_status_id_to_sys_hier_info,tsk_task_key_mon_time | fk_task_status_id_to_sys_hier_info | 9 | const | 276168 | using where |
+----+-------------+----------+------+----------------------------------------------------------+------------------------------------+---------+-------+--------+-------------+
可以看出,有两个索引可用fk_task_status_id_to_sys_hier_info,tsk_task_key_mon_time,而最终执行语句时采用了status_id上的外键索引。
再看一下tsk_task表的索引情况吧:
+----------+------------------------------------+-------------+-------------+
| table | key_name | column_name | cardinality |
+----------+------------+------------------------------------+--------------+
| tsk_task | primary | id | 999149 |
| tsk_task | fk_task_status_id_to_sys_hier_info | status_id | 16 |
| tsk_task | tsk_task_key_mon_time | mon_time | 13502 |
+----------+------------------------------------+-------------+-----------+--
在oracle或其他关系数据库下,where条件中的字段顺序对索引的选择起着很重要的作用。我们调整一下字段顺序,把status_id放在后面,再explain一下:
explain select * from tsk_task where mon_time >= '2007-11-22' and mon_time < '2007-11-23' and status_id = 1064;
但是没什么效果,mysql还是选用系统建立的status_id外键索引。
仔细分析一下,看来cardinality属性(即索引中的唯一值的个数)对索引的选择起了极其重要的作用,mysql选择了索引值唯一值个数小的那个索引作为整条语句的索引。
针对这条语句,如果使用fk_task_status_id_to_sys_hier_info做索引,而tsk_task表中存放很多天数据的话,那扫描的记录数会很多,速度较慢。可以有以下几个优化方案:
如果一天的任务数不多的话,我们删除索引fk_task_status_id_to_sys_hier_info,那mysql会使用索引tsk_task_key_mon_time,然后在该天的数据中在扫描status_id为1064的记录,那速度也不慢;
如果一天的任务数多的话,我们需删除索引fk_task_status_id_to_sys_hier_info和tsk_task_key_mon_time,然后再建立status_id,mon_time的联合索引,这样效率肯定会很高。
因此建议,对那些记录数多的表,建议不要使用外键,以避免造成性能效率的严重降低。
2. 尽量控制每张表的记录数
当一张表的记录数很大时,管理和维护就会很麻烦,如索引维护就会占用很长时间,从而会给系统的正常运行造成很大的干扰。
对随时间推移数据量不断增长的表,我们可以根据时间来区分实时数据和历史数据,可以使用后台服务程序定期移动实时表中的数据到历史表中,从而控制实时表的记录数,提高查询和操作效率。但注意每次移动的时间要足够短,不要影响正常程序的数据写入。如果占用时间太长,可能会造成死锁问题。
3. 数据散列(partition)策略
当客户数达到一定规模后,单个数据库将无法支撑更高的并发访问,此时可以考虑把客户数据散列(partition)到多个数据库中,以分担负载,提高系统的整体性能与效率。
数据散列可以考虑采用federeated或sharded方式,网上有不少这方面的资料。
一些小技巧
1. 如何查出效率低的语句?
在mysql下,在启动参数中设置 --log-slow-queries=[文件名],就可以在指定的日志文件中记录执行时间超过long_query_time(缺省为10秒)的sql语句。你也可以在启动配置文件中修改long query的时间,如:
# set long query time to 8 seconds
long_query_time=8
2. 如何查询某表的索引?
可使用show index语句,如:
show index from [表名]
3. 如何查询某条语句的索引使用情况?
可用explain语句来看一下某条select语句的索引使用情况。如果是update或delete语句,需要先转换为select语句。
4. 如何把导出innodb引擎的内容到错误日志文件中?
我们可以使用show innodb status命令来查看innodb引擎的很多有用的信息,如当前进程、事务、外键错误、死锁问题和其它一些统计数据。如何让该信息能记录在日志文件中呢?只要使用如下语句创建innodb_monitor表,mysql就会每15秒钟把该系统写入到错误日志文件中:
create table innodb_monitor (a int) engine=innodb;
如果你不再需要导出到错误日志文件,只要删除该表即可:
drop table innodb_monitor;
5. 如何定期删除庞大的日志文件?
只要在启动配置文件中设置日志过期时间即可:
expire_logs_days=10
经验教训
1. 重点关注索引
下面以表tsk_task表为例说明sql语句优化过程。tsk_task表用于保存系统监测任务,相关字段及索引如下:
id:主键;
mon_time:监测时间;建了索引;
status_id:任务状态;与sys_hier_info.id建立了外键关系。
注mysql自动会为外键建立索引,在本次优化过程中,发现这些自动建立的外键索引会对sql语句的效率产生不必要的干扰,需要特别注意!
首先,我们在日志文件中查到下面语句的执行比较慢,超过10秒了:
# query_time: 18 lock_time: 0 rows_sent: 295 rows_examined: 88143
select * from tsk_task where status_id = 1064 and mon_time >= '2007-11-22' and mon_time < '2007-11-23';
哦,原来在88143条记录中要查出符合条件的295条记录,那当然慢了。赶紧用explain语句看一下索引使用情况吧:
+----+-------------+----------+------+----------------------------------------------------------+------------------------------------+---------+-------+--------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | extra |
+----+-------------+----------+------+----------------------------------------------------------+------------------------------------+---------+-------+--------+-------------+
| 1 | simple | tsk_task | ref | fk_task_status_id_to_sys_hier_info,tsk_task_key_mon_time | fk_task_status_id_to_sys_hier_info | 9 | const | 276168 | using where |
+----+-------------+----------+------+----------------------------------------------------------+------------------------------------+---------+-------+--------+-------------+
可以看出,有两个索引可用fk_task_status_id_to_sys_hier_info,tsk_task_key_mon_time,而最终执行语句时采用了status_id上的外键索引。
再看一下tsk_task表的索引情况吧:
+----------+------------------------------------+-------------+-------------+
| table | key_name | column_name | cardinality |
+----------+------------+------------------------------------+--------------+
| tsk_task | primary | id | 999149 |
| tsk_task | fk_task_status_id_to_sys_hier_info | status_id | 16 |
| tsk_task | tsk_task_key_mon_time | mon_time | 13502 |
+----------+------------------------------------+-------------+-----------+--
在oracle或其他关系数据库下,where条件中的字段顺序对索引的选择起着很重要的作用。我们调整一下字段顺序,把status_id放在后面,再explain一下:
explain select * from tsk_task where mon_time >= '2007-11-22' and mon_time < '2007-11-23' and status_id = 1064;
但是没什么效果,mysql还是选用系统建立的status_id外键索引。
仔细分析一下,看来cardinality属性(即索引中的唯一值的个数)对索引的选择起了极其重要的作用,mysql选择了索引值唯一值个数小的那个索引作为整条语句的索引。
针对这条语句,如果使用fk_task_status_id_to_sys_hier_info做索引,而tsk_task表中存放很多天数据的话,那扫描的记录数会很多,速度较慢。可以有以下几个优化方案:
如果一天的任务数不多的话,我们删除索引fk_task_status_id_to_sys_hier_info,那mysql会使用索引tsk_task_key_mon_time,然后在该天的数据中在扫描status_id为1064的记录,那速度也不慢;
如果一天的任务数多的话,我们需删除索引fk_task_status_id_to_sys_hier_info和tsk_task_key_mon_time,然后再建立status_id,mon_time的联合索引,这样效率肯定会很高。
因此建议,对那些记录数多的表,建议不要使用外键,以避免造成性能效率的严重降低。
2. 尽量控制每张表的记录数
当一张表的记录数很大时,管理和维护就会很麻烦,如索引维护就会占用很长时间,从而会给系统的正常运行造成很大的干扰。
对随时间推移数据量不断增长的表,我们可以根据时间来区分实时数据和历史数据,可以使用后台服务程序定期移动实时表中的数据到历史表中,从而控制实时表的记录数,提高查询和操作效率。但注意每次移动的时间要足够短,不要影响正常程序的数据写入。如果占用时间太长,可能会造成死锁问题。
3. 数据散列(partition)策略
当客户数达到一定规模后,单个数据库将无法支撑更高的并发访问,此时可以考虑把客户数据散列(partition)到多个数据库中,以分担负载,提高系统的整体性能与效率。
数据散列可以考虑采用federeated或sharded方式,网上有不少这方面的资料。
一些小技巧
1. 如何查出效率低的语句?
在mysql下,在启动参数中设置 --log-slow-queries=[文件名],就可以在指定的日志文件中记录执行时间超过long_query_time(缺省为10秒)的sql语句。你也可以在启动配置文件中修改long query的时间,如:
# set long query time to 8 seconds
long_query_time=8
2. 如何查询某表的索引?
可使用show index语句,如:
show index from [表名]
3. 如何查询某条语句的索引使用情况?
可用explain语句来看一下某条select语句的索引使用情况。如果是update或delete语句,需要先转换为select语句。
4. 如何把导出innodb引擎的内容到错误日志文件中?
我们可以使用show innodb status命令来查看innodb引擎的很多有用的信息,如当前进程、事务、外键错误、死锁问题和其它一些统计数据。如何让该信息能记录在日志文件中呢?只要使用如下语句创建innodb_monitor表,mysql就会每15秒钟把该系统写入到错误日志文件中:
create table innodb_monitor (a int) engine=innodb;
如果你不再需要导出到错误日志文件,只要删除该表即可:
drop table innodb_monitor;
5. 如何定期删除庞大的日志文件?
只要在启动配置文件中设置日志过期时间即可:
expire_logs_days=10
经验教训
1. 重点关注索引
下面以表tsk_task表为例说明sql语句优化过程。tsk_task表用于保存系统监测任务,相关字段及索引如下:
id:主键;
mon_time:监测时间;建了索引;
status_id:任务状态;与sys_hier_info.id建立了外键关系。
注mysql自动会为外键建立索引,在本次优化过程中,发现这些自动建立的外键索引会对sql语句的效率产生不必要的干扰,需要特别注意!
首先,我们在日志文件中查到下面语句的执行比较慢,超过10秒了:
# query_time: 18 lock_time: 0 rows_sent: 295 rows_examined: 88143
select * from tsk_task where status_id = 1064 and mon_time >= '2007-11-22' and mon_time < '2007-11-23';
哦,原来在88143条记录中要查出符合条件的295条记录,那当然慢了。赶紧用explain语句看一下索引使用情况吧:
+----+-------------+----------+------+----------------------------------------------------------+------------------------------------+---------+-------+--------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | extra |
+----+-------------+----------+------+----------------------------------------------------------+------------------------------------+---------+-------+--------+-------------+
| 1 | simple | tsk_task | ref | fk_task_status_id_to_sys_hier_info,tsk_task_key_mon_time | fk_task_status_id_to_sys_hier_info | 9 | const | 276168 | using where |
+----+-------------+----------+------+----------------------------------------------------------+------------------------------------+---------+-------+--------+-------------+
可以看出,有两个索引可用fk_task_status_id_to_sys_hier_info,tsk_task_key_mon_time,而最终执行语句时采用了status_id上的外键索引。
再看一下tsk_task表的索引情况吧:
+----------+------------------------------------+-------------+-------------+
| table | key_name | column_name | cardinality |
+----------+------------+------------------------------------+--------------+
| tsk_task | primary | id | 999149 |
| tsk_task | fk_task_status_id_to_sys_hier_info | status_id | 16 |
| tsk_task | tsk_task_key_mon_time | mon_time | 13502 |
+----------+------------------------------------+-------------+-----------+--
在oracle或其他关系数据库下,where条件中的字段顺序对索引的选择起着很重要的作用。我们调整一下字段顺序,把status_id放在后面,再explain一下:
explain select * from tsk_task where mon_time >= '2007-11-22' and mon_time < '2007-11-23' and status_id = 1064;
但是没什么效果,mysql还是选用系统建立的status_id外键索引。
仔细分析一下,看来cardinality属性(即索引中的唯一值的个数)对索引的选择起了极其重要的作用,mysql选择了索引值唯一值个数小的那个索引作为整条语句的索引。
针对这条语句,如果使用fk_task_status_id_to_sys_hier_info做索引,而tsk_task表中存放很多天数据的话,那扫描的记录数会很多,速度较慢。可以有以下几个优化方案:
如果一天的任务数不多的话,我们删除索引fk_task_status_id_to_sys_hier_info,那mysql会使用索引tsk_task_key_mon_time,然后在该天的数据中在扫描status_id为1064的记录,那速度也不慢;
如果一天的任务数多的话,我们需删除索引fk_task_status_id_to_sys_hier_info和tsk_task_key_mon_time,然后再建立status_id,mon_time的联合索引,这样效率肯定会很高。
因此建议,对那些记录数多的表,建议不要使用外键,以避免造成性能效率的严重降低。
2. 尽量控制每张表的记录数
当一张表的记录数很大时,管理和维护就会很麻烦,如索引维护就会占用很长时间,从而会给系统的正常运行造成很大的干扰。
对随时间推移数据量不断增长的表,我们可以根据时间来区分实时数据和历史数据,可以使用后台服务程序定期移动实时表中的数据到历史表中,从而控制实时表的记录数,提高查询和操作效率。但注意每次移动的时间要足够短,不要影响正常程序的数据写入。如果占用时间太长,可能会造成死锁问题。
3. 数据散列(partition)策略
当客户数达到一定规模后,单个数据库将无法支撑更高的并发访问,此时可以考虑把客户数据散列(partition)到多个数据库中,以分担负载,提高系统的整体性能与效率。
数据散列可以考虑采用federeated或sharded方式,网上有不少这方面的资料。
一些小技巧
1. 如何查出效率低的语句?
在mysql下,在启动参数中设置 --log-slow-queries=[文件名],就可以在指定的日志文件中记录执行时间超过long_query_time(缺省为10秒)的sql语句。你也可以在启动配置文件中修改long query的时间,如:
# set long query time to 8 seconds
long_query_time=8
2. 如何查询某表的索引?
可使用show index语句,如:
show index from [表名]
3. 如何查询某条语句的索引使用情况?
可用explain语句来看一下某条select语句的索引使用情况。如果是update或delete语句,需要先转换为select语句。
4. 如何把导出innodb引擎的内容到错误日志文件中?
我们可以使用show innodb status命令来查看innodb引擎的很多有用的信息,如当前进程、事务、外键错误、死锁问题和其它一些统计数据。如何让该信息能记录在日志文件中呢?只要使用如下语句创建innodb_monitor表,mysql就会每15秒钟把该系统写入到错误日志文件中:
create table innodb_monitor (a int) engine=innodb;
如果你不再需要导出到错误日志文件,只要删除该表即可:
drop table innodb_monitor;
5. 如何定期删除庞大的日志文件?
只要在启动配置文件中设置日志过期时间即可:
expire_logs_days=10
经验教训
1. 重点关注索引
下面以表tsk_task表为例说明sql语句优化过程。tsk_task表用于保存系统监测任务,相关字段及索引如下:
id:主键;
mon_time:监测时间;建了索引;
status_id:任务状态;与sys_hier_info.id建立了外键关系。
注mysql自动会为外键建立索引,在本次优化过程中,发现这些自动建立的外键索引会对sql语句的效率产生不必要的干扰,需要特别注意!
首先,我们在日志文件中查到下面语句的执行比较慢,超过10秒了:
# query_time: 18 lock_time: 0 rows_sent: 295 rows_examined: 88143
select * from tsk_task where status_id = 1064 and mon_time >= '2007-11-22' and mon_time < '2007-11-23';
哦,原来在88143条记录中要查出符合条件的295条记录,那当然慢了。赶紧用explain语句看一下索引使用情况吧:
+----+-------------+----------+------+----------------------------------------------------------+------------------------------------+---------+-------+--------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | extra |
+----+-------------+----------+------+----------------------------------------------------------+------------------------------------+---------+-------+--------+-------------+
| 1 | simple | tsk_task | ref | fk_task_status_id_to_sys_hier_info,tsk_task_key_mon_time | fk_task_status_id_to_sys_hier_info | 9 | const | 276168 | using where |
+----+-------------+----------+------+----------------------------------------------------------+------------------------------------+---------+-------+--------+-------------+
可以看出,有两个索引可用fk_task_status_id_to_sys_hier_info,tsk_task_key_mon_time,而最终执行语句时采用了status_id上的外键索引。
再看一下tsk_task表的索引情况吧:
+----------+------------------------------------+-------------+-------------+
| table | key_name | column_name | cardinality |
+----------+------------+------------------------------------+--------------+
| tsk_task | primary | id | 999149 |
| tsk_task | fk_task_status_id_to_sys_hier_info | status_id | 16 |
| tsk_task | tsk_task_key_mon_time | mon_time | 13502 |
+----------+------------------------------------+-------------+-----------+--
在oracle或其他关系数据库下,where条件中的字段顺序对索引的选择起着很重要的作用。我们调整一下字段顺序,把status_id放在后面,再explain一下:
explain select * from tsk_task where mon_time >= '2007-11-22' and mon_time < '2007-11-23' and status_id = 1064;
但是没什么效果,mysql还是选用系统建立的status_id外键索引。
仔细分析一下,看来cardinality属性(即索引中的唯一值的个数)对索引的选择起了极其重要的作用,mysql选择了索引值唯一值个数小的那个索引作为整条语句的索引。
针对这条语句,如果使用fk_task_status_id_to_sys_hier_info做索引,而tsk_task表中存放很多天数据的话,那扫描的记录数会很多,速度较慢。可以有以下几个优化方案:
如果一天的任务数不多的话,我们删除索引fk_task_status_id_to_sys_hier_info,那mysql会使用索引tsk_task_key_mon_time,然后在该天的数据中在扫描status_id为1064的记录,那速度也不慢;
如果一天的任务数多的话,我们需删除索引fk_task_status_id_to_sys_hier_info和tsk_task_key_mon_time,然后再建立status_id,mon_time的联合索引,这样效率肯定会很高。
因此建议,对那些记录数多的表,建议不要使用外键,以避免造成性能效率的严重降低。
2. 尽量控制每张表的记录数
当一张表的记录数很大时,管理和维护就会很麻烦,如索引维护就会占用很长时间,从而会给系统的正常运行造成很大的干扰。
对随时间推移数据量不断增长的表,我们可以根据时间来区分实时数据和历史数据,可以使用后台服务程序定期移动实时表中的数据到历史表中,从而控制实时表的记录数,提高查询和操作效率。但注意每次移动的时间要足够短,不要影响正常程序的数据写入。如果占用时间太长,可能会造成死锁问题。
3. 数据散列(partition)策略
当客户数达到一定规模后,单个数据库将无法支撑更高的并发访问,此时可以考虑把客户数据散列(partition)到多个数据库中,以分担负载,提高系统的整体性能与效率。
数据散列可以考虑采用federeated或sharded方式,网上有不少这方面的资料。
配置my.cnf/my.ini,增加 --log-slow-queries 配置,记录所有的slow query,然后挨个优化
本文来源于 web开发网
select @a=drclass1, @b=drclass2, @c=drclass3, @d=drclass4, @e=drclass5 from teacher where teacherid = @teacherid
create table classname(classname char(50))
insert into classname (classname) values (@a)
if (@b is not null)
begin
insert into classname (classname) values (@b)
if (@c is not null)
begin
insert into classname (classname) values (@c)
if (@d is not null)
begin
insert into classname (classname) values (@d)
if (@e is not null)
begin
insert into classname (classname) values (@e)
end
end
end
end
select * from classname
以上这些sql语句能不能转成一个存储过程?我自己试了下
alter procedure pr_getclass
@teacherid int,
@a char(50),
@b char(50),
@c char(50),
@d char(50),
@e char(50)
as
select @a=drclass1, @b=drclass2, @c=drclass3, @d=drclass4, @e=drclass5 from teacher where teacherid = @teacherid
drop table classname
create table classname(classname char(50))
insert into classname (classname) values (@a)
if (@b is not null)
begin
insert into classname (classname) values (@b)
if (@c is not null)
begin
insert into classname (classname) values (@c)
if (@d is not null)
begin
insert into classname (classname) values (@d)
if (@e is not null)
begin
insert into classname (classname) values (@e)
end
end
end
end
select * from classname
但是这样的话,这个存储过程就有6个变量,实际上应该只提供一个变量就可以了
主要的问题就是自己没搞清楚 @a,@b,@c,@d 等是临时变量,是放在as后面重新做一些申明的,而不是放在开头整个存储过程的变量定义。
(标准化越来越近了):namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />
实战sql语句收集(不断更新中--)
前言:这里将我编程实践中遇到的有价值的sql语句一路记下来,一方面方便自己查用,一方面也夯实下即将遗忘的回忆。整个过程中我会不断更新,直到不能再加为止,同时,这里只记录最实用的咚咚,不效仿学院派的那一套。
一、常用sql语句荟萃
1,查询:
1.1,简单查询:select * from table where
1.2,连接查询:
什么是连接查询?顾名释义,就是查询时涉及多个表的查询。是以说到连接,废话一下,要知道连接还是关系数据库的主要特点呢。
连接查询分为三种:外连接(outer join),内连接(inner join),交叉连接(cross join)。
(标准化越来越近了):namespace prefix = st1 ns = "urn:schemas-microsoft-com:office:smarttags" />1.2.1,内连接(inner join)使用比较运算符进行表间某(些)列数据的比较操作,并列出这些表中与连接条件相匹配的数据行。根据所使用的比较方式不同,内连接又分为等值连接、自然连接和不等连接三种。
1.2.2,外连接分为左外连接(left outer join或left join)、右外连接(right outer join或right join)和全外连接(full outer join或full join)三种。与内连接不同的是,外连接不只列出与连接条件相匹配的行,而是列出左表(左外连接时)、右表(右外连接时)或两个表(全外连接时)中所有符合搜索条件的数据行。
1.2.3,交叉连接(cross join)没有where 子句,它返回连接表中所有数据行的笛卡尔积,其结果集合中的数据行数等于第一个表中符合查询条件的数据行数乘以第二个表中符合查询条件的数据行数。连接操作中的on (join_condition) 子句指出连接条件,它由被连接表中的列和比较运算符、逻辑运算符等构成。
1.2.4,无论哪种连接都不能对text、ntext和image数据类型列进行直接连接,但可以对这三种列进行间接连接。例如:
select p1.pub_id,p2.pub_id,p1.pr_info
from pub_info as p1 inner join pub_info as p2
on datalength(p1.pr_info)=datalength(p2.pr_info)
1.2.5,使用where子句设置查询条件
where子句设置查询条件,过滤掉不需要的数据行。例如下面语句查询年龄大于20的数据:
select *
from usertable
where age>20
where子句可包括各种条件运算符:
比较运算符(大小比较):>、>=、=、<、<=、<>、!>、!<
范围运算符(表达式值是否在指定的范围):between…and…
not between…and…
列表运算符(判断表达式是否为列表中的指定项):in (项1,项2……)
not in (项1,项2……)
模式匹配符(判断值是否与指定的字符通配格式相符):like、not like
空值判断符(判断表达式是否为空):is null、not is null
逻辑运算符(用于多条件的逻辑连接):not、and、or
1、范围运算符例:age between 10 and 30相当于age>=10 and age<=30
2、列表运算符例:country in ('germany','china')
3、模式匹配符例:常用于模糊查找,它判断列值是否与指定的字符串格式相匹配。可用于char、varchar、text、ntext、datetime和smalldatetime等类型查询。
可使用以下通配字符:
百分号%:可匹配任意类型和长度的字符,如果是中文,请使用两个百分号即%%。
下划线_:匹配单个任意字符,它常用来限制表达式的字符长度。
方括号[]:指定一个字符、字符串或范围,要求所匹配对象为它们中的任一个。
[^]:其取值也[] 相同,但它要求所匹配对象为指定字符以外的任一个字符。
例如:
限制以publishing结尾,使用like '%publishing'
限制以a开头:like '[a]%'
限制以a开头外:like '[^a]%'
空值判断符例:where age is null
2,更新:update table
3,插入:
3.1,一般插入:
insert into publishers
(pub_id, pub_name, city, state)
values
('9001', 'acme publishing', 'new york', 'ny')
3.2,插入多行
使用 insert 语句可以向表添加多行数据。这些多行数据是从已经存有这些数据的另一个表中选取的。本例中,向 pubhold 表中添加有关在加利福尼亚和德克萨斯州的出版商的数据。这些数据可从 publishers 表中获得。
insert pubhpold select * from publishers
where state = 'ca' or state = 'tx'
4,删除:
4.1,delete语句联合删除:
delete from uu_suitetominclassroomsect
where min_classroom_sect_id in
(select min_classroom_sect_id
from uu_minclassroomsect
where min_classroom_id = '112')
二、视图使用细则
1,一个典型的视图
create view view_uugrouptaxis
as
select uu_groupinfo.group_id, uu_groupinfo.group_name,
uu_grouptype.main_type, uu_grouptype.group_type_name,
uu_groupinfo.group_icon_url, isnull
((select count(*)
from uu_groupuser
where uu_groupinfo.group_id = uu_groupuser.group_id), 0)
* 50 + isnull(uu_groupinfo.fundcount, 0) + isnull
((select count(*)
from dv_topic
where dv_topic.boardid = uu_groupinfo.subforum_id), 0) * 5 + isnull
((select count(*)
from uu_groupphotos
where uu_groupphotos.group_id = uu_groupinfo.group_id), 0)
* 10 + isnull(uu_groupinfo.topic_account, 0)
* 2 + isnull(uu_groupinfo.hit_account, 0) as group_activedegree,
isnull
((select count(*)
from uu_groupuser
where uu_groupinfo.group_id = uu_groupuser.group_id), 0)
as group_membernum, isnull(uu_groupinfo.fundcount, 0) as fundcount,
(select count(*)
from dv_topic
where dv_topic.boardid = uu_groupinfo.subforum_id) as group_articlenum,
(select count(*)
from uu_groupphotos
where uu_groupphotos.group_id = uu_groupinfo.group_id) as group_photonum,
uu_groupinfo.topic_account, uu_groupinfo.hit_account,
(select user_name
from uu_registeruser
where uu_registeruser.user_id = uu_groupinfo.creator_id)
as group_creatorname, uu_groupinfo.create_time
from uu_groupinfo inner join
uu_grouptype on
uu_groupinfo.group_type_id = uu_grouptype.group_type_id
三,存储过程的创建和调用
1,存储过程的调用
execute procedurename @param='value'
2,一个典型的带参数存储过程
create procedure p_delminiclassproc
@miniclassroom_id int
as
declare @billtag varchar(4)
set nocount on
if @miniclassroom_id is null
begin
return(-1)
end
else
begin transaction
--删除套餐信息
delete from uu_suitetominclassroomsect
where min_classroom_sect_id in
(select min_classroom_sect_id
from uu_minclassroomsect
where min_classroom_id =@miniclassroom_id)
--删除小课堂段信息
delete from uu_minclassroomsect
where min_classroom_id = @miniclassroom_id
--删除小课堂用户购买记录
delete from uu_userbuyminclassroom
where min_classroom_id = @miniclassroom_id
--删除对应小课堂年级学科信息
delete from uu_minclassroomtogradeclass
where min_classroom_id = @miniclassroom_id
--删除小课堂发言
delete from uu_minclassroomdiscuss
where min_classroom_id = @miniclassroom_id
--删除课程讨论
delete from uu_coursediscuss
where course_id in
(select course_id
from uu_courseinfo
where min_classroom_id = @miniclassroom_id)
--删除用户课程收藏
sql语句的优化是将性能低下的sql语句转换成目的相同的性能优异的sql语句。
人工智能自动sql优化就是使用人工智能技术,自动对sql语句进行重写,从而找到性能最好的等效sql语句。
数据库性能的优化
一个数据库系统的生命周期可以分成:设计、开发和成品三个阶段。在设计阶段进行数据库性能优化的成本最低,收益最大。在成品阶段进行数据库性能优化的成本最高,收益最小。
数据库的优化通常可以通过对网络、硬件、操作系统、数据库参数和应用程序的优化来进行。最常见的优化手段就是对硬件的升级。根据统计,对网络、硬件、操作系统、数据库参数进行优化所获得的性能提升,全部加起来只占数据库系统性能提升的40%左右,其余的60%系统性能提升来自对应用程序的优化。许多优化专家认为,对应用程序的优化可以得到80%的系统性能的提升。
应用程序的优化
应用程序的优化通常可分为两个方面:源代码和sql语句。由于涉及到对程序逻辑的改变,源代码的优化在时间成本和风险上代价很高,而对数据库系统性能的提升收效有限。
为什么要优化sql语句
. sql语句是对数据库进行操作的惟一途径,对数据库系统的性能起着决定性的作用。
. sql语句消耗了70%至90%的数据库资源。
. sql语句独立于程序设计逻辑,对sql语句进行优化不会影响程序逻辑。
. sql语句有不同的写法,在性能上的差异非常大。
. sql语句易学,但难精通。
优化sql语句的传统方法是通过手工重写来对sql语句进行优化。dba或资深程序员通过对sql语句执行计划的分析,依靠经验,尝试重写sql语句,然后对结果和性能进行比较,以试图找到性能较佳的sql语句。这种传统上的作法无法找出sql语句的所有可能写法,且依赖于人的经验,非常耗费时间。
sql优化技术的发展历程
第一代sql优化工具是执行计划分析工具。这类工具针对输入的sql语句,从数据库提取执行计划,并解释执行计划中关键字的含义。
第二代sql优化工具只能提供增加索引的建议,它通过对输入的sql语句的执行计划的分析,来产生是否要增加索引的建议。
第三代sql优化工具不仅分析输入sql语句的执行计划,还对输入的sql语句本身进行语法分析,经过分析产生写法上的改进建议。
人工智能自动sql优化
sql语句性能优化--lecco sql expert
图1 人工智能自动sql优化示意图
人工智能自动sql优化出现在90年代末。目前在商用数据库领域,lecco technology limited(灵高科研有限公司)拥有该技术,并提供使用该技术的自动优化产品lecco sql expert,它支持oracle(大型网站数据库平台)、sybase、ms sql server(windows平台上强大的数据库平台)和ibm db2数据库平台。该产品针对数据库应用的开发和维护阶段提供的模块有:sql语法优化器、pl/sql集成化开发调试环境(ide)、扫描器、数据库监视器等。其核心模块sql 语法优化器的工作原理为:①输入一条源sql语句;②“人工智能反馈式搜索引擎”对输入的sql语句,结合检测到的数据库结构和索引进行重写,产生n条等效的sql语句输出;③产生的n条等效sql语句再送入“人工智能反馈式搜索引擎”进行重写,直至无法产生新的输出或搜索限额满;④对输出的sql语句进行过滤,选出具有不同执行计划的sql语句;⑤对得到的sql语句进行批量测试,找出性能最好的sql语句。
lecco sql expert自动优化实例
假设我们从源代码中抽取出这条sql语句(也可以通过内带的扫描器或监视器获得sql语句):
select count(*)
from employee
swheresexists (select 'x'
from department
swheresemp_dept=dpt_id
and dpt_name like 'ac%')
and emp_id in (select sal_emp_id
from emp_sal_hist b
swheressal_salary > 70000)
按下“优化”按钮后,经过10几秒,sql expert就完成了优化的过程,并在这10几秒的时间里重写产生了2267 条等价的sql语句,其中136条sql语句有不同的执行计划。
接下来,我们可以对自动重写产生的136条sql语句进行批运行测试,以选出性能最佳的等效sql语句。按下“批运行” 按钮,在“终止条件” 页选择“最佳运行时间sql语句”,按“确定”。
经过几分钟的测试运行后,我们可以发现sql124的运行时间和反应时间最短。运行速度约有22.75倍的提升(源sql语句运行时间为2.73秒,sql124运行时间为0.12秒)。现在我们就可以把sql124放入源代码中,结束一条sql语句的优化工作了。
“边做边学式训练”提升sql开发水平
lecco sql expert不仅能够找到最佳的sql语句,它所提供的“边做边学式训练”还能够教开发人员和数据库管理员如何写出性能最好的sql语句。lecco sql expert的“sql比较器”可以标明源sql和待选sql间的不同之处。
以上面优化的结果为例,为了查看源sql语句和sql124在写法上有什么不同,我们可以按下“比较器” 按钮,对sql124和源sql语句进行比较。“sql 比较器”将sql124相对于源sql语句的不同之处以蓝颜色表示了出来。如果选择“双向比较”复选框,“sql 比较器”可以将两条sql语句的不同之处以蓝色表示。当然,我们也可以从源语句和重写后的sql 语句中任选两条进行比较。
从比较的结果可以看到,重写后的sql124把第一个exists改写成了in;在字段dpt_id上进行了合并空字符串的操作,以诱导数据库先执行子查询中的
(select dpt_id||''
from department
where dpt_name like 'ac%')
在子查询完成后,再与employee表进行嵌套循环连接(nested loop join)。
如果觉得对写法的改变难以理解,还可以点中“执行计划”复选框,通过比较两条sql语句的执行计划的不同,来了解其中的差异。在查看执行计划过程中,如果有什么不明白的地方,可以点中“sql信息按钮”,再点击执行计划看不明白的地方,lecco sql expert的上下文敏感帮助系统将提供执行计划该处的解释。
在“sql比较器”中,选中“统计信息”复选框后,可得到详细的两条sql语句运行时的统计信息比较,这对于学习不同的sql写法对数据库资源的消耗很有帮助。
lecco sql expert优化模块的特点
lecco sql expert优化模块的特点主要表现为:自动优化sql语句;以独家的人工智能知识库“反馈式搜索引擎”来重写性能优异的sql语句;找出所有等效的sql语句及可能的执行计划;保证产生相同的结果;先进的sql语法分析器能处理最复杂的sql语句;可以重写select、select into、update、insert和delete语句;通过测试运行,为应用程序和数据库自动找到性能最好的sql语句;提供微秒级的计时,能够优化web应用程序和有大量用户的在线事务处理中运行时间很短的sql语句;为开发人员提供“边做边学式训练”,迅速提高开发人员的sql编程技能;提供上下文敏感的执行计划帮助系统和sql运行状态帮助;不是猜测或建议,而是独一无二的sql重写解决方案。
写出专家级的sql语句
lecco sql expert的出现,使sql的优化变得极其简单,只要能够写出sql语句,它就能帮用户找到最好性能的写法。lecco sql expert不仅能在很短的时间内找到所有可能的优化方案,而且能够通过实际测试,确定最有效的优化方案。同以往的数据库优化手段相比较,lecco sql expert将数据库优化技术带到了一个崭新的技术高度,依赖人的经验、耗费大量时间、受人的思维束缚的数据库优化手段已经被高效、省时且准确的自动优化软件所取代了。通过内建的“lecco小助手”的帮助,即使是sql的开发新手,也能快速且简单地写出专家级的sql语句。
比如现在有一人员表 (表名:peosons)
若想将姓名、身份证号、住址这三个字段完全相同的记录查询出来
select p1.* from persons p1,persons p2 where p1.id<>p2.id and p1.cardid = p2.cardid and p1.pname = p2.pname and p1.address = p2.address
可以实现上述效果.
select语句前加:
declare @d datetime
set @d=getdate()
并在select语句后加:
select [语句执行花费时间(毫秒)]=datediff(ms,@d,getdate())
transact_sql小手册
*******************transact_sql********************
--语 句 功 能
--数据操作
select --从数据库表中检索数据行和列
insert --向数据库表添加新数据行
delete --从数据库表中删除数据行
update --更新数据库表中的数据
--数据定义
create table --创建一个数据库表
drop table --从数据库中删除表
alter table --修改数据库表结构
create view --创建一个视图
drop view --从数据库中删除视图
create index --为数据库表创建一个索引
drop index --从数据库中删除索引
create procedure --创建一个存储过程
drop procedure --从数据库中删除存储过程
create trigger --创建一个触发器
drop trigger --从数据库中删除触发器
create schema --向数据库添加一个新模式
drop schema --从数据库中删除一个模式
create domain --创建一个数据值域
alter domain --改变域定义
drop domain --从数据库中删除一个域
--数据控制
grant --授予用户访问权限
deny --拒绝用户访问
revoke --解除用户访问权限
--事务控制
commit --结束当前事务
rollback --中止当前事务
set transaction --定义当前事务数据访问特征
--程序化sql
declare --为查询设定游标
explan --为查询描述数据访问计划
open --检索查询结果打开一个游标
fetch --检索一行查询结果
close --关闭游标
prepare --为动态执行准备sql 语句
execute --动态地执行sql 语句
describe --描述准备好的查询
---局部变量
declare @id char(10)
--set @id = '10010001'
select @id = '10010001'
---全局变量
---必须以@@开头
--if else
declare @x int @y int @z int
select @x = 1 @y = 2 @z=3
if @x > @y
print 'x > y' --打印字符串'x > y'
else if @y > @z
print 'y > z'
else print 'z > y'
--case
use pangu
update employee
set e_wage =
case
when job_level = '1' then e_wage*1.08
when job_level = '2' then e_wage*1.07
when job_level = '3' then e_wage*1.06
else e_wage*1.05
end
--while continue break
declare @x int @y int @c int
select @x = 1 @y=1
while @x < 3
begin
print @x --打印变量x 的值
while @y < 3
begin
select @c = 100*@x + @y
print @c --打印变量c 的值
select @y = @y + 1
end
select @x = @x + 1
select @y = 1
end
--waitfor
--例 等待1 小时2 分零3 秒后才执行select 语句
waitfor delay '01:02:03'
select * from employee
--例 等到晚上11 点零8 分后才执行select 语句
waitfor time '23:08:00'
select * from employee
***select***
select *(列名) from table_name(表名) where column_name operator value
ex:(宿主)
select * from stock_information where stockid = str(nid)
stockname = 'str_name'
stockname like '% find this %'
stockname like '[a-za-z]%' --------- ([]指定值的范围)
stockname like '[^f-m]%' --------- (^排除指定范围)
--------- 只能在使用like关键字的where子句中使用通配符)
or stockpath = 'stock_path'
or stocknumber < 1000
and stockindex = 24
not stocksex = 'man'
stocknumber between 20 and 100
stocknumber in(10,20,30)
order by stockid desc(asc) --------- 排序,desc-降序,asc-升序
order by 1,2 --------- by列号
stockname = (select stockname from stock_information where stockid = 4)
--------- 子查询
--------- 除非能确保内层select只返回一个行的值,
--------- 否则应在外层where子句中用一个in限定符
select distinct column_name form table_name --------- distinct指定检索独有的列值,不重复
select stocknumber ,"stocknumber + 10" = stocknumber + 10 from table_name
select stockname , "stocknumber" = count(*) from table_name group by stockname
--------- group by 将表按行分组,指定列中有相同的值
having count(*) = 2 --------- having选定指定的组
select *
from table1, table2
where table1.id *= table2.id -------- 左外部连接,table1中有的而table2中没有得以null表示
table1.id =* table2.id -------- 右外部连接
select stockname from table1
union [all] ----- union合并查询结果集,all-保留重复行
select stockname from table2
***insert***
insert into table_name (stock_name,stock_number) value ("xxx","xxxx")
value (select stockname , stocknumber from stock_table2)---value为select语句
***update***
update table_name set stockname = "xxx" [where stockid = 3]
stockname = default
stockname = null
stocknumber = stockname + 4
***delete***
delete from table_name where stockid = 3
truncate table_name ----------- 删除表中所有行,仍保持表的完整性
drop table table_name --------------- 完全删除表
***alter table*** --- 修改数据库表结构
alter table database.owner.table_name add column_name char(2) null .....
sp_help table_name ---- 显示表已有特征
create table table_name (name char(20), age smallint, lname varchar(30))
insert into table_name select ......... ----- 实现删除列的方法(创建新表)
alter table table_name drop constraint stockname_default ---- 删除stockname的default约束
***function(/*常用函数*/)***
----统计函数----
avg --求平均值
count --统计数目
max --求最大值
min --求最小值
sum --求和
--avg
use pangu
select avg(e_wage) as dept_avgwage
from employee
group by dept_id
--max
--求工资最高的员工姓名
use pangu
select e_name
from employee
where e_wage =
(select max(e_wage)
from employee)
--stdev()
--stdev()函数返回表达式中所有数据的标准差
--stdevp()
--stdevp()函数返回总体标准差
--var()
--var()函数返回表达式中所有值的统计变异数
--varp()
--varp()函数返回总体变异数
----算术函数----
/***三角函数***/
sin(float_expression) --返回以弧度表示的角的正弦
cos(float_expression) --返回以弧度表示的角的余弦
tan(float_expression) --返回以弧度表示的角的正切
cot(float_expression) --返回以弧度表示的角的余切
/***反三角函数***/
asin(float_expression) --返回正弦是float 值的以弧度表示的角
acos(float_expression) --返回余弦是float 值的以弧度表示的角
atan(float_expression) --返回正切是float 值的以弧度表示的角
atan2(float_expression1,float_expression2)
--返回正切是float_expression1 /float_expres-sion2的以弧度表示的角
degrees(numeric_expression)
--把弧度转换为角度返回与表达式相同的数据类型可为
--integer/money/real/float 类型
radians(numeric_expression) --把角度转换为弧度返回与表达式相同的数据类型可为
--integer/money/real/float 类型
exp(float_expression) --返回表达式的指数值
log(float_expression) --返回表达式的自然对数值
log10(float_expression)--返回表达式的以10 为底的对数值
sqrt(float_expression) --返回表达式的平方根
/***取近似值函数***/
ceiling(numeric_expression) --返回>=表达式的最小整数返回的数据类型与表达式相同可为
--integer/money/real/float 类型
floor(numeric_expression) --返回<=表达式的最小整数返回的数据类型与表达式相同可为
--integer/money/real/float 类型
round(numeric_expression) --返回以integer_expression 为精度的四舍五入值返回的数据
--类型与表达式相同可为integer/money/real/float 类型
abs(numeric_expression) --返回表达式的绝对值返回的数据类型与表达式相同可为
--integer/money/real/float 类型
sign(numeric_expression) --测试参数的正负号返回0 零值1 正数或-1 负数返回的数据类型
--与表达式相同可为integer/money/real/float 类型
pi() --返回值为π 即3.1415926535897936
rand([integer_expression]) --用任选的[integer_expression]做种子值得出0-1 间的随机浮点数
----字符串函数----
ascii() --函数返回字符表达式最左端字符的ascii 码值
char() --函数用于将ascii 码转换为字符
--如果没有输入0 ~ 255 之间的ascii 码值char 函数会返回一个null 值
lower() --函数把字符串全部转换为小写
upper() --函数把字符串全部转换为大写
str() --函数把数值型数据转换为字符型数据
ltrim() --函数把字符串头部的空格去掉
rtrim() --函数把字符串尾部的空格去掉
left(),right(),substring() --函数返回部分字符串
charindex(),patindex() --函数返回字符串中某个指定的子串出现的开始位置
soundex() --函数返回一个四位字符码
--soundex函数可用来查找声音相似的字符串但soundex函数对数字和汉字均只返回0 值
difference() --函数返回由soundex 函数返回的两个字符表达式的值的差异
--0 两个soundex 函数返回值的第一个字符不同
--1 两个soundex 函数返回值的第一个字符相同
--2 两个soundex 函数返回值的第一二个字符相同
--3 两个soundex 函数返回值的第一二三个字符相同
--4 两个soundex 函数返回值完全相同
quotename() --函数返回被特定字符括起来的字符串
/*select quotename('abc', '{') quotename('abc')
运行结果如下
----------------------------------{
{abc} [abc]*/
replicate() --函数返回一个重复character_expression 指定次数的字符串
/*select replicate('abc', 3) replicate( 'abc', -2)
运行结果如下
----------- -----------
abcabcabc null*/
reverse() --函数将指定的字符串的字符排列顺序颠倒
replace() --函数返回被替换了指定子串的字符串
/*select replace('abc123g', '123', 'def')
运行结果如下
----------- -----------
abcdefg*/
space() --函数返回一个有指定长度的空白字符串
stuff() --函数用另一子串替换字符串指定位置长度的子串
----数据类型转换函数----
cast() 函数语法如下
cast() (<expression> as <data_ type>[ length ])
convert() 函数语法如下
convert() (<data_ type>[ length ], <expression> [, style])
select cast(100+99 as char) convert(varchar(12), getdate())
运行结果如下
------------------------------ ------------
199 jan 15 2000
----日期函数----
day() --函数返回date_expression 中的日期值
month() --函数返回date_expression 中的月份值
year() --函数返回date_expression 中的年份值
dateadd(<datepart> ,<number> ,<date>)
--函数返回指定日期date 加上指定的额外日期间隔number 产生的新日期
datediff(<datepart> ,<number> ,<date>)
--函数返回两个指定日期在datepart 方面的不同之处
datename(<datepart> , <date>) --函数以字符串的形式返回日期的指定部分
datepart(<datepart> , <date>) --函数以整数值的形式返回日期的指定部分
getdate() --函数以datetime 的缺省格式返回系统当前的日期和时间
----系统函数----
app_name() --函数返回当前执行的应用程序的名称
coalesce() --函数返回众多表达式中第一个非null 表达式的值
col_length(<'table_name'>, <'column_name'>) --函数返回表中指定字段的长度值
col_name(<table_id>, <column_id>) --函数返回表中指定字段的名称即列名
datalength() --函数返回数据表达式的数据的实际长度
db_id(['database_name']) --函数返回数据库的编号
db_name(database_id) --函数返回数据库的名称
host_id() --函数返回服务器端计算机的名称
host_name() --函数返回服务器端计算机的名称
identity(<data_type>[, seed increment]) [as column_name])
--identity() 函数只在select into 语句中使用用于插入一个identity column列到新表中
/*select identity(int, 1, 1) as column_name
into newtable
from oldtable*/
isdate() --函数判断所给定的表达式是否为合理日期
isnull(<check_expression>, <replacement_value>) --函数将表达式中的null 值用指定值替换
isnumeric() --函数判断所给定的表达式是否为合理的数值
newid() --函数返回一个uniqueidentifier 类型的数值
nullif(<expression1>, <expression2>)
--nullif 函数在expression1 与expression2 相等时返回null 值若不相等时则返回expression1 的值