SQL 性能优化大全
- 格式:ppt
- 大小:1.10 MB
- 文档页数:47
五种提高 SQL 性能的方法有时,为了让应用程序运行得更快,所做的全部工作就是在这里或那里做一些很小调整。
啊,但关键在于确定如何进行调整!迟早您会遇到这种情况:应用程序中的 SQL 查询不能按照您想要的方式进行响应。
它要么不返回数据,要么耗费的时间长得出奇。
如果它降低了报告或您的企业应用程序的速度,用户必须等待的时间过长,他们就会很不满意。
就像您的父母不想听您解释为什么在深更半夜才回来一样,用户也不会听你解释为什么查询耗费这么长时间。
(“对不起,妈妈,我使用了太多的 LEFT JOIN。
”)用户希望应用程序响应迅速,他们的报告能够在瞬间之内返回分析数据。
就我自己而言,如果在 Web 上冲浪时某个页面要耗费十多秒才能加载(好吧,五秒更实际一些),我也会很不耐烦。
为了解决这些问题,重要的是找到问题的根源。
那么,从哪里开始呢?根本原因通常在于数据库设计和访问它的查询。
在本月的专栏中,我将讲述四项技术,这些技术可用于提高基于 SQL Server? 的应用程序的性能或改善其可伸缩性。
我将仔细说明 LEFT JOIN、CROSS JOIN 的使用以及 IDENTITY 值的检索。
请记住,根本没有神奇的解决方案。
调整您的数据库及其查询需要占用时间、进行分析,还需要大量的测试。
这些技术都已被证明行之有效,但对您的应用程序而言,可能其中一些技术比另一些技术更适用。
从 INSERT 返回 IDENTITY我决定从遇到许多问题的内容入手:如何在执行 SQL INSERT 后检索 IDENTITY 值。
通常,问题不在于如何编写检索值的查询,而在于在哪里以及何时进行检索。
在 SQL Server 中,下面的语句可用于检索由最新在活动数据库连接上运行的 SQL 语句所创建的 IDENTITY 值:SELECT @@IDENTITY这个 SQL 语句并不复杂,但需要记住的一点是:如果这个最新的 SQL 语句不是 INSERT,或者您针对非 INSERT SQL 的其他连接运行了此 SQL,则不会获得期望的值。
SQL Server数据库优化方案汇总50种方法优化SQL Server1、没有索引或者没有用到索引(这是查询慢最常见的问题,是程序设计的缺陷)2、I/O吞吐量小,形成了瓶颈效应。
3、没有创建计算列导致查询不优化。
4、内存不足5、网络速度慢6、查询出的数据量过大(可以采用多次查询,其他的方法降低数据量)7、锁或者死锁(这也是查询慢最常见的问题,是程序设计的缺陷)8、sp_lock,sp_who,活动的用户查看,原因是读写竞争资源。
9、返回了不必要的行和列10、查询语句不好,没有优化可以通过如下方法来优化查询 :1、把数据、日志、索引放到不同的I/O设备上,增加读取速度,以前可以将Tempdb应放在RAID0上,SQL2000不在支持。
数据量(尺寸)越大,提高I/O越重要.2、纵向、横向分割表,减少表的尺寸(sp_spaceuse)3、升级硬件4、根据查询条件,建立索引,优化索引、优化访问方式,限制结果集的数据量。
注意填充因子要适当(最好是使用默认值0)。
索引应该尽量小,使用字节数小的列建索引好(参照索引的创建),不要对有限的几个值的字段建单一索引如性别字段5、提高网速;6、扩大服务器的内存,Windows 2000和SQL server 2000能支持4-8G的内存。
配置虚拟内存:虚拟内存大小应基于计算机上并发运行的服务进行配置。
运行 Microsoft SQL Server? 2000 时,可考虑将虚拟内存大小设置为计算机中安装的物理内存的 1.5 倍。
如果另外安装了全文检索功能,并打算运行 Microsoft 搜索服务以便执行全文索引和查询,可考虑:将虚拟内存大小配置为至少是计算机中安装的物理内存的 3 倍。
将 SQL Server max server memory 服务器配置选项配置为物理内存的 1.5 倍(虚拟内存大小设置的一半)。
7、增加服务器 CPU个数;但是必须明白并行处理串行处理更需要资源例如内存。
编程技巧:优化SQL查询性能的7个方法1. 索引的正确使用在SQL查询中,索引是提升性能的关键。
正确创建和使用索引可以大大减少查询时间。
了解每个表中的数据字段,根据查询需求创建适当的索引是至关重要的。
避免在频繁进行查询和更新操作的字段上创建过多索引,因为索引也需要维护。
2. 避免全表扫描使用合适的WHERE子句和条件来缩小查询范围,避免不必要的全表扫描。
根据业务需求,添加合适的筛选条件可以有效地减少扫描行数,提高性能。
3. 慎用通配符通配符操作符(如'%')在SQL查询中可能会导致性能问题。
这是因为使用通配符会导致数据库执行全表扫描或者非常庞大的索引扫描操作。
尽量避免在模糊搜索中滥用通配符,可以考虑使用前缀搜索或者其他方式替代。
4. 避免使用子查询子查询可以给出所需结果,但是它们经常需要更多时间去执行。
如果可能,尝试将子查询转化为联接操作来提高性能。
5. 合理使用连接在多表查询中,关联条件和连接顺序对性能有重要影响。
使用INNER JOIN、LEFT JOIN等不同的连接方式时,请确保正确设置关联条件,并且有意识地按照操作逻辑和业务需求选择合适的连接方式。
6. 避免重复操作在一些复杂查询中,可能会出现重复操作的情况。
避免执行相同或非必要的子查询,可以考虑使用临时表或者存储过程来优化查询性能。
7. 对大数据量进行分页如果需要展示大量数据并进行分页显示,很可能会面临性能问题。
使用LIMIT 和OFFSET等关键字来限制返回结果的数量是提高性能的有效方法。
此外,还可以考虑使用缓存技术来减少数据库访问。
通过以上7个方法,你可以优化SQL查询性能并提升系统效率。
请根据业务需求综合运用这些技巧,并根据具体场景进行调整和优化。
记住不同场景下可能需要区别对待,持续监测和评估系统性能是保持高效查询的关键。
SELECT语句的性能调优有时是一个非常耗时的任务,在我看来它遵循帕累托原则。
20%的努力很可能会给你带来80%的性能提升,而为了获得另外20%的性能提升你可能需要花费80%的时间。
除非你在金星工作,那里的每一天都等于地球上的243天,否则交付期限很有可能使你没有足够的时间来调优SQL查询。
根据我多年编写和运行SQL语句的经验,我开始开发一个检查列表,当我试图提高查询性能时供我参考。
在进行查询计划和阅读我使用的数据库文档之前,我会参考其中的内容,数据库文档有时会很复杂。
我的检查列表绝对说不上全面或科学,它更像是一个保守计算,但我可以说,遵循这些简单的步骤大部分时间我确实能得到性能提升。
检查列表如下。
检查索引在SQL语句的WHERE和JOIN部分中用到的所有字段上,都应该加上索引。
进行这个3分钟SQL性能测试。
不管你的成绩如何,一定要阅读那些带有信息的结果。
限制工作数据集的大小检查那些SELECT语句中用到的表,看看你是否可以应用WHERE子句进行过滤。
一个典型的例子是,当表中只有几千行记录时,一个查询能够很好地执行。
但随着应用程序的成长,查询慢了下来。
解决方案或许非常简单,限制查询来查看当前月的数据即可。
当你的查询语句带有子查询时,注意在子查询的内部语句上使用过滤,而不是在外部语句上。
只选择你需要的字段额外的字段通常会增加返回数据的纹理,从而导致更多的数据被返回到SQL客户端。
另外:•使用带有报告和分析功能的应用程序时,有时报告性能低是因为报告工具必须对收到的、带有详细形式的数据做聚合操作。
•偶尔查询也可能运行地足够快,但你的问题可能是一个网络相关的问题,因为大量的详细数据通过网络发送到报告服务器。
•当使用一个面向列的DBMS时,只有你选择的列会从磁盘读取。
在你的查询中包含的列越少,IO开销就越小。
移除不必要的表移除不必要的表的原因,和移除查询语句中不需要的字段的原因一致。
复杂sql优化的方法及思路复杂SQL优化的方法及思路SQL是关系型数据库管理系统中最常用的语言,但是在处理复杂查询时,SQL语句往往会变得非常复杂和冗长,导致查询速度缓慢。
为了提高查询效率,我们需要进行SQL优化。
以下是一些复杂SQL优化的方法及思路。
1.索引优化索引是提高数据库查询效率的重要手段之一。
在设计表结构时,应该根据实际情况建立适当的索引。
在查询语句中使用索引可以大大减少数据扫描量,从而提高查询效率。
2.避免使用子查询子查询虽然方便了我们编写复杂的SQL语句,但是在执行过程中会增加额外的开销。
因此,在编写复杂SQL语句时应尽量避免使用子查询。
3.减少JOIN操作JOIN操作也是影响查询效率的一个重要因素。
在设计表结构时应尽量避免使用JOIN操作或者减少JOIN操作次数。
4.合理使用聚合函数聚合函数(如SUM、AVG等)可以对数据进行统计分析,在处理大量数据时非常有用。
但是,在使用聚合函数时要注意不要频繁调用,否则会降低查询效率。
5.使用EXPLAIN命令分析查询语句EXPLAIN命令可以分析查询语句的执行计划,从而找出影响查询效率的因素。
通过分析EXPLAIN结果,可以对SQL语句进行优化。
6.避免使用SELECT *SELECT *会查询所有列,包括不需要的列,增加了数据扫描量,降低了查询效率。
在编写SQL语句时应尽量避免使用SELECT *。
7.合理使用缓存缓存可以减少数据库访问次数,提高查询效率。
在设计系统架构时应考虑缓存的使用。
8.优化表结构表结构的设计也是影响SQL查询效率的一个重要因素。
在设计表结构时应尽量避免冗余数据和过多的列。
以上是一些复杂SQL优化的方法及思路。
通过合理运用这些方法和思路,可以大大提高SQL查询效率,为数据库管理系统提供更好的性能和稳定性。
52条SQL语句性能优化策略,建议收藏本文会提到52 条SQL 语句性能优化策略。
1、对查询进行优化,应尽量避免全表扫描,首先应考虑在WHERE 及ORDER BY 涉及的列上建立索引。
2、应尽量避免在WHERE 子句中对字段进行NULL 值判断,创建表时NULL 是默认值,但大多数时候应该使用NOT NULL,或者使用一个特殊的值,如0,-1 作为默认值。
3、应尽量避免在WHERE 子句中使用!= 或<> 操作符。
MySQL 只有对以下操作符才使用索引:<,<=,=,>,>=,BETWEEN,IN,以及某些时候的LIKE。
4、应尽量避免在WHERE 子句中使用OR 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描,可以使用UNION 合并查询:select id from t where num=10 union all select id from t where num=20。
5、IN 和NOT IN 也要慎用,否则会导致全表扫描。
对于连续的数值,能用BETWEEN 就不要用IN:select id from t where num between 1 and 3。
6、下面的查询也将导致全表扫描:select id from t where name like‘%abc%’或者select id from t where name like‘%abc’若要提高效率,可以考虑全文检索。
而select id from t where name like‘abc%’才用到索引。
7、如果在WHERE 子句中使用参数,也会导致全表扫描。
8、应尽量避免在WHERE 子句中对字段进行表达式操作,应尽量避免在WHERE 子句中对字段进行函数操作。
9、很多时候用EXISTS 代替IN 是一个好的选择:select num from a where num in(selectnum from b)。
SQLServer的性能优化技巧随着IT技术的快速发展,数据库作为系统的核心组成部分,在各行各业的信息化建设中扮演着至关重要的角色。
作为一种重要的关系型数据库管理系统,SQLServer的性能往往直接影响着系统的运行效率和稳定性。
本文将介绍一些SQLServer的性能优化技巧,供读者参考。
一、使用恰当的数据库引擎SQLServer支持多种不同的数据库引擎,如MyISAM、InnoDB 等。
每一种引擎都有自己的特点和适用范围。
在进行数据建模时,要根据应用场景的需要选择最适合的引擎。
一般来说,InnoDB引擎支持事务、外键以及行级锁等特性,适合于对数据完整性要求比较高的系统;而MyISAM引擎则适合于读写比例较低的系统。
选择恰当的数据库引擎可以提高SQLServer的性能。
二、适当调整缓存参数SQLServer有多种缓存,包括查询缓存、表缓存、索引缓存等等。
合理的调整这些缓存参数,可以提高系统的性能。
其中,查询缓存可以减少重复查询的时间,提高响应速度;表缓存可以满足多次使用同样的查询所需的表缓存需求;索引缓存则可以提供快速的查询性能。
在具体的应用中,需要根据场景和需求选择不同的缓存参数,以达到最优的性能表现。
三、使用合适的索引策略索引是SQLServer中非常重要的性能优化策略。
适当的索引可以提高系统的查询速度和效率。
但是,在使用索引时,需要考虑到索引对插入、修改、删除等操作的影响。
如果索引过多,会导致这些操作的性能下降。
因此,必须在保证查询速度和效率的基础上,综合考虑索引对系统整体运行的影响。
四、合理使用分区技术对于大型的数据库系统,分区技术可以将数据划分为多个小段,降低系统的压力和负载,提高系统的处理速度。
在SQLServer中,可以根据表大小或者数据时间等因素进行分区。
通过使用分区技术,可以实现数据存储和查询的快速响应,同时有效地缓解系统的负载压力。
五、使用恰当的查询语句查询语句在SQLServer中起着至关重要的作用。
Oracle SQL性能优化的40条军规1. SQL语句执行步骤语法分析> 语义分析> 视图转换>表达式转换> 选择优化器>选择连接方式>选择连接顺序>选择数据的搜索路径>运行“执行计划”2. 选用适合的Oracle优化器RULE(基于规则)COST(基于成本)CHOOSE(选择性)3. 访问Table的方式全表扫描全表扫描就是顺序地访问表中每条记录,ORACLE采用一次读入多个数据块(database block)的方式优化全表扫描。
通过ROWID访问表ROWID包含了表中记录的物理位置信息,ORACLE采用索引实现了数据和存放数据的物理位置(ROWID)之间的联系,通常索引提供了快速访问ROWID的方法,因此那些基于索引列的查询就可以得到性能上的提高。
4. 共享SQL 语句Oracle提供对执行过的SQL语句进行高速缓冲的机制。
被解析过并且确定了执行路径的SQL语句存放在SGA的共享池中。
Oracle执行一个SQL语句之前每次先从SGA共享池中查找是否有缓冲的SQL语句,如果有则直接执行该SQL语句。
可以通过适当调整SGA共享池大小来达到提高Oracle执行性能的目的。
5. 选择最有效率的表名顺序ORACLE的解析器按照从右到左的顺序处理FROM子句中的表名,因此FROM子句中写在最后的表(基础表driving table)将被最先处理。
当ORACLE处理多个表时,会运用排序及合并的方式连接它们。
首先,扫描第一个表(FROM 子句中最后的那个表)并对记录进行派序,然后扫描第二个表(FROM子句中最后第二个表),最后将所有从第二个表中检索出的记录与第一个表中合适记录进行合并。
只在基于规则的优化器中有效。
举例:表TAB1 16,384 条记录表TAB2 1 条记录/*选择TAB2作为基础表(最好的方法)*/select count(*) from tab1,tab2 执行时间0.96秒/*选择TAB2作为基础表(不佳的方法)*/select count(*) from tab2,tab1 执行时间26.09秒如果有3个以上的表连接查询, 那就需要选择交叉表(intersection table)作为基础表, 交叉表是指那个被其他表所引用的表。
常⽤SQLServer进⾏性能优化语句1、锁监控查看锁住的表:select request_session_id spid,OBJECT_NAME(resource_associated_entity_id) tableNamefrom sys.dm_tran_locks where resource_type='OBJECT'查看哪个会话引起阻塞并且它们在运⾏什么:SELECT DTL.[request_session_id]AS[session_id] ,DB_NAME(DTL.[resource_database_id]) AS[Database] ,DTL.resource_type ,CASE WHEN DTL.resource_type IN ( 'DATABASE', 'FILE', 'METADATA' )THEN DTL.resource_typeWHEN DTL.resource_type ='OBJECT'THEN OBJECT_NAME(DTL.resource_associated_entity_id,DTL.[resource_database_id])WHEN DTL.resource_type IN ( 'KEY', 'PAGE', 'RID' )THEN ( SELECT OBJECT_NAME([object_id])FROM sys.partitionsWHERE sys.partitions.hobt_id = DTL.resource_associated_entity_id)ELSE'Unidentified'END AS[Parent Object] ,DTL.request_mode AS[Lock Type] ,DTL.request_status AS[Request Status] ,DER.[blocking_session_id] ,DES.[login_name] ,CASE DTL.request_lifetimeWHEN0THEN DEST_R.TEXTELSE DEST_C.TEXTEND AS[Statement]FROM sys.dm_tran_locks DTLLEFT JOIN sys.[dm_exec_requests] DER ON DTL.[request_session_id]= DER.[session_id]INNER JOIN sys.dm_exec_sessions DES ON DTL.request_session_id = DES.[session_id]INNER JOIN sys.dm_exec_connections DEC ON DTL.[request_session_id]=DEC.[most_recent_session_id] OUTER APPLY sys.dm_exec_sql_text(DEC.[most_recent_sql_handle]) AS DEST_COUTER APPLY sys.dm_exec_sql_text(DER.sql_handle) AS DEST_RWHERE DTL.[resource_database_id]=DB_ID()AND DTL.[resource_type]NOT IN ( 'DATABASE', 'METADATA' )ORDER BY DTL.[request_session_id];查看因为单条UPDATE语句锁住的⽤户表:SELECT[resource_type] ,DB_NAME([resource_database_id]) AS[Database Name] ,CASE WHEN DTL.resource_type IN ( 'DATABASE', 'FILE', 'METADATA' )THEN DTL.resource_typeWHEN DTL.resource_type ='OBJECT'THEN OBJECT_NAME(DTL.resource_associated_entity_id,DTL.[resource_database_id])WHEN DTL.resource_type IN ( 'KEY', 'PAGE', 'RID' )THEN ( SELECT OBJECT_NAME([object_id])FROM sys.partitionsWHERE sys.partitions.hobt_id = DTL.resource_associated_entity_id)ELSE'Unidentified'END AS requested_object_name ,[request_mode] ,[resource_description]FROM sys.dm_tran_locks DTLWHERE DTL.[resource_type]<>'DATABASE';单库中的锁定和阻塞:SELECT DTL.[resource_type]AS[resource type] ,CASE WHEN DTL.[resource_type]IN ( 'DATABASE', 'FILE', 'METADATA' )THEN DTL.[resource_type]WHEN DTL.[resource_type]='OBJECT'THEN OBJECT_NAME(DTL.resource_associated_entity_id)WHEN DTL.[resource_type]IN ( 'KEY', 'PAGE', 'RID' )THEN ( SELECT OBJECT_NAME([object_id])FROM sys.partitionsWHERE sys.partitions.[hobt_id]= DTL.[resource_associated_entity_id])ELSE'Unidentified'END AS[Parent Object] ,DTL.[request_mode]AS[Lock Type] ,DTL.[request_status]AS[Request Status] ,DOWT.[wait_duration_ms]AS[wait duration ms] ,DOWT.[wait_type]AS[wait type] ,DOWT.[session_id]AS[blocked session id] ,DES_blocked.[login_name]AS[blocked_user] ,SUBSTRING(dest_blocked.text, der.statement_start_offset /2,( CASE WHEN der.statement_end_offset =-1THEN DATALENGTH(dest_blocked.text)ELSE der.statement_end_offsetEND- der.statement_start_offset ) /2) AS[blocked_command] ,DOWT.[blocking_session_id]AS[blocking session id] ,DES_blocking.[login_name]AS[blocking user] ,DEST_blocking.[text]AS[blocking command] ,DOWT.resource_description AS[blocking resource detail]FROM sys.dm_tran_locks DTLINNER JOIN sys.dm_os_waiting_tasks DOWT ON DTL.lock_owner_address = DOWT.resource_addressINNER JOIN sys.[dm_exec_requests] DER ON DOWT.[session_id]= DER.[session_id]INNER JOIN sys.dm_exec_sessions DES_blocked ON DOWT.[session_id]= DES_Blocked.[session_id]INNER JOIN sys.dm_exec_sessions DES_blocking ON DOWT.[blocking_session_id]= DES_Blocking.[session_id] INNER JOIN sys.dm_exec_connections DEC ON DTL.[request_session_id]=DEC.[most_recent_session_id]CROSS APPLY sys.dm_exec_sql_text(DEC.[most_recent_sql_handle]) AS DEST_BlockingCROSS APPLY sys.dm_exec_sql_text(DER.sql_handle) AS DEST_BlockedWHERE DTL.[resource_database_id]=DB_ID()识别在⾏级的锁定和阻塞:SELECT'['+DB_NAME(ddios.[database_id]) +'].['+ su.[name]+'].['+ o.[name]+']'AS[statement] ,i.[name]AS'index_name' ,ddios.[partition_number] ,ddios.[row_lock_count] ,ddios.[row_lock_wait_count] ,CAST (100.0* ddios.[row_lock_wait_count]/ ( ddios.[row_lock_count] ) AS DECIMAL(5,2)) AS[%_times_blocked] ,ddios.[row_lock_wait_in_ms] ,CAST (1.0* ddios.[row_lock_wait_in_ms]/ ddios.[row_lock_wait_count]AS DECIMAL(15,2)) AS[avg_row_lock_wait_in_ms]FROM sys.dm_db_index_operational_stats(DB_ID(), NULL, NULL, NULL) ddiosINNER JOIN sys.indexes i ON ddios.[object_id]= i.[object_id]AND i.[index_id]= ddios.[index_id]INNER JOIN sys.objects o ON ddios.[object_id]= o.[object_id]INNER JOIN sys.sysusers su ON o.[schema_id]= su.[UID]WHERE ddios.row_lock_wait_count >0AND OBJECTPROPERTY(ddios.[object_id], 'IsUserTable') =1AND i.[index_id]>0ORDER BY ddios.[row_lock_wait_count]DESC ,su.[name] ,o.[name] ,i.[name]识别闩锁等待:SELECT'['+DB_NAME() +'].['+ OBJECT_SCHEMA_NAME(ddios.[object_id])+'].['+OBJECT_NAME(ddios.[object_id]) +']'AS[object_name] ,i.[name]AS index_name ,ddios.page_io_latch_wait_count ,ddios.page_io_latch_wait_in_ms ,( ddios.page_io_latch_wait_in_ms / ddios.page_io_latch_wait_count ) AS avg_page_io_latch_wait_in_ms FROM sys.dm_db_index_operational_stats(DB_ID(), NULL, NULL, NULL) ddiosINNER JOIN sys.indexes i ON ddios.[object_id]= i.[object_id]AND i.index_id = ddios.index_idWHERE ddios.page_io_latch_wait_count >0AND OBJECTPROPERTY(i.object_id, 'IsUserTable') =1ORDER BY ddios.page_io_latch_wait_count DESC ,avg_page_io_latch_wait_in_ms DESC识别锁升级:SELECT OBJECT_NAME(ddios.[object_id], ddios.database_id) AS[object_name] , AS index_name ,ddios.index_id ,ddios.partition_number ,ddios.index_lock_promotion_attempt_count ,ddios.index_lock_promotion_count ,( ddios.index_lock_promotion_attempt_count/ ddios.index_lock_promotion_count ) AS percent_successFROM sys.dm_db_index_operational_stats(DB_ID(), NULL, NULL, NULL) ddiosINNER JOIN sys.indexes i ON ddios.object_id= i.object_idAND ddios.index_id = i.index_idWHERE ddios.index_lock_promotion_count >0ORDER BY index_lock_promotion_count DESC;与锁争⽤有关的索引:SELECT OBJECT_NAME(ddios.object_id, ddios.database_id) AS object_name , AS index_name ,ddios.index_id ,ddios.partition_number ,ddios.page_lock_wait_count ,ddios.page_lock_wait_in_ms ,CASE WHEN DDMID.database_id IS NULL THEN'N'ELSE'Y'END AS missing_index_identifiedFROM sys.dm_db_index_operational_stats(DB_ID(), NULL, NULL, NULL) ddiosINNER JOIN sys.indexes i ON ddios.object_id= i.object_idAND ddios.index_id = i.index_idLEFT OUTER JOIN ( SELECT DISTINCTdatabase_id ,object_idFROM sys.dm_db_missing_index_details) AS DDMID ON DDMID.database_id = ddios.database_idAND DDMID.object_id= ddios.object_idWHERE ddios.page_lock_wait_in_ms >0ORDER BY ddios.page_lock_wait_count DESC;2、索引监控未被使⽤的索引:SELECT OBJECT_NAME(i.[object_id]) AS[Table Name] ,FROM sys.indexes AS iINNER JOIN sys.objects AS o ON i.[object_id]= o.[object_id]WHERE i.index_id NOT IN ( SELECT ddius.index_idFROM sys.dm_db_index_usage_stats AS ddiusWHERE ddius.[object_id]= i.[object_id]AND i.index_id = ddius.index_idAND database_id =DB_ID() )AND o.[type]='U'ORDER BY OBJECT_NAME(i.[object_id]) ASC;需要维护但是未被⽤过的索引:SUM(SP.rows) AS[total_rows]FROM sys.dm_db_index_usage_stats ddiusINNER JOIN sys.indexes i ON ddius.[object_id]= i.[object_id]AND i.[index_id]= ddius.[index_id]INNER JOIN sys.partitions SP ON ddius.[object_id]= SP.[object_id]AND SP.[index_id]= ddius.[index_id]INNER JOIN sys.objects o ON ddius.[object_id]= o.[object_id]INNER JOIN sys.sysusers su ON o.[schema_id]= su.[UID]WHERE ddius.[database_id]=DB_ID() -- current database onlyAND OBJECTPROPERTY(ddius.[object_id], 'IsUserTable') =1AND ddius.[index_id]>0GROUP BY su.[name] ,o.[name] ,i.[name] ,ddius.[user_seeks]+ ddius.[user_scans]+ ddius.[user_lookups] ,ddius.[user_updates]HAVING ddius.[user_seeks]+ ddius.[user_scans]+ ddius.[user_lookups]=0ORDER BY ddius.[user_updates]DESC ,su.[name] ,o.[name] ,i.[name]可能不⾼效的⾮聚集索引 (writes > reads):SELECT OBJECT_NAME(ddius.[object_id]) AS[Table Name] , AS[Index Name] ,i.index_id ,user_updates AS[Total Writes] ,user_seeks + user_scans + user_lookups AS[Total Reads] ,user_updates - ( user_seeks + user_scans + user_lookups ) AS[Difference] FROM sys.dm_db_index_usage_stats AS ddius WITH ( NOLOCK )INNER JOIN sys.indexes AS i WITH ( NOLOCK ) ON ddius.[object_id]= i.[object_id]AND i.index_id = ddius.index_idWHERE OBJECTPROPERTY(ddius.[object_id], 'IsUserTable') =1AND ddius.database_id =DB_ID()AND user_updates > ( user_seeks + user_scans + user_lookups )AND i.index_id >1ORDER BY[Difference]DESC ,[Total Writes]DESC ,[Total Reads]ASC;没有⽤于⽤户查询的索引:ddios.[leaf_insert_count] ,ddios.[leaf_delete_count] ,ddios.[leaf_update_count] ,ddios.[nonleaf_insert_count] ,ddios.[nonleaf_delete_count] ,ddios.[nonleaf_update_count]FROM sys.dm_db_index_usage_stats ddiusINNER JOIN sys.indexes i ON ddius.[object_id]= i.[object_id]AND i.[index_id]= ddius.[index_id]INNER JOIN sys.partitions SP ON ddius.[object_id]= SP.[object_id]AND SP.[index_id]= ddius.[index_id]INNER JOIN sys.objects o ON ddius.[object_id]= o.[object_id]INNER JOIN sys.sysusers su ON o.[schema_id]= su.[UID]INNER JOIN sys.[dm_db_index_operational_stats](DB_ID(), NULL, NULL,NULL) AS ddios ON ddius.[index_id]= ddios.[index_id]AND ddius.[object_id]= ddios.[object_id]AND SP.[partition_number]= ddios.[partition_number]AND ddius.[database_id]= ddios.[database_id]WHERE OBJECTPROPERTY(ddius.[object_id], 'IsUserTable') =1AND ddius.[index_id]>0AND ddius.[user_seeks]+ ddius.[user_scans]+ ddius.[user_lookups]=0ORDER BY ddius.[user_updates]DESC ,su.[name] ,o.[name] ,i.[name]查找丢失索引:SELECT user_seeks * avg_total_user_cost * ( avg_user_impact *0.01 ) AS[index_advantage] ,st_user_seek ,dbmid.[statement]AS[Database.Schema.Table] ,dbmid.equality_columns ,dbmid.inequality_columns ,dbmid.included_columns ,dbmigs.unique_compiles ,er_seeks ,dbmigs.avg_total_user_cost ,dbmigs.avg_user_impactFROM sys.dm_db_missing_index_group_stats AS dbmigs WITH ( NOLOCK )INNER JOIN sys.dm_db_missing_index_groups AS dbmig WITH ( NOLOCK ) ON dbmigs.group_handle = dbmig.index_group_handle INNER JOIN sys.dm_db_missing_index_details AS dbmid WITH ( NOLOCK ) ON dbmig.index_handle = dbmid.index_handle WHERE dbmid.[database_id]=DB_ID()ORDER BY index_advantage DESC;索引上的碎⽚超过15%并且索引体积较⼤(超过500页)的索引:SELECT'['+DB_NAME() +'].['+ OBJECT_SCHEMA_NAME(ddips.[object_id],DB_ID()) +'].['+OBJECT_NAME(ddips.[object_id], DB_ID()) +']'AS[statement] ,i.[name]AS[index_name] ,ddips.[index_type_desc] ,ddips.[partition_number] ,ddips.[alloc_unit_type_desc] ,ddips.[index_depth] ,ddips.[index_level] ,CAST(ddips.[avg_fragmentation_in_percent]AS SMALLINT) AS[avg_frag_%] ,CAST(ddips.[avg_fragment_size_in_pages]AS SMALLINT) AS[avg_frag_size_in_pages] ,ddips.[fragment_count] ,ddips.[page_count]FROM sys.dm_db_index_physical_stats(DB_ID(), NULL, NULL, NULL, 'limited') ddipsINNER JOIN sys.[indexes] i ON ddips.[object_id]= i.[object_id]AND ddips.[index_id]= i.[index_id]WHERE ddips.[avg_fragmentation_in_percent]>15AND ddips.[page_count]>500ORDER BY ddips.[avg_fragmentation_in_percent] ,OBJECT_NAME(ddips.[object_id], DB_ID()) ,i.[name]缺失索引:SELECT migs.group_handle, mid.*FROM sys.dm_db_missing_index_group_stats AS migsINNER JOIN sys.dm_db_missing_index_groups AS migON (migs.group_handle = mig.index_group_handle)INNER JOIN sys.dm_db_missing_index_details AS midON (mig.index_handle = mid.index_handle)WHERE migs.group_handle =2⽆⽤索引:SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTEDSELECTDB_NAME() AS DatbaseName, SCHEMA_NAME(O.Schema_ID) AS SchemaName, OBJECT_NAME(I.object_id) AS TableName, AS IndexNameINTO #TempNeverUsedIndexesFROM sys.indexes I INNER JOIN sys.objects O ON I.object_id= O.object_id WHERE1=2EXEC sp_MSForEachDB 'USE [?]; INSERT INTO #TempNeverUsedIndexes SELECTDB_NAME() AS DatbaseName, SCHEMA_NAME(O.Schema_ID) AS SchemaName, OBJECT_NAME(I.object_id) AS TableName, AS IndexNameFROM sys.indexes I INNER JOIN sys.objects O ON I.object_id = O.object_id LEFT OUTER JOIN sys.dm_db_index_usage_stats S ON S.object_id = I.object_id AND I.index_id = S.index_idAND DATABASE_ID = DB_ID()WHERE OBJECTPROPERTY(O.object_id,''IsMsShipped'') = 0AND IS NOT NULLAND S.object_id IS NULL'SELECT*FROM #TempNeverUsedIndexesORDER BY DatbaseName, SchemaName, TableName, IndexNameDROP TABLE #TempNeverUsedIndexes经常被⼤量更新,但是却基本不适⽤的索引:SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTEDSELECTDB_NAME() AS DatabaseName, SCHEMA_NAME(o.Schema_ID) AS SchemaName, OBJECT_NAME(s.[object_id]) AS TableName, AS IndexName, er_updates, s.system_seeks + s.system_scans + s.system_lookupsAS[System usage]INTO #TempUnusedIndexesFROM sys.dm_db_index_usage_stats sINNER JOIN sys.indexes i ON s.[object_id]= i.[object_id]AND s.index_id = i.index_idINNER JOIN sys.objects o ON i.object_id= O.object_idWHERE1=2EXEC sp_MSForEachDB 'USE [?]; INSERT INTO #TempUnusedIndexes SELECT TOP 20DB_NAME() AS DatabaseName, SCHEMA_NAME(o.Schema_ID) AS SchemaName, OBJECT_NAME(s.[object_id]) AS TableName, AS IndexName, er_updates, s.system_seeks + s.system_scans + s.system_lookupsAS [System usage]FROM sys.dm_db_index_usage_stats sINNER JOIN sys.indexes i ON s.[object_id] = i.[object_id]AND s.index_id = i.index_idINNER JOIN sys.objects o ON i.object_id = O.object_idWHERE s.database_id = DB_ID()AND OBJECTPROPERTY(s.[object_id], ''IsMsShipped'') = 0AND er_seeks = 0AND er_scans = 0AND er_lookups = 0AND IS NOT NULLORDER BY er_updates DESC'SELECT TOP20*FROM #TempUnusedIndexes ORDER BY[user_updates]DESC DROP TABLE #TempUnusedIndexes3、数据库环境监控查询当前数据库的配置信息:Select configuration_id ConfigurationId,name Name,description Description,Cast(value as int) value,Cast(minimum as int) Minimum,Cast(maximum as int) Maximum,Cast(value_in_use as int) ValueInUse,is_dynamic IsDynamic,is_advanced IsAdvancedFrom sys.configurationsOrder By is_advanced, name检查SQL SERVER 当前已创建的线程数:select count(*) from sys.dm_os_workers查询当前连接到数据库的⽤户信息:Select s.login_name LoginName,s.host_name HostName,s.transaction_isolation_level TransactionIsolationLevel,Max(c.connect_time) LastConnectTime,Count(*) ConnectionCount,Sum(Cast(c.num_reads as BigInt)) TotalReads,Sum(Cast(c.num_writes as BigInt)) TotalWritesFrom sys.dm_exec_connections cJoin sys.dm_exec_sessions sOn c.most_recent_session_id = s.session_idGroup By s.login_name, s.host_name, s.transaction_isolation_level查询CPU和内存利⽤率:Select DateAdd(s, (timestamp- (osi.cpu_ticks /Convert(Float, (osi.cpu_ticks / osi.ms_ticks)))) /1000, GETDATE()) AS EventTime, Record.value('(./Record/SchedulerMonitorEvent/SystemHealth/SystemIdle)[1]', 'int') as SystemIdle,Record.value('(./Record/SchedulerMonitorEvent/SystemHealth/ProcessUtilization)[1]', 'int') as ProcessUtilization,Record.value('(./Record/SchedulerMonitorEvent/SystemHealth/MemoryUtilization)[1]', 'int') as MemoryUtilizationFrom (Select timestamp,convert(xml, record) As RecordFrom sys.dm_os_ring_buffersWhere ring_buffer_type = N'RING_BUFFER_SCHEDULER_MONITOR'And record Like'%<SystemHealth>%') xCross Join sys.dm_os_sys_info osiOrder By timestamp查看每个数据库缓存⼤⼩:SELECT COUNT(*) *8/1024AS'Cached Size (MB)' ,CASE database_idWHEN32767THEN'ResourceDb'ELSE DB_NAME(database_id)END AS'Database'FROM sys.dm_os_buffer_descriptorsGROUP BY DB_NAME(database_id) ,database_idORDER BY'Cached Size (MB)'DESC统计IO活动信息:SET STATISTICS IO ONselect top10*from TableSET STATISTICS IO OFF清除缓存SQL语句:CHECKPOINT;GODBCC FREEPROCCACHE ---清空执⾏计划缓存DBCC DROPCLEANBUFFERS; --清空数据缓存GO查看当前进程的信息:DBCC INPUTBUFFER(51)查看当前数据是否启⽤了快照隔离:DBCC USEROPTIONS;查看摸个数据库数据表中的数据页类型:--In_Row_Data: 分别为存储⾏内数据的--LOB_Data: 存储Lob对象,Lob对象⽤于存储存在数据库的⼆进制⽂件--当这个类型的列出现时,原有的列会存储⼀个24字节的指针,⽽将具体的⼆进制数据存在LOB页中--Row_Overflow_data:存储溢出数据的,使⽤Varchar,nvarchar等数据类型时,当⾏的⼤⼩不超过8060字节时,全部存在⾏内In-row data --当varchar中存储的数据过多使得整⾏超过8060字节时,会将额外的部分存于Row-overflow data页中,--如果update这列使得⾏⼤⼩减少到⼩于8060字节,则这⾏⼜会全部回到in-row data页--text,ntext和image类型来说,每⼀列只要不为null,即使占⽤很⼩的数据,也需要额外分配⼀个LOB页DBCC IND ( Lawyer, [dbo.tb_Contract], -1)4、sql执⾏次数和性能监控查询CPU最⾼的10条SQL:SELECT TOP10TEXT AS'SQL Statement',last_execution_time AS'Last Execution Time',(total_logical_reads + total_physical_reads + total_logical_writes) / execution_count AS[Average IO],(total_worker_time / execution_count) /1000000.0AS[Average CPU Time (sec)],(total_elapsed_time / execution_count) /1000000.0AS[Average Elapsed Time (sec)],execution_count AS "Execution Count",qs.total_physical_reads,qs.total_logical_writes,qp.query_plan AS "Query Plan"FROM sys.dm_exec_query_stats qsCROSS APPLY sys.dm_exec_sql_text(qs.plan_handle) stCROSS APPLY sys.dm_exec_query_plan(qs.plan_handle) qpORDER BY total_elapsed_time / execution_count DESC找出执⾏频繁的语句的SQL语句:with aa as (SELECT--执⾏次数QS.execution_count,--查询语句SUBSTRING(ST.text,(QS.statement_start_offset/2)+1,((CASE QS.statement_end_offset WHEN-1THEN DATALENGTH(st.text)ELSE QS.statement_end_offset END- QS.statement_start_offset)/2) +1) AS statement_text,--执⾏⽂本ST.text,--执⾏计划st_elapsed_time,qs.min_elapsed_time,qs.max_elapsed_time,QS.total_worker_time,st_worker_time,QS.max_worker_time,QS.min_worker_timeFROMsys.dm_exec_query_stats QS--关键字CROSS APPLYsys.dm_exec_sql_text(QS.sql_handle) STWHEREst_execution_time >'2016-02-14 00:00:00'and execution_count >500-- AND ST.text LIKE '%%'--ORDER BY--QS.execution_count DESC)select text,max(execution_count) execution_count --,last_elapsed_time,min_elapsed_time,max_elapsed_time from aawhere[text]not like'%sp_MSupd_%'and[text]not like'%sp_MSins_%'and[text]not like'%sp_MSdel_%' group by textorder by2desc查找逻辑读取最⾼的查询(存储过程):SELECT TOP ( 25 ) AS[SP Name] ,Deps.total_logical_reads AS[TotalLogicalReads] ,deps.total_logical_reads / deps.execution_count AS[AvgLogicalReads] ,deps.execution_count ,ISNULL(deps.execution_count /DATEDIFF(Second, deps.cached_time,GETDATE()), 0) AS[Calls/Second] ,deps.total_elapsed_time ,deps.total_elapsed_time / deps.execution_count AS[avg_elapsed_time] ,deps.cached_timeFROM sys.procedures AS pINNER JOIN sys.dm_exec_procedure_stats AS deps ON p.[Object_id]= deps.[Object_id]WHERE deps.Database_id =DB_ID()ORDER BY deps.total_logical_reads DESC;查看某个SQL的执⾏计划:SET STATISTICS PROFILE ONSELECT*FROM DemoSET STATISTICS PROFILE OFF查询某个SQL的执⾏时间:SET STATISTICS Time ONSELECT*FROM DemoSET STATISTICS TIME OFF查询某个SQL的IO信息:SET STATISTICS IO ONSELECT*FROM DemoSET STATISTICS IO OFF5、开源监控脚本,监控数据库锁定情况USE masterGOIF NOT EXISTS (SELECT*FROM INFORMATION_SCHEMA.ROUTINES WHERE ROUTINE_NAME ='sp_WhoIsActive') EXEC ('CREATE PROC dbo.sp_WhoIsActive AS SELECT ''stub version, to be replaced''')GO/*********************************************************************************************Who Is Active? v11.17 (2016-10-18)(C) 2007-2016, Adam MachanicFeedback:mailto:*******************Updates: License:Who is Active? is free to download and use for personal, educational, and internalcorporate purposes, provided that this header is preserved. Redistribution or saleof Who is Active?, in whole or in part, is prohibited without the author's expresswritten consent.*********************************************************************************************/ALTER PROC dbo.sp_WhoIsActive(--~--Filters--Both inclusive and exclusive--Set either filter to '' to disable--Valid filter types are: session, program, database, login, and host--Session is a session ID, and either 0 or '' can be used to indicate "all" sessions--All other filter types support % or _ as wildcards@filter sysname ='',@filter_type VARCHAR(10) ='session',@not_filter sysname ='',@not_filter_type VARCHAR(10) ='session',--Retrieve data about the calling session?@show_own_spid BIT=0,--Retrieve data about system sessions?@show_system_spids BIT=0,--Controls how sleeping SPIDs are handled, based on the idea of levels of interest--0 does not pull any sleeping SPIDs--1 pulls only those sleeping SPIDs that also have an open transaction--2 pulls all sleeping SPIDs@show_sleeping_spids TINYINT=1,--If 1, gets the full stored procedure or running batch, when available--If 0, gets only the actual statement that is currently running in the batch or procedure@get_full_inner_text BIT=0,--Get associated query plans for running tasks, if available--If @get_plans = 1, gets the plan based on the request's statement offset--If @get_plans = 2, gets the entire plan based on the request's plan_handle@get_plans TINYINT=0,@get_plans TINYINT=0,--Get the associated outer ad hoc query or stored procedure call, if available@get_outer_command BIT=0,--Enables pulling transaction log write info and transaction duration@get_transaction_info BIT=0,--Get information on active tasks, based on three interest levels--Level 0 does not pull any task-related information--Level 1 is a lightweight mode that pulls the top non-CXPACKET wait, giving preference to blockers--Level 2 pulls all available task-based metrics, including:--number of active tasks, current wait stats, physical I/O, context switches, and blocker information@get_task_info TINYINT=1,--Gets associated locks for each request, aggregated in an XML format@get_locks BIT=0,--Get average time for past runs of an active query--(based on the combination of plan handle, sql handle, and offset)@get_avg_time BIT=0,--Get additional non-performance-related information about the session or request--text_size, language, date_format, date_first, quoted_identifier, arithabort, ansi_null_dflt_on,--ansi_defaults, ansi_warnings, ansi_padding, ansi_nulls, concat_null_yields_null,--transaction_isolation_level, lock_timeout, deadlock_priority, row_count, command_type----If a SQL Agent job is running, an subnode called agent_info will be populated with some or all of--the following: job_id, job_name, step_id, step_name, msdb_query_error (in the event of an error)----If @get_task_info is set to 2 and a lock wait is detected, a subnode called block_info will be--populated with some or all of the following: lock_type, database_name, object_id, file_id, hobt_id,--applock_hash, metadata_resource, metadata_class_id, object_name, schema_name@get_additional_info BIT=0,--Walk the blocking chain and count the number of--total SPIDs blocked all the way down by a given session--Also enables task_info Level 1, if @get_task_info is set to 0@find_block_leaders BIT=0,--Pull deltas on various metrics--Interval in seconds to wait before doing the second data pull@delta_interval TINYINT=0,--List of desired output columns, in desired order--Note that the final output will be the intersection of all enabled features and all--columns in the list. Therefore, only columns associated with enabled features will--actually appear in the output. Likewise, removing columns from this list may effectively--disable features, even if they are turned on----Each element in this list must be one of the valid output column names. Names must be--delimited by square brackets. White space, formatting, and additional characters are--allowed, as long as the list contains exact matches of delimited valid column names.@output_column_list VARCHAR(8000) ='[dd%][session_id][sql_text][sql_command][login_name][wait_info][tasks][tran_log%][cpu%][temp%][block%][reads%][writes%][cont --Column(s) by which to sort output, optionally with sort directions.--Valid column choices:--session_id, physical_io, reads, physical_reads, writes, tempdb_allocations,--tempdb_current, CPU, context_switches, used_memory, physical_io_delta,--reads_delta, physical_reads_delta, writes_delta, tempdb_allocations_delta,--tempdb_current_delta, CPU_delta, context_switches_delta, used_memory_delta,--tasks, tran_start_time, open_tran_count, blocking_session_id, blocked_session_count,--percent_complete, host_name, login_name, database_name, start_time, login_time----Note that column names in the list must be bracket-delimited. Commas and/or white--space are not required.@sort_order VARCHAR(500) ='[start_time] ASC',--Formats some of the output columns in a more "human readable" form--0 disables outfput format--1 formats the output for variable-width fonts--2 formats the output for fixed-width fonts@format_output TINYINT=1,@format_output TINYINT=1,--If set to a non-blank value, the script will attempt to insert into the specified--destination table. Please note that the script will not verify that the table exists,--or that it has the correct schema, before doing the insert.--Table can be specified in one, two, or three-part format@destination_table VARCHAR(4000) ='',--If set to 1, no data collection will happen and no result set will be returned; instead,--a CREATE TABLE statement will be returned via the @schema parameter, which will match --the schema of the result set that would be returned by using the same collection of the--rest of the parameters. The CREATE TABLE statement will have a placeholder token of--<table_name> in place of an actual table name.@return_schema BIT=0,@schema VARCHAR(MAX) =NULL OUTPUT,--Help! What do I do?@help BIT=0--~)/*OUTPUT COLUMNS--------------Formatted/Non: [session_id] [smallint] NOT NULLSession ID (a.k.a. SPID)Formatted: [dd hh:mm:ss.mss] [varchar](15) NULLNon-Formatted: <not returned>For an active request, time the query has been runningFor a sleeping session, time since the last batch completedFormatted: [dd hh:mm:ss.mss (avg)] [varchar](15) NULLNon-Formatted: [avg_elapsed_time] [int] NULL(Requires @get_avg_time option)How much time has the active portion of the query taken in the past, on average? Formatted: [physical_io] [varchar](30) NULLNon-Formatted: [physical_io] [bigint] NULLShows the number of physical I/Os, for active requestsFormatted: [reads] [varchar](30) NULLNon-Formatted: [reads] [bigint] NULLFor an active request, number of reads done for the current queryFor a sleeping session, total number of reads done over the lifetime of the session Formatted: [physical_reads] [varchar](30) NULLNon-Formatted: [physical_reads] [bigint] NULLFor an active request, number of physical reads done for the current queryFor a sleeping session, total number of physical reads done over the lifetime of the session Formatted: [writes] [varchar](30) NULLNon-Formatted: [writes] [bigint] NULLFor an active request, number of writes done for the current queryFor a sleeping session, total number of writes done over the lifetime of the session Formatted: [tempdb_allocations] [varchar](30) NULLNon-Formatted: [tempdb_allocations] [bigint] NULLFor an active request, number of TempDB writes done for the current queryFor a sleeping session, total number of TempDB writes done over the lifetime of the session Formatted: [tempdb_current] [varchar](30) NULLNon-Formatted: [tempdb_current] [bigint] NULLFor an active request, number of TempDB pages currently allocated for the queryFor a sleeping session, number of TempDB pages currently allocated for the session Formatted: [CPU] [varchar](30) NULLNon-Formatted: [CPU] [int] NULLFor an active request, total CPU time consumed by the current queryFor a sleeping session, total CPU time consumed over the lifetime of the session Formatted: [context_switches] [varchar](30) NULLNon-Formatted: [context_switches] [bigint] NULL。
数据库SQL优化大总结之百万级数据库优化方案网上关于SQL优化的教程很多,但是比较杂乱。
近日有空整理了一下,写出来跟大家分享一下,其中有错误和不足的地方,还请大家纠正补充。
这篇文章我花费了大量的时间查找资料、修改、排版,希望大家阅读之后,感觉好的话推荐给更多的人,让更多的人看到、纠正以及补充。
1.对查询进行优化,要尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引。
2.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,如:select id from t where num isnull最好不要给数据库留NULL,尽可能的使用NOT NULL填充数据库.备注、描述、评论之类的可以设置为NULL,其他的,最好不要使用NULL。
不要以为NULL 不需要空间,比如:char(100) 型,在字段建立时,空间就固定了,不管是否插入值(NULL也包含在内),都是占用100个字符的空间的,如果是varchar 这样的变长字段,null 不占用空间。
可以在num上设置默认值0,确保表中num列没有null值,然后这样查询:select id from t where num=03.应尽量避免在 where 子句中使用 != 或 <> 操作符,否则将引擎放弃使用索引而进行全表扫描。
4.应尽量避免在 where 子句中使用 or 来连接条件,如果一个字段有索引,一个字段没有索引,将导致引擎放弃使用索引而进行全表扫描,如:select id from t where num=10or Name ='admin'可以这样查询:select id from t where num= 10union allselect id from t where Name ='admin'5.in 和 not in 也要慎用,否则会导致全表扫描,如:select id from t where num in(1,2,3)对于连续的数值,能用 between 就不要用 in 了:select id from t where num between1and3很多时候用exists 代替 in是一个好的选择:select num from a where num in(select num from b)用下面的语句替换:select num from a where exists(select1from b where num=a.num)6.下面的查询也将导致全表扫描:select id from t where name like…%abc%‟若要提高效率,可以考虑全文检索。