- 浏览: 67725 次
- 性别:
- 来自: 深圳
文章分类
最新评论
-
fabulasia:
试过了,没卵用
android程序在LogCat没有日志输出解决方案 -
心惶惶呀:
有用
android程序在LogCat没有日志输出解决方案 -
jialeadmin:
写的不错 很基础
JAVA面试题解惑系列(十)——话说多线程 -
skying007:
哥们,你写的太经典了。受益匪浅!!!!
JAVA面试题解惑系列(十一)——这些运算符你是否还记得? -
skying007:
总结的真不错,辛苦了。
125常见JAVA技术面试题
原文地址:http://blog.csdn.net/robinson_0612/article/details/7406672
No SQL,No cost. SQL语句是造成数据库开销最大的部分。而不良SQL写法直接导致数据库系统性能下降的情形比比皆是。那么如何才能称得
上高效的SQL语句呢?一是查询优化器为当前的SQL语句生成最佳的执行计划,保证数据读写使用最佳路径;二是设置合理的物理存储结构,如表
的类型,字段的顺序,字段的数据类型等。本文主要描述如何编写高效的SQL语句并给出示例。下面的描述主要分为三个部分,一是编写高效SQL
语句,二是使用索引提高查询性能的部分,三是总结部分。
一、编写高效SQL语句
二、合理使用索引以提高性能
索引依赖于表而存在,是真实表的一个缩影,类似于一本书的目录,通过目录以更快获得所需的结果。Oracle使用了一个复杂的自平衡
B数据结构。即任意记录的DML操作将打破索引的平衡,而定期重构索引使得索引重新获得平衡。通常,通过索引查找数据比全表扫描更高效。
任意的DQL或DML操作,SQL优化引擎优先使用索引来计算当前操作的成本以生成最佳的执行计划。一旦使用索引操出参数optimizer_index_cost_adj
设定的值才使用全表扫描。同样对于多表连接使用索引也可以提高效率。同时索引也提供主键(primary key)的唯一性验证。
除了那些LONG或LONG RAW数据类型,你可以索引几乎所有的列.通常,在大型表中使用索引特别有效.当然,你也会发现,在扫描小表时,使用索
引同样能提高效率。
虽然使用索引能得到查询效率的提高,但是索引需要空间来存储,需要定期维护.尤其是在有大量DML操作的表上,任意的DML操作都将引起索
引的变更这意味着每条记录的INSERT , DELETE , UPDATE将为此多付出4 , 5 次的磁盘I/O . 因为索引需要额外的存储空间和处理,
那些不必要的索引反而会使查询反应时间变慢。
DML操作使用索引上存在碎片而失去高度均衡,因此定期的重构索引是有必要的.
- 1) 避免基于索引列的计算
- where 子句中的谓词上存在索引,而此时基于该列的计算将使得索引失效
- -->低效:
- SELECT employee_id, first_name
- FROM employees
- WHERE employee_id + 10 > 150; -->索引列上使用了计算,因此索引失效,走全表扫描方式
- -->高效:
- SELECT employee_id, first_name
- FROM employees
- WHERE employee_id > 160; -->走索引范围扫描方式
- 例外情形
- 上述规则不适用于SQL中的MIN和MAX函数
- hr@CNMMBO> SELECT MAX( employee_id ) max_id
- 2 FROM employees
- 3 WHERE employee_id
- 4 + 10 > 150;
- 1 row selected.
- Execution Plan
- ----------------------------------------------------------
- Plan hash value: 1481384439
- ---------------------------------------------------------------------------------------------
- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
- ---------------------------------------------------------------------------------------------
- | 0 | SELECT STATEMENT | | 1 | 4 | 1 (0)| 00:00:01 |
- | 1 | SORT AGGREGATE | | 1 | 4 | | |
- | 2 | FIRST ROW | | 5 | 20 | 1 (0)| 00:00:01 |
- |* 3 | INDEX FULL SCAN (MIN/MAX)| EMP_EMP_ID_PK | 5 | 20 | 1 (0)| 00:00:01 |
- ---------------------------------------------------------------------------------------------
- 2) 避免在索引列上使用NOT运算或不等于运算(<>,!=)
- 通常,我们要避免在索引列上使用NOT或<>,两者会产生在和在索引列上使用函数相同的影响。 当ORACLE遇到NOT或不等运算时,他就会停止
- 使用索引转而执行全表扫描。
- -->低效:
- SELECT *
- FROM emp
- WHERE NOT ( deptno = 20 ); -->实际上NOT ( deptno = 20 )等同于deptno <> 20,即deptno <>同样会限制索引
- -->高效:
- SELECT *
- FROM emp
- WHERE deptno > 20 OR deptno < 20;
- -->尽管此方式可以替换且实现上述结果,但依然走全表扫描,如果是单纯的 > 或 < 运算,则此时为索引范围扫描
- 需要注意的是,在某些时候, ORACLE优化器会自动将NOT转化成相对应的关系操作符
- 其次如果是下列运算符进行NOT运算,依然有可能选择走索引, 仅仅除了NOT = 之外,因为 NOT = 等价于 <>
- “NOT >” to <=
- “NOT >=” to <
- “NOT <” to >=
- “NOT <=” to >
- 来看一个实际的例子
- hr@CNMMBO> SELECT *
- 2 FROM employees
- 3 where not employee_id<100; -->索引列上使用了not,但是该查询返回了所有的记录,即107条,因此此时选择走全表扫描
- 107 rows selected.
- Execution Plan
- ----------------------------------------------------------
- Plan hash value: 1445457117
- -------------------------------------------------------------------------------
- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
- -------------------------------------------------------------------------------
- | 0 | SELECT STATEMENT | | 107 | 7276 | 3 (0)| 00:00:01 |
- |* 1 | TABLE ACCESS FULL| EMPLOYEES | 107 | 7276 | 3 (0)| 00:00:01 | -->执行计划中使用了走全表扫描方式
- -------------------------------------------------------------------------------
- Predicate Information (identified by operation id):
- ---------------------------------------------------
- 1 - filter("EMPLOYEE_ID">=100) -->查看这里的谓词信息被自动转换为 >= 运算符
- hr@CNMMBO> SELECT *
- 2 FROM employees
- 3 where not employee_id<140; -->此例与上面的语句相同,仅仅是查询范围不同返回67条记录,而此时选择了索引范围扫描
- 67 rows selected.
- Execution Plan
- ----------------------------------------------------------
- Plan hash value: 603312277
- ---------------------------------------------------------------------------------------------
- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
- ---------------------------------------------------------------------------------------------
- | 0 | SELECT STATEMENT | | 68 | 4624 | 3 (0)| 00:00:01 |
- | 1 | TABLE ACCESS BY INDEX ROWID| EMPLOYEES | 68 | 4624 | 3 (0)| 00:00:01 |
- |* 2 | INDEX RANGE SCAN | EMP_EMP_ID_PK | 68 | | 1 (0)| 00:00:01 | -->索引范围扫描方式
- ---------------------------------------------------------------------------------------------
- Predicate Information (identified by operation id):
- ---------------------------------------------------
- 2 - access("EMPLOYEE_ID">=140)
- 3) 用UNION 替换OR(适用于索引列)
- 通常情况下,使用UNION 替换WHERE子句中的OR将会起到较好的效果.基于索引列使用OR使得优化器倾向于使用全表扫描,而不是扫描索引.
- 注意,以上规则仅适用于多个索引列有效。 如果有column没有被索引, 查询效率可能会因为你没有选择OR而降低。
- -->低效:
- SELECT deptno, dname
- FROM dept
- WHERE loc = 'DALLAS' OR deptno = 20;
- -->高效:
- SELECT deptno, dname
- FROM dept
- WHERE loc = 'DALLAS'
- UNION
- SELECT deptno, dname
- FROM dept
- WHERE deptno = 30
- -->经测试,由于数据量较少,此时where子句中的谓词上都存在索引列时,两者性能相当.
- -->假定where子句中存在两列
- scott@CNMMBO> create table t6 as select object_id,owner,object_name from dba_objects where owner='SYS' and rownum<1001;
- scott@CNMMBO> insert into t6 select object_id,owner,object_name from dba_objects where owner='SCOTT' and rownum<6;
- scott@CNMMBO> create index i_t6_object_id on t6(object_id);
- scott@CNMMBO> create index i_t6_owner on t6(owner);
- scott@CNMMBO> insert into t6 select object_id,owner,object_name from dba_objects where owner='SYSTEM' and rownum<=300;
- scott@CNMMBO> commit;
- scott@CNMMBO> exec dbms_stats.gather_table_stats('SCOTT','T6',cascade=>true);
- scott@CNMMBO> select owner,count(*) from t6 group by owner;
- OWNER COUNT(*)
- -------------------- ----------
- SCOTT 5
- SYSTEM 300
- SYS 1000
- scott@CNMMBO> select * from t6 where owner='SCOTT' and rownum<2;
- OBJECT_ID OWNER OBJECT_NAME
- ---------- -------------------- --------------------
- 69450 SCOTT T_TEST
- scott@CNMMBO> select * from t6 where object_id=69450 or owner='SYSTEM';
- 301 rows selected.
- Execution Plan
- ----------------------------------------------------------
- Plan hash value: 238853296
- -----------------------------------------------------------------------------------------------
- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
- -----------------------------------------------------------------------------------------------
- | 0 | SELECT STATEMENT | | 300 | 7200 | 5 (0)| 00:00:01 |
- | 1 | CONCATENATION | | | | | |
- | 2 | TABLE ACCESS BY INDEX ROWID| T6 | 1 | 24 | 2 (0)| 00:00:01 |
- |* 3 | INDEX RANGE SCAN | I_T6_OBJECT_ID | 1 | | 1 (0)| 00:00:01 |
- |* 4 | TABLE ACCESS BY INDEX ROWID| T6 | 299 | 7176 | 3 (0)| 00:00:01 |
- |* 5 | INDEX RANGE SCAN | I_T6_OWNER | 300 | | 1 (0)| 00:00:01 |
- -----------------------------------------------------------------------------------------------
- Predicate Information (identified by operation id):
- ---------------------------------------------------
- 3 - access("OBJECT_ID"=69450)
- 4 - filter(LNNVL("OBJECT_ID"=69450))
- 5 - access("OWNER"='SYSTEM')
- Statistics
- ----------------------------------------------------------
- 0 recursive calls
- 0 db block gets
- 46 consistent gets
- 0 physical reads
- 0 redo size
- 11383 bytes sent via SQL*Net to client
- 712 bytes received via SQL*Net from client
- 22 SQL*Net roundtrips to/from client
- 0 sorts (memory)
- 0 sorts (disk)
- 301 rows processed
- scott@CNMMBO> select * from t6 where owner='SYSTEM' or object_id=69450;
- 301 rows selected.
- Execution Plan
- ----------------------------------------------------------
- Plan hash value: 238853296
- -----------------------------------------------------------------------------------------------
- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
- -----------------------------------------------------------------------------------------------
- | 0 | SELECT STATEMENT | | 300 | 7200 | 5 (0)| 00:00:01 |
- | 1 | CONCATENATION | | | | | |
- | 2 | TABLE ACCESS BY INDEX ROWID| T6 | 1 | 24 | 2 (0)| 00:00:01 |
- |* 3 | INDEX RANGE SCAN | I_T6_OBJECT_ID | 1 | | 1 (0)| 00:00:01 |
- |* 4 | TABLE ACCESS BY INDEX ROWID| T6 | 299 | 7176 | 3 (0)| 00:00:01 |
- |* 5 | INDEX RANGE SCAN | I_T6_OWNER | 300 | | 1 (0)| 00:00:01 |
- -----------------------------------------------------------------------------------------------
- Predicate Information (identified by operation id):
- ---------------------------------------------------
- 3 - access("OBJECT_ID"=69450)
- 4 - filter(LNNVL("OBJECT_ID"=69450))
- 5 - access("OWNER"='SYSTEM')
- Statistics
- ----------------------------------------------------------
- 1 recursive calls
- 0 db block gets
- 46 consistent gets
- 0 physical reads
- 0 redo size
- 11383 bytes sent via SQL*Net to client
- 712 bytes received via SQL*Net from client
- 22 SQL*Net roundtrips to/from client
- 0 sorts (memory)
- 0 sorts (disk)
- 301 rows processed
- scott@CNMMBO> select * from t6
- 2 where object_id=69450
- 3 union
- 4 select * from t6
- 5 where owner='SYSTEM';
- 301 rows selected.
- Execution Plan
- ----------------------------------------------------------
- Plan hash value: 370530636
- ------------------------------------------------------------------------------------------------
- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
- ------------------------------------------------------------------------------------------------
- | 0 | SELECT STATEMENT | | 301 | 7224 | 7 (72)| 00:00:01 |
- | 1 | SORT UNIQUE | | 301 | 7224 | 7 (72)| 00:00:01 |
- | 2 | UNION-ALL | | | | | |
- | 3 | TABLE ACCESS BY INDEX ROWID| T6 | 1 | 24 | 2 (0)| 00:00:01 |
- |* 4 | INDEX RANGE SCAN | I_T6_OBJECT_ID | 1 | | 1 (0)| 00:00:01 |
- | 5 | TABLE ACCESS BY INDEX ROWID| T6 | 300 | 7200 | 3 (0)| 00:00:01 |
- |* 6 | INDEX RANGE SCAN | I_T6_OWNER | 300 | | 1 (0)| 00:00:01 |
- ------------------------------------------------------------------------------------------------
- Predicate Information (identified by operation id):
- ---------------------------------------------------
- 4 - access("OBJECT_ID"=69450)
- 6 - access("OWNER"='SYSTEM')
- Statistics
- ----------------------------------------------------------
- 1 recursive calls
- 0 db block gets
- 7 consistent gets
- 0 physical reads
- 0 redo size
- 11383 bytes sent via SQL*Net to client
- 712 bytes received via SQL*Net from client
- 22 SQL*Net roundtrips to/from client
- 1 sorts (memory)
- 0 sorts (disk)
- 301 rows processed
- -->从上面的统计信息可知,consistent gets由46下降为7,故当where子句中谓词上存在索引时,使用union替换or更高效
- -->即使当列object_id与owner上不存在索引时,使用union仍然比or更高效(在Oracle 10g R2与Oracle 11g R2测试)
- 4) 避免索引列上使用函数
- -->下面是一个来自实际生产环境的例子
- -->表acc_pos_int_tbl上business_date列存在索引,由于使用了SUBSTR函数,此时索引失效,使用全表扫描
- SELECT acc_num
- , curr_cd
- , DECODE( '20110728'
- , ( SELECT TO_CHAR( LAST_DAY( TO_DATE( '20110728', 'YYYYMMDD' ) ), 'YYYYMMDD' ) FROM dual ), 0
- , adj_credit_int_lv1_amt
- + adj_credit_int_lv2_amt
- - adj_debit_int_lv1_amt
- - adj_debit_int_lv2_amt )
- AS interest
- FROM acc_pos_int_tbl
- WHERE SUBSTR( business_date, 1, 6 ) = SUBSTR( '20110728', 1, 6 ) AND business_date <= '20110728';
- -->改进的办法
- SELECT acc_num
- , curr_cd
- , DECODE( '20110728'
- , ( SELECT TO_CHAR( LAST_DAY( TO_DATE( '20110728', 'YYYYMMDD' ) ), 'YYYYMMDD' ) FROM dual ), 0
- , adj_credit_int_lv1_amt
- + adj_credit_int_lv2_amt
- - adj_debit_int_lv1_amt
- - adj_debit_int_lv2_amt )
- AS interest
- FROM acc_pos_int_tbl acc_pos_int_tbl
- WHERE business_date >= TO_CHAR( LAST_DAY( ADD_MONTHS( TO_DATE( '20110728', 'yyyymmdd' ), -1 ) )
- + 1, 'yyyymmdd' )
- AND business_date <= '20110728';
- -->下面的例子虽然没有使用函数,但字符串连接同样导致索引失效
- -->低效:
- SELECT account_name, amount
- FROM transaction
- WHERE account_name
- || account_type = 'AMEXA';
- -->高效:
- SELECT account_name, amount
- FROM transaction
- WHERE account_name = 'AMEX' AND account_type = 'A';
- 5) 比较不匹配的数据类型
- -->下面的查询中business_date列上存在索引,且为字符型,这种
- -->低效:
- SELECT *
- FROM acc_pos_int_tbl
- WHERE business_date = 20090201;
- Execution Plan
- ----------------------------------------------------------
- Plan hash value: 2335235465
- -------------------------------------------------------------------------------------
- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
- -------------------------------------------------------------------------------------
- | 0 | SELECT STATEMENT | | 37516 | 2857K| 106K (1)| 00:21:17 |
- |* 1 | TABLE ACCESS FULL| ACC_POS_INT_TBL | 37516 | 2857K| 106K (1)| 00:21:17 |
- -------------------------------------------------------------------------------------
- Predicate Information (identified by operation id):
- ---------------------------------------------------
- 1 - filter(TO_NUMBER("BUSINESS_DATE")=20090201) -->这里可以看到产生了类型转换
- -->高效:
- SELECT *
- FROM acc_pos_int_tbl
- WHERE business_date = '20090201'
- 6) 索引列上使用 NULL 值
- IS NULL和IS NOT NULL会限制索引的使用,因为数据中没有值等于NULL值,即便是NULL值也不等于NULL值.且NULL值不存储在于索引之中
- 因此应尽可能避免在索引类上使用NULL值
- SELECT acc_num
- , pl_cd
- , order_qty
- , trade_date
- FROM trade_client_tbl
- WHERE input_date IS NOT NULL;
- Execution Plan
- ----------------------------------------------------------
- Plan hash value: 901462645
- --------------------------------------------------------------------------------------
- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
- --------------------------------------------------------------------------------------
- | 0 | SELECT STATEMENT | | 1 | 44 | 15 (0)| 00:00:01 |
- |* 1 | TABLE ACCESS FULL| TRADE_CLIENT_TBL | 1 | 44 | 15 (0)| 00:00:01 |
- --------------------------------------------------------------------------------------
- alter table trade_client_tbl modify (input_date not null);
- 不推荐使用的查询方式
- SELECT * FROM table_name WHERE col IS NOT NULL
- SELECT * FROM table_name WHERE col IS NULL
- 推荐使用的方式
- SELECT * FROM table_name WHERE col >= 0 --尽可能的使用 =, >=, <=, like 等运算符
- -->Author: Robinson Cheng
- -->Blog: http://blog.csdn.net/robinson_0612
三、总结
1、尽可能最小化基表数据以及中间结果集(通过过滤条件避免后续产生不必要的计算与聚合)
2、为where子句中的谓词信息提供最佳的访问路径(rowid访问,索引访问)
3、使用合理的SQL写法来避免过多的Oracle内部开销以提高性能
4、合理的使用提示以提高表之间的连接来提高连接效率(如避免迪卡尔集,将不合理的嵌套连接改为hash连接等)
相关推荐
本项目提供了一个Java源码工具,能够帮助用户便捷地将Oracle SQL语句转换为MySQL SQL语句。 Oracle SQL与MySQL SQL的主要差异在于以下几个方面: 1. **数据类型**:Oracle支持的数据类型如NUMBER、LONG、RAW等在...
XML 查询语句转换成 SQL 语句的实现 本文主要讨论将 XML 查询语句转换成 SQL 语句的实现。XML 已经成为 Internet 上数据交换的事实标准,特别是在 Web 数据挖掘技术中,对数据源的结构化需求导致人们对 XML 文档的...
然而,在某些情况下,我们需要将这些表达式转换为实际的SQL语句,以便在数据库中执行。本文将深入探讨如何实现这一过程。 首先,我们需要了解Lambda表达式的基本结构。Lambda表达式通常以参数列表开始,后面跟着一...
在实际工作中,合理利用"SQL语句万能生成器"这样的工具,结合个人的SQL知识,能够更高效地完成数据库操作,提升工作效率。在压缩包文件"sql_ok"中,可能包含了这样一个工具的安装程序或使用教程,可以帮助你开始探索...
【如何编写高效SQL语句】 在数据库管理中,编写高效的SQL语句对于系统性能的优化至关重要。本篇将深入探讨性能调整的综合观点、有效应用设计以及SQL语句处理的过程,帮助你提升数据库系统的整体效率。 首先,性能...
sql语句说明sql语句说明sql语句说明sql语句说明sql语句说明sql语句说明sql语句说明sql语句说明sql语句说明sql语句说明sql语句说明sql语句说明sql语句说明sql语句说明sql语句说明sql语句说明sql语句说明sql语句说明...
总结来说,"将数据库中的数据转换成SQL语句"是一种高效的数据迁移方法,尤其在面临版本不兼容时。使用像"DataRow2InsertSql.exe"这样的工具,可以简化这一过程,但同时也需注意相关的技术挑战和最佳实践。
在压缩包子文件的文件名"ms转sql语句.exe"中,我们可以推测这可能是一个用于将SQL Server语句转换为Oracle兼容格式的可执行程序。这样的工具通常会提供用户友好的界面,让用户导入SQL Server的数据库元数据,然后...
这就涉及到了“SQL语句转String”这一过程。 首先,理解SQL语句转String的含义。它指的是将已经验证过的SQL查询语句,如SELECT、INSERT、UPDATE或DELETE等,转换成Java代码中的字符串形式。这样做的目的是使得Java...
在Java编程中,调试SQL语句是开发过程中的常见任务,尤其当面对复杂且冗长的查询时。为了提高效率并使SQL语句更易于理解和分析,格式化SQL语句显得尤为重要。标题提及的"Java打印漂亮的SQL语句(被格式化的SQL语句)...
"值对象转换成SQL语句"的主题涉及到了如何将程序中的对象模型直接映射到SQL查询,这通常被称为对象关系映射(ORM)。ORM是一种编程技术,它允许开发者使用面向对象的方式来操作数据库,而无需直接编写SQL语句,降低...
在开发数据库应用程序时,SQL语句是至关重要的工具,用于查询、插入、更新和删除数据库中的数据。本文将深入探讨SQL语句命令及其在数据库操作中的应用,特别是使用ADO.NET框架进行数据库交互。 首先,我们需要了解...
在实际开发中,有时我们需要了解LINQ查询是如何转换为SQL语句的,以便于调试和优化性能。以下将详细介绍几种查看LINQ生成SQL语句的方法。 1. **Debug.WriteLine()** 在使用LINQ查询时,可以利用`Debug.WriteLine()...
PB脚本中SQL语句写法与SQL中语句写法对照 PB脚本中SQL语句写法与SQL中语句写法对照是非常重要的知识点,因为PB脚本和SQL语言在写法和应用中有所不同。本文将对PB脚本中SQL语句写法和SQL中语句写法进行对比和分析。 ...
功能: 根据表名、where条件,生成导出数据的SQL语句。(包含insert语句。结果可一键执行,利于数据导出、导入) 参数: @tableName nvarchar(100) --表名 ,@sqlWhere nvarchar(500) --where条件,如'a=''123'' '(传...
高效SQL语句测试使用没什么用不要浪费时间
ArcGIS软件提供了丰富的工具和界面,允许用户通过SQL语句对地理空间数据进行高效的操作。 #### 二、SQL语句类型及其在ArcGIS中的运用 ##### 1. 数据操作语言(DML) DML主要用于对数据库中的数据进行增删改查操作,...
把XML文件转换成SQL语句,导入数据库。
下载本资源后,在命令提示符下用 cscript.exe MDBtoSQLite.vbs <mdb file> 可以将ACCESS数据库转成SQL语句. ' Usage: ' ' cscript.exe MDBtoSQLite.vbs <mdb file> [second mdb file] [...] ' ' Example: cscript ...