数据库操作优化(insert、update、delete、select)

一、insert优化:

要操作的表为test,字段test。这里优化的方式是先组合一条多行的sql语句,然后再进行插入操作,这样就很大程度上优化了插入数据的效率。

测试代码如下:

$sql = "insert into `test` (`test`) values('$content')";
for ($i=1;$i<999;$i++) {
$sql .= ",('$content')";
}
mysql_query($sql);

执行时间为:

0.0323481559753

0.0371758937836

0.0419669151306

 更多的说明:

起因:有一个innoDB引擎的表Table,在一个大概3000次的foreach循环中执行

INSERT INTO Table(columnA, columnB) VALUES (valueA,valueB)


结果居然超出了60S的php执行限制(当然这个限制可以在php.ini中修改),让我很不解为何插入效率如此低下。

经过查找资料以及摸索,得到以下优化方法:

1、innoDB是mysql引擎中唯一支持事务transaction的引擎。默认所有用户行为都在事务内发生。
默认mysql建立新连接时,innoDB采用自动提交autocommit模式,每个SQL语句在它自己上形成一个单独的事务,即insert一次就commit了一次,InnoDB在该事务提交时必须刷新日志到磁盘,因此效率受限于磁盘读写效率。
你可以通过

mysql_query("SET AUTOCOMMIT = 0");


来关闭自动提交模式。

如 果自动提交模式被关闭,那么我们可以认为一个用户总是有一个事务打开着。一个SQLCOMMIT或ROLLBACK语句结束当前事务并且一个新事务开始。两个语句都释放所有在当前事务中被设置的InnoDB锁定。一个COMMIT语句意味着在当前事务中做的改变被生成为永久的,并且变成其它用户可见的。一个ROLLBACK语句,在另一方面,撤销所有当前事务做的修改。
当然如果是自动提交模式,通过用明确的STARTTRANSACTION或BEGIN语句来开始一个事务,并用COMMIT或者ROLLBACK语句来结束它,这样用户仍旧可以执行一个多重语句事务。

2、因此对于本例,在建立数据库连接后,立即关闭自动提交,在foreach循环结束后,一次commit即可,效率大大提升。

mysql_query("SET AUTOCOMMIT = 0");
foreach(***)
  INSERT INTO Table(columnA, columnB) VALUES(valueA, valueB)
mysql_query("commit");



3、对于多次insert行到同一表的需求,你还可以采用多行插入语法来减少客户端和服务器之间的通讯开支。


INSERT INTO Table(columnA, columnB) VALUES (1,2), (5,5),(3,3), ...



4、如果你的表有索引,索引会拖慢insert速度。大量插入数据时,可以先关闭索引,然后再重建索引。

ALTER TABLE Table DISABLE KEYS;
INSERT INTO ***;
ALTER TABLE Table ENABLE KEYS;

 

二、update优化:

 

三、select优化:

 使用索引优化查询:

2.索引作用

在索引列上,除了上面提到的有序查找之外,数据库利用各种各样的快速定位技术,能够大大提高查询效率。特别是当数据量非常大,查询涉及多个表时,使用索引往往能使查询速度加快成千上万倍。

例如,有3个未索引的表t1t2t3,分别只包含列c1c2c3,每个表分别含有1000行数据组成,指为11000的数值,查找对应值相等行的查询如下所示。

 

SELECT c1,c2,c3 FROM t1,t2,t3 WHERE c1=c2 ANDc1=c3

此查询结果应该为1000行,每行包含3个相等的值。在无索引的情况下处理此查询,必须寻找3个表所有的组合,以便得出与WHERE子句相配的那些行。而可能的组合数目为1000×1000×1000(十亿),显然查询将会非常慢。

如果对每个表进行索引,就能极大地加速查询进程。利用索引的查询处理如下。

1)从表t1中选择第一行,查看此行所包含的数据。

2使用表t2上的索引,直接定位t2中与t1的值匹配的行。类似,利用表t3上的索引,直接定位t3中与来自t1的值匹配的行。

3)扫描表t1的下一行并重复前面的过程,直到遍历t1中所有的行。

在此情形下,仍然对表t1执行了一个完全扫描,但能够在表t2t3上进行索引查找直接取出这些表中的行,比未用索引时要快一百万倍。

利用索引,MySQL加速了WHERE子句满足条件行的搜索,而在多表连接查询时,在执行连接时加快了与其他表中的行匹配的速度。

13.4.2  创建索引

在执行CREATE TABLE语句时可以创建索引,也可以单独用CREATEINDEXALTER TABLE来为表增加索引。

1ALTER TABLE

ALTER TABLE用来创建普通索引、UNIQUE索引或PRIMARYKEY索引。

 

ALTER TABLE table_name ADD INDEX index_name(column_list)

ALTER TABLE table_name ADD UNIQUE(column_list)

ALTER TABLE table_name ADD PRIMARY KEY(column_list)

 

其中table_name是要增加索引的表名,column_list指出对哪些列进行索引,多列时各列之间用逗号分隔。索引名index_name可选,缺省时,MySQL将根据第一个索引列赋一个名称。另外,ALTER TABLE允许在单个语句中更改多个表,因此可以在同时创建多个索引。

2CREATE INDEX

CREATE INDEX可对表增加普通索引或UNIQUE索引。

 

CREATE INDEX index_name ON table_name(column_list)

CREATE UNIQUE INDEX index_name ON table_name(column_list)

 

table_nameindex_namecolumn_list具有与ALTERTABLE语句中相同的含义,索引名不可选。另外,不能用CREATE INDEX语句创建PRIMARYKEY索引。

3.索引类型

在创建索引时,可以规定索引能否包含重复值。如果不包含,则索引应该创建为PRIMARYKEYUNIQUE索引。对于单列惟一性索引,这保证单列不包含重复的值。对于多列惟一性索引,保证多个值的组合不重复。

PRIMARY KEY索引和UNIQUE索引非常类似。事实上,PRIMARYKEY索引仅是一个具有名称PRIMARYUNIQUE索引。这表示一个表只能包含一个PRIMARYKEY,因为一个表中不可能具有两个同名的索引。

下面的SQL语句对students表在sid上添加PRIMARYKEY索引。

 

ALTER TABLE students ADD PRIMARY KEY(sid)

 

13.4.3  删除索引

可利用ALTER TABLEDROPINDEX语句来删除索引。类似于CREATEINDEX语句,DROP INDEX可以在ALTERTABLE内部作为一条语句处理,语法如下。

 

DROP INDEX index_name ONtalbe_name

ALTER TABLE table_name DROP INDEXindex_name

ALTER TABLE table_name DROP PRIMARYKEY

 

其中,前两条语句是等价的,删除掉table_name中的索引index_name

3条语句只在删除PRIMARYKEY索引时使用,因为一个表只可能有一个PRIMARY KEY索引,因此不需要指定索引名。如果没有创建PRIMARY KEY索引,但表具有一个或多个UNIQUE索引,则MySQL将删除第一个UNIQUE索引。

如果从表中删除了某列,则索引会受到影响。对于多列组合的索引,如果删除其中的某列,则该列也会从索引中删除。如果删除组成索引的所有列,则整个索引将被删除。

13.5.1  使用INSERT增加记录

向表中增加新记录有以下几种方法。

*    通过INSERT语句将记录插入表中;

*    通过从某个文件读取数据来增加记录,可以利用LOAD DATAmysqlimport实用程序装入数据,也可以预先写成多个INSERT语句的形式。

本节将首先介绍使用INSERT语句。使用INSERT语句,需要指定接收数据的表,以及所要增加的数据,具有以下几种形式。

1.给出所有列的值

语法如下。

 

INSERT INTO table_nameVALUES(value1,value2,...)

 

例如:

#students表中添加两个学生记录张三

mysql> INSERT INTO studentsVALUES

   -> (1,'张三','',21);

#courses表中添加三个课程记录C语言

mysql> INSERT INTO coursesVALUES

   -> (1,'C语言',3,'李老师');

#stu_cou表中添加张三和李四的成绩信息

mysql> INSERT INTO stu_couVALUES

   -> ( 1,2,90 );

 

其中,VALUES必须包含表中每列的值,并且按表中列的存放次序依次给出。在MySQL 中,需要用单引号或双引号将串和日期值括起来。如果某一列为AUTO_INCREMENT,则可赋值为NULL

另外,利用单个的INSERT语句可以一次将几行插入一个表中。

 

INSERT INTO table_name VALIUES(...),(...),...

 

如下所示:

 

mysql> INSERT INTO studentsVALUES

   -> (1,'张三','',21),

   -> (2,'李四','',22);

 

2.给出要赋值的列,然后再给出值

语法如下。

 

INSERT INTOtable_name(column_name1,column_name2,...) VALUES(value1,value1,...)

 

这种方式适用于只给表中的某些列赋值,例如:

 

mysql> INSERT INTOcourses(cid,cname,ccredit) VALUES

   -> (1,'C语言',3);

 

同样,也可以一次添加多个记录:

 

mysql> INSERT INTOcourses(cid,cname,ccredit) VALUES

   -> (1,'C语言',3);

   -> (2,'数据结构',2);

 

对于未指定的列,所添加的指为默认值,或NULL

3.用col_name =value 的形式给出列和值

语法如下。

 

INSERT INTO table_name SETcolumn_name1=value1,column_name2=value2,...

 

例如:

 

mysql> INSERT INTO coursesSET

   -> (cid=1,,cname='C语言',ccredit=3);

使用这种形式的INSERT不能一次插入多行数据。

13.5.2  使用LOADDATA批量增加记录

从文件读取数据可以将大量记录装载到表中。LOAD DATA语句可以批量装载数据,它从一个文件中读取数据。

 

mysql> LOAD DATA LOCALINFILE "students_data.txt" INTO TABLE students;

 

该语句读取客户机当前目录中数据文件students_data.txt的内容,并将其发送到服务器并装入students表。缺省时,LOADDATA语句假定列值由tab键分隔,而行以换行符结束,并假定各个值是按列在表中的存放次序给出的。

13.5.3  使用mysqlimport批量增加记录

LOADDATA外,还可以使用mysqlimport实用程序直接从文件读取批量数据。实际上,mysqlimport实用程序相当于LOADDATA 的命令行的一个接口。

从外壳程序调用mysqlimport

 

% mysqlimport --localstudents_coureses student.txt

 

Mysqlimport自动生成一个LOADDATA语句,此语句把student.txt文件中的数据装入student表。Mysqlimport根据数据文件名导出表名(将文件名第一个圆点前的所有字符作为表名)。例如,student.txt将被装入student表,而president.txt将被装入president表。如果有多个需要装入单个表的文件,应仔细地确定文件名,使表名一致,如table.1.txttable.2.txt等,否则mysqlimport将不能使用正确的表名。

对于table1.txttable2.txt这样的文件名,mysqlimport将会认为相应的表名为table1table2

13.5.4  修改记录(UPDATE

为了修改现有记录,可利用UPDATE语句,格式如下。

 

UPDATE table_name

SETcolumn_name1=new_value1,column_name2=new_value2,...

WHERE Condition

 

WHERE子句是可选的,指定所要修改的记录。如果不指定的话,表中的每个记录都被更新。下面的查询将每个学生的性别都更改为“F”。

 

UPDATE students SET sgender='F'

 

将每个学生的年龄都加1

 

UPDATE students SET sage=sage+1

 

本节,不详细介绍WHERE字句,在下一节“查询数据”中将详细介绍。

13.5.5  删除记录(DELETE

DELETE语句用于删除现有记录,格式如下。

DELETE FROM table_name

WHERE Condition

 

UPDATE一样,WHERE子句是可选的,指定所要删除的记录。如果不指定,表中的每个记录都被删除。下面的查询将删除性别为“F”的学生。

 

DELETE FROM students WHEREsgender='f'

 

下面的查询将删除性别为“F”并且年龄大于21的学生。

 

DELETE FROM students WHERE sgender='f' ANDsage>21

 

不带WHEREDELETE查询将删除所有的学生。

 

DELETE FROM students

 

 

MySQL优化之COUNT(*)效率

刚给一个朋友解决他写的Discuz!插件的问题,说到MySQL的COUNT(*)的效率,发现越说越说不清楚,干脆写下来,分享给大家。

COUNT(*)与COUNT(COL)
网上搜索了下,发现各种说法都有:
比如认为COUNT(COL)比COUNT(*)快的;
认为COUNT(*)比COUNT(COL)快的;
还有朋友很搞笑的说到这个其实是看人品的。

不加WHERE限制条件的情况下,COUNT(*)与COUNT(COL)基本可以认为是等价的;
但是在有WHERE限制条件的情况下,COUNT(*)会比COUNT(COL)快非常多;

具体的数据参考如下:

mysql> SELECT COUNT(*) FROM cdb_posts where fid =604;
+————+
| COUNT(fid) |
+————+
| 79000 |
+————+
1 row in set (0.03 sec)

mysql> SELECT COUNT(tid) FROM cdb_posts where fid= 604;
+————+
| COUNT(tid) |
+————+
| 79000 |
+————+
1 row in set (0.33 sec)

mysql> SELECT COUNT(pid) FROM cdb_posts where fid= 604;
+————+
| COUNT(pid) |
+————+
| 79000 |
+————+
1 row in set (0.33 sec)

COUNT(*)通常是对主键进行索引扫描,而COUNT(COL)就不一定了,另外前者是统计表中的所有符合的纪录总数,而后者是计算表中所有符合的COL的纪录数。还有有区别的。

COUNT时的WHERE
这点以前就写过,详细请看《Mysql中count(*),DISTINCT的使用方法和效率研究》:http://www.ccvita.com/156.html

简单说下,就是COUNT的时候,如果没有WHERE限制的话,MySQL直接返回保存有总的行数
而在有WHERE限制的情况下,总是需要对MySQL进行全表遍历。

优化总结
1.任何情况下SELECT COUNT(*) FROM tablename是最优选择;
2.尽量减少SELECT COUNT(*) FROM tablename WHERE COL = ‘value’这种查询;
3.杜绝SELECT COUNT(COL) FROM tablename WHERE COL2 = ‘value’ 的出现。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值