Sql Oracle中的动态查询索引
我有一个程序,根据参数建立动态where条件。如果任何参数为null,我们将忽略以在where条件中检查此列。有人能告诉我什么是索引表以获得最佳性能的最佳方法吗Sql Oracle中的动态查询索引,sql,oracle,indexing,Sql,Oracle,Indexing,我有一个程序,根据参数建立动态where条件。如果任何参数为null,我们将忽略以在where条件中检查此列。有人能告诉我什么是索引表以获得最佳性能的最佳方法吗 V_sql VARCHAR2(10000):='SELECT SV_ACC_REG.ACC_REG_ID AS ACC_REG_ID , SV_ACC_REG.PRODUCT_ID AS PRODUCT_ID , GEN_PROD
V_sql VARCHAR2(10000):='SELECT
SV_ACC_REG.ACC_REG_ID AS ACC_REG_ID ,
SV_ACC_REG.PRODUCT_ID AS PRODUCT_ID ,
GEN_PRODUCT.FULL_NAME AS PRODUCT_NAME ,
SV_ACC_REG.STATUS AS STATUS ,
SV_ACC_REG.OPENING_DATE AS OPENING_DATE ,
SV_ACC_REG.CURRENT_BALANCE AS CURRENT_BALANCE ,
SV_ACC_REG.CLOSING_DATE AS CLOSING_DATE ,
SV_ACC_REG.REG_NO AS REG_NO ,
SV_ACC_REG.IS_WITHDRAW_BY_SINGLE AS IS_WITHDRAW_BY_SINGLE,
SV_ACC_REG.IS_SINGLE AS IS_SINGLE ,
SV_ACC_REG.IS_EXTENDABLE AS IS_EXTENDABLE ,
SV_ACC_REG.REMARKS AS REMARKS ,
SV_ACC_REG.PR_NO AS PR_NO ,
SV_ACC_REG.CREATED_ON AS CREATED_ON ,
SV_ACC_REG.CREATED_BY AS CREATED_BY ,
SV_ACC_REG.UPDATED_ON AS UPDATED_ON ,
SV_ACC_REG.UPDATED_BY AS UPDATED_BY ,
SV_ACC_REG.IS_DELETED AS IS_DELETED ,
SV_ACC_REG.DELETED_ON AS DELETED_ON ,
SV_ACC_REG.DELETED_BY AS DELETED_BY ,
SV_ACC_REG.CLIENT_TYPE AS CLIENT_TYPE ,
SV_ACC_REG.IS_TRANSFER AS IS_TRANSFER ,
SV_ACC_REG.WITHDRAW_TYPE AS WITHDRAW_TYPE ,
SV_ACC_REG.DEATH_DATE AS DEATH_DATE ,
SV_ACC_REG.IS_MIGRATE AS IS_MIGRATE ,
SV_ACC_REG.MIGRATE_COMMENTS AS MIGRATE_COMMENTS ,
SV_ACC_REG.CHEQUE_HONOR_DATE AS CHEQUE_HONOR_DATE ,
SV_ACC_REG.SO_NO AS SO_NO ,
SV_ACC_REG.IS_MINOR AS IS_MINOR ,
SV_ACC_REG.NAME AS NAME ,
SV_ACC_REG.IS_OLD AS IS_OLD ,
SV_ACC_REG.IS_NO_PROFIT_CALC AS IS_NO_PROFIT_CALC ,
SV_ACC_REG.IS_SIX_M_PROFIT_CALC AS IS_SIX_M_PROFIT_CALC ,
SV_ACC_REG.IS_SEND_DPMG ,
SV_CUSTOMER_INFO.CUSTOMER_NAME AS CUSTOMER_NAME
FROM SV_ACC_REG
LEFT JOIN GEN_PRODUCT ON SV_ACC_REG.PRODUCT_ID=GEN_PRODUCT.PRODUCT_NO
LEFT JOIN SV_CUSTOMER_INFO ON SV_ACC_REG.ACC_REG_ID = SV_CUSTOMER_INFO.ACC_REG_ID';
V_WHERE VARCHAR2(500):=' WHERE ';
BEGIN
BEGIN
V_WHERE:=' WHERE ';
IF p_ACC_REG_ID IS NOT NULL THEN
V_WHERE := V_WHERE || ' SV_ACC_REG.ACC_REG_ID = '||p_ACC_REG_ID||' AND';
END IF;
IF p_PRODUCT_ID IS NOT NULL THEN
V_WHERE := V_WHERE || ' SV_ACC_REG.PRODUCT_ID = '||p_PRODUCT_ID||' AND';
END IF;
IF p_STATUS IS NOT NULL THEN
V_WHERE := V_WHERE || ' SV_ACC_REG.STATUS = '||p_STATUS||' AND';
END IF;
IF p_IS_TRANSFER IS NOT NULL THEN
V_WHERE := V_WHERE || ' SV_ACC_REG.IS_TRANSFER = '||p_IS_TRANSFER||' AND';
END IF;
IF p_SO_NO IS NOT NULL THEN
V_WHERE := V_WHERE || ' SV_ACC_REG.SO_NO = '||p_SO_NO||' AND';
END IF;
IF p_IS_OLD IS NOT NULL THEN
V_WHERE := V_WHERE || ' SV_ACC_REG.IS_OLD = '||p_IS_OLD||' AND';
END IF;
IF p_IS_SEND_DPMG IS NOT NULL THEN
V_WHERE := V_WHERE || ' SV_ACC_REG.IS_SEND_DPMG = '||p_IS_SEND_DPMG||' AND';
END IF;
IF p_IS_SIX_M_PROFIT_CALC IS NOT NULL THEN
V_WHERE := V_WHERE || ' IS_SIX_M_PROFIT_CALC= '||p_IS_SEND_DPMG||' AND';
END IF;
IF LENGTH(' WHERE ') =7 THEN
V_sql :=V_sql ||' ORDER BY SV_ACC_REG.ACC_REG_ID ASC';
ELSE
V_sql :=V_sql || SUBSTR(V_WHERE, 1, LENGTH(V_WHERE) - 3) ||' ORDER BY SV_ACC_REG.ACC_REG_ID ASC';
END IF;
--V_sql :=SUBSTR(V_sql, 1, LENGTH(V_sql) - 3);
--OPEN cur_OUT FOR V_sql USING p_ACC_REG_ID, p_PRODUCT_ID,p_STATUS,p_IS_TRANSFER,p_SO_NO,p_IS_OLD,p_IS_SEND_DPMG,p_IS_SIX_M_PROFIT_CALC;
OPEN cur_OUT FOR V_sql ;
END;
END;
另一个问题是假设我有一个12列的表。我有两个查询,其中一个在where条件中有三列,另一个在where条件中有八列。在这种情况下,我应该创建两个不同的索引以获得更好的性能吗
V_sql VARCHAR2(10000):='SELECT
SV_ACC_REG.ACC_REG_ID AS ACC_REG_ID ,
SV_ACC_REG.PRODUCT_ID AS PRODUCT_ID ,
GEN_PRODUCT.FULL_NAME AS PRODUCT_NAME ,
SV_ACC_REG.STATUS AS STATUS ,
SV_ACC_REG.OPENING_DATE AS OPENING_DATE ,
SV_ACC_REG.CURRENT_BALANCE AS CURRENT_BALANCE ,
SV_ACC_REG.CLOSING_DATE AS CLOSING_DATE ,
SV_ACC_REG.REG_NO AS REG_NO ,
SV_ACC_REG.IS_WITHDRAW_BY_SINGLE AS IS_WITHDRAW_BY_SINGLE,
SV_ACC_REG.IS_SINGLE AS IS_SINGLE ,
SV_ACC_REG.IS_EXTENDABLE AS IS_EXTENDABLE ,
SV_ACC_REG.REMARKS AS REMARKS ,
SV_ACC_REG.PR_NO AS PR_NO ,
SV_ACC_REG.CREATED_ON AS CREATED_ON ,
SV_ACC_REG.CREATED_BY AS CREATED_BY ,
SV_ACC_REG.UPDATED_ON AS UPDATED_ON ,
SV_ACC_REG.UPDATED_BY AS UPDATED_BY ,
SV_ACC_REG.IS_DELETED AS IS_DELETED ,
SV_ACC_REG.DELETED_ON AS DELETED_ON ,
SV_ACC_REG.DELETED_BY AS DELETED_BY ,
SV_ACC_REG.CLIENT_TYPE AS CLIENT_TYPE ,
SV_ACC_REG.IS_TRANSFER AS IS_TRANSFER ,
SV_ACC_REG.WITHDRAW_TYPE AS WITHDRAW_TYPE ,
SV_ACC_REG.DEATH_DATE AS DEATH_DATE ,
SV_ACC_REG.IS_MIGRATE AS IS_MIGRATE ,
SV_ACC_REG.MIGRATE_COMMENTS AS MIGRATE_COMMENTS ,
SV_ACC_REG.CHEQUE_HONOR_DATE AS CHEQUE_HONOR_DATE ,
SV_ACC_REG.SO_NO AS SO_NO ,
SV_ACC_REG.IS_MINOR AS IS_MINOR ,
SV_ACC_REG.NAME AS NAME ,
SV_ACC_REG.IS_OLD AS IS_OLD ,
SV_ACC_REG.IS_NO_PROFIT_CALC AS IS_NO_PROFIT_CALC ,
SV_ACC_REG.IS_SIX_M_PROFIT_CALC AS IS_SIX_M_PROFIT_CALC ,
SV_ACC_REG.IS_SEND_DPMG ,
SV_CUSTOMER_INFO.CUSTOMER_NAME AS CUSTOMER_NAME
FROM SV_ACC_REG
LEFT JOIN GEN_PRODUCT ON SV_ACC_REG.PRODUCT_ID=GEN_PRODUCT.PRODUCT_NO
LEFT JOIN SV_CUSTOMER_INFO ON SV_ACC_REG.ACC_REG_ID = SV_CUSTOMER_INFO.ACC_REG_ID';
V_WHERE VARCHAR2(500):=' WHERE ';
BEGIN
BEGIN
V_WHERE:=' WHERE ';
IF p_ACC_REG_ID IS NOT NULL THEN
V_WHERE := V_WHERE || ' SV_ACC_REG.ACC_REG_ID = '||p_ACC_REG_ID||' AND';
END IF;
IF p_PRODUCT_ID IS NOT NULL THEN
V_WHERE := V_WHERE || ' SV_ACC_REG.PRODUCT_ID = '||p_PRODUCT_ID||' AND';
END IF;
IF p_STATUS IS NOT NULL THEN
V_WHERE := V_WHERE || ' SV_ACC_REG.STATUS = '||p_STATUS||' AND';
END IF;
IF p_IS_TRANSFER IS NOT NULL THEN
V_WHERE := V_WHERE || ' SV_ACC_REG.IS_TRANSFER = '||p_IS_TRANSFER||' AND';
END IF;
IF p_SO_NO IS NOT NULL THEN
V_WHERE := V_WHERE || ' SV_ACC_REG.SO_NO = '||p_SO_NO||' AND';
END IF;
IF p_IS_OLD IS NOT NULL THEN
V_WHERE := V_WHERE || ' SV_ACC_REG.IS_OLD = '||p_IS_OLD||' AND';
END IF;
IF p_IS_SEND_DPMG IS NOT NULL THEN
V_WHERE := V_WHERE || ' SV_ACC_REG.IS_SEND_DPMG = '||p_IS_SEND_DPMG||' AND';
END IF;
IF p_IS_SIX_M_PROFIT_CALC IS NOT NULL THEN
V_WHERE := V_WHERE || ' IS_SIX_M_PROFIT_CALC= '||p_IS_SEND_DPMG||' AND';
END IF;
IF LENGTH(' WHERE ') =7 THEN
V_sql :=V_sql ||' ORDER BY SV_ACC_REG.ACC_REG_ID ASC';
ELSE
V_sql :=V_sql || SUBSTR(V_WHERE, 1, LENGTH(V_WHERE) - 3) ||' ORDER BY SV_ACC_REG.ACC_REG_ID ASC';
END IF;
--V_sql :=SUBSTR(V_sql, 1, LENGTH(V_sql) - 3);
--OPEN cur_OUT FOR V_sql USING p_ACC_REG_ID, p_PRODUCT_ID,p_STATUS,p_IS_TRANSFER,p_SO_NO,p_IS_OLD,p_IS_SEND_DPMG,p_IS_SIX_M_PROFIT_CALC;
OPEN cur_OUT FOR V_sql ;
END;
END;
有人能告诉我索引表的最佳方法是什么吗
获得最佳性能
V_sql VARCHAR2(10000):='SELECT
SV_ACC_REG.ACC_REG_ID AS ACC_REG_ID ,
SV_ACC_REG.PRODUCT_ID AS PRODUCT_ID ,
GEN_PRODUCT.FULL_NAME AS PRODUCT_NAME ,
SV_ACC_REG.STATUS AS STATUS ,
SV_ACC_REG.OPENING_DATE AS OPENING_DATE ,
SV_ACC_REG.CURRENT_BALANCE AS CURRENT_BALANCE ,
SV_ACC_REG.CLOSING_DATE AS CLOSING_DATE ,
SV_ACC_REG.REG_NO AS REG_NO ,
SV_ACC_REG.IS_WITHDRAW_BY_SINGLE AS IS_WITHDRAW_BY_SINGLE,
SV_ACC_REG.IS_SINGLE AS IS_SINGLE ,
SV_ACC_REG.IS_EXTENDABLE AS IS_EXTENDABLE ,
SV_ACC_REG.REMARKS AS REMARKS ,
SV_ACC_REG.PR_NO AS PR_NO ,
SV_ACC_REG.CREATED_ON AS CREATED_ON ,
SV_ACC_REG.CREATED_BY AS CREATED_BY ,
SV_ACC_REG.UPDATED_ON AS UPDATED_ON ,
SV_ACC_REG.UPDATED_BY AS UPDATED_BY ,
SV_ACC_REG.IS_DELETED AS IS_DELETED ,
SV_ACC_REG.DELETED_ON AS DELETED_ON ,
SV_ACC_REG.DELETED_BY AS DELETED_BY ,
SV_ACC_REG.CLIENT_TYPE AS CLIENT_TYPE ,
SV_ACC_REG.IS_TRANSFER AS IS_TRANSFER ,
SV_ACC_REG.WITHDRAW_TYPE AS WITHDRAW_TYPE ,
SV_ACC_REG.DEATH_DATE AS DEATH_DATE ,
SV_ACC_REG.IS_MIGRATE AS IS_MIGRATE ,
SV_ACC_REG.MIGRATE_COMMENTS AS MIGRATE_COMMENTS ,
SV_ACC_REG.CHEQUE_HONOR_DATE AS CHEQUE_HONOR_DATE ,
SV_ACC_REG.SO_NO AS SO_NO ,
SV_ACC_REG.IS_MINOR AS IS_MINOR ,
SV_ACC_REG.NAME AS NAME ,
SV_ACC_REG.IS_OLD AS IS_OLD ,
SV_ACC_REG.IS_NO_PROFIT_CALC AS IS_NO_PROFIT_CALC ,
SV_ACC_REG.IS_SIX_M_PROFIT_CALC AS IS_SIX_M_PROFIT_CALC ,
SV_ACC_REG.IS_SEND_DPMG ,
SV_CUSTOMER_INFO.CUSTOMER_NAME AS CUSTOMER_NAME
FROM SV_ACC_REG
LEFT JOIN GEN_PRODUCT ON SV_ACC_REG.PRODUCT_ID=GEN_PRODUCT.PRODUCT_NO
LEFT JOIN SV_CUSTOMER_INFO ON SV_ACC_REG.ACC_REG_ID = SV_CUSTOMER_INFO.ACC_REG_ID';
V_WHERE VARCHAR2(500):=' WHERE ';
BEGIN
BEGIN
V_WHERE:=' WHERE ';
IF p_ACC_REG_ID IS NOT NULL THEN
V_WHERE := V_WHERE || ' SV_ACC_REG.ACC_REG_ID = '||p_ACC_REG_ID||' AND';
END IF;
IF p_PRODUCT_ID IS NOT NULL THEN
V_WHERE := V_WHERE || ' SV_ACC_REG.PRODUCT_ID = '||p_PRODUCT_ID||' AND';
END IF;
IF p_STATUS IS NOT NULL THEN
V_WHERE := V_WHERE || ' SV_ACC_REG.STATUS = '||p_STATUS||' AND';
END IF;
IF p_IS_TRANSFER IS NOT NULL THEN
V_WHERE := V_WHERE || ' SV_ACC_REG.IS_TRANSFER = '||p_IS_TRANSFER||' AND';
END IF;
IF p_SO_NO IS NOT NULL THEN
V_WHERE := V_WHERE || ' SV_ACC_REG.SO_NO = '||p_SO_NO||' AND';
END IF;
IF p_IS_OLD IS NOT NULL THEN
V_WHERE := V_WHERE || ' SV_ACC_REG.IS_OLD = '||p_IS_OLD||' AND';
END IF;
IF p_IS_SEND_DPMG IS NOT NULL THEN
V_WHERE := V_WHERE || ' SV_ACC_REG.IS_SEND_DPMG = '||p_IS_SEND_DPMG||' AND';
END IF;
IF p_IS_SIX_M_PROFIT_CALC IS NOT NULL THEN
V_WHERE := V_WHERE || ' IS_SIX_M_PROFIT_CALC= '||p_IS_SEND_DPMG||' AND';
END IF;
IF LENGTH(' WHERE ') =7 THEN
V_sql :=V_sql ||' ORDER BY SV_ACC_REG.ACC_REG_ID ASC';
ELSE
V_sql :=V_sql || SUBSTR(V_WHERE, 1, LENGTH(V_WHERE) - 3) ||' ORDER BY SV_ACC_REG.ACC_REG_ID ASC';
END IF;
--V_sql :=SUBSTR(V_sql, 1, LENGTH(V_sql) - 3);
--OPEN cur_OUT FOR V_sql USING p_ACC_REG_ID, p_PRODUCT_ID,p_STATUS,p_IS_TRANSFER,p_SO_NO,p_IS_OLD,p_IS_SEND_DPMG,p_IS_SIX_M_PROFIT_CALC;
OPEN cur_OUT FOR V_sql ;
END;
END;
好的,这个查询中有10个可选参数,null/notnull的一些可能组合是2^10=1024,因此您可以得到这个查询的1000个变体,每个变体可能需要一组不同的索引。这里很难给出合理的暗示。
在你的情况下,我会怎么做:
将应用程序部署到生产环境中
允许用户每周使用应用程序几天
登录数据库并运行以下查询您必须拥有DBA授予的相应权限
选择最有问题的查询,对其进行调优,然后在几天/几周/几个月后反复重复。
此查询将提取有关最常用的查询以及消耗资源最多的查询的基本统计信息。这里有很多统计数据,如执行情况、总运行时间、缓冲区获取等,这些数据将为您提供应用程序操作、用户行为等的总体情况,并允许您选择最差的查询进行进一步分析。
您可以进一步查询v$sql\u plan,以获取rdbms use sql\u id和plan\u hash\u value列使用的执行计划,以便对它们进行分析
select
SQL_TEXT
, SQL_FULLTEXT
, SQL_ID
, FETCHES
, EXECUTIONS
, FIRST_LOAD_TIME
, PARSE_CALLS
, DISK_READS
, BUFFER_GETS
, USER_IO_WAIT_TIME
, ROWS_PROCESSED
, OPTIMIZER_MODE
, OPTIMIZER_COST
, HASH_VALUE
, PLAN_HASH_VALUE
, CHILD_NUMBER
, CPU_TIME
, ELAPSED_TIME
, IO_INTERCONNECT_BYTES
, PHYSICAL_READ_REQUESTS
, PHYSICAL_READ_BYTES
from v$sql t
where upper(sql_text) like upper('%FROM SV_ACC_REG%LEFT JOIN GEN_PRODUCT ON SV_ACC_REG.PRODUCT_ID=GEN_PRODUCT.PRODUCT_NO%')
order by executions desc
有人能告诉我索引表的最佳方法是什么吗
获得最佳性能
V_sql VARCHAR2(10000):='SELECT
SV_ACC_REG.ACC_REG_ID AS ACC_REG_ID ,
SV_ACC_REG.PRODUCT_ID AS PRODUCT_ID ,
GEN_PRODUCT.FULL_NAME AS PRODUCT_NAME ,
SV_ACC_REG.STATUS AS STATUS ,
SV_ACC_REG.OPENING_DATE AS OPENING_DATE ,
SV_ACC_REG.CURRENT_BALANCE AS CURRENT_BALANCE ,
SV_ACC_REG.CLOSING_DATE AS CLOSING_DATE ,
SV_ACC_REG.REG_NO AS REG_NO ,
SV_ACC_REG.IS_WITHDRAW_BY_SINGLE AS IS_WITHDRAW_BY_SINGLE,
SV_ACC_REG.IS_SINGLE AS IS_SINGLE ,
SV_ACC_REG.IS_EXTENDABLE AS IS_EXTENDABLE ,
SV_ACC_REG.REMARKS AS REMARKS ,
SV_ACC_REG.PR_NO AS PR_NO ,
SV_ACC_REG.CREATED_ON AS CREATED_ON ,
SV_ACC_REG.CREATED_BY AS CREATED_BY ,
SV_ACC_REG.UPDATED_ON AS UPDATED_ON ,
SV_ACC_REG.UPDATED_BY AS UPDATED_BY ,
SV_ACC_REG.IS_DELETED AS IS_DELETED ,
SV_ACC_REG.DELETED_ON AS DELETED_ON ,
SV_ACC_REG.DELETED_BY AS DELETED_BY ,
SV_ACC_REG.CLIENT_TYPE AS CLIENT_TYPE ,
SV_ACC_REG.IS_TRANSFER AS IS_TRANSFER ,
SV_ACC_REG.WITHDRAW_TYPE AS WITHDRAW_TYPE ,
SV_ACC_REG.DEATH_DATE AS DEATH_DATE ,
SV_ACC_REG.IS_MIGRATE AS IS_MIGRATE ,
SV_ACC_REG.MIGRATE_COMMENTS AS MIGRATE_COMMENTS ,
SV_ACC_REG.CHEQUE_HONOR_DATE AS CHEQUE_HONOR_DATE ,
SV_ACC_REG.SO_NO AS SO_NO ,
SV_ACC_REG.IS_MINOR AS IS_MINOR ,
SV_ACC_REG.NAME AS NAME ,
SV_ACC_REG.IS_OLD AS IS_OLD ,
SV_ACC_REG.IS_NO_PROFIT_CALC AS IS_NO_PROFIT_CALC ,
SV_ACC_REG.IS_SIX_M_PROFIT_CALC AS IS_SIX_M_PROFIT_CALC ,
SV_ACC_REG.IS_SEND_DPMG ,
SV_CUSTOMER_INFO.CUSTOMER_NAME AS CUSTOMER_NAME
FROM SV_ACC_REG
LEFT JOIN GEN_PRODUCT ON SV_ACC_REG.PRODUCT_ID=GEN_PRODUCT.PRODUCT_NO
LEFT JOIN SV_CUSTOMER_INFO ON SV_ACC_REG.ACC_REG_ID = SV_CUSTOMER_INFO.ACC_REG_ID';
V_WHERE VARCHAR2(500):=' WHERE ';
BEGIN
BEGIN
V_WHERE:=' WHERE ';
IF p_ACC_REG_ID IS NOT NULL THEN
V_WHERE := V_WHERE || ' SV_ACC_REG.ACC_REG_ID = '||p_ACC_REG_ID||' AND';
END IF;
IF p_PRODUCT_ID IS NOT NULL THEN
V_WHERE := V_WHERE || ' SV_ACC_REG.PRODUCT_ID = '||p_PRODUCT_ID||' AND';
END IF;
IF p_STATUS IS NOT NULL THEN
V_WHERE := V_WHERE || ' SV_ACC_REG.STATUS = '||p_STATUS||' AND';
END IF;
IF p_IS_TRANSFER IS NOT NULL THEN
V_WHERE := V_WHERE || ' SV_ACC_REG.IS_TRANSFER = '||p_IS_TRANSFER||' AND';
END IF;
IF p_SO_NO IS NOT NULL THEN
V_WHERE := V_WHERE || ' SV_ACC_REG.SO_NO = '||p_SO_NO||' AND';
END IF;
IF p_IS_OLD IS NOT NULL THEN
V_WHERE := V_WHERE || ' SV_ACC_REG.IS_OLD = '||p_IS_OLD||' AND';
END IF;
IF p_IS_SEND_DPMG IS NOT NULL THEN
V_WHERE := V_WHERE || ' SV_ACC_REG.IS_SEND_DPMG = '||p_IS_SEND_DPMG||' AND';
END IF;
IF p_IS_SIX_M_PROFIT_CALC IS NOT NULL THEN
V_WHERE := V_WHERE || ' IS_SIX_M_PROFIT_CALC= '||p_IS_SEND_DPMG||' AND';
END IF;
IF LENGTH(' WHERE ') =7 THEN
V_sql :=V_sql ||' ORDER BY SV_ACC_REG.ACC_REG_ID ASC';
ELSE
V_sql :=V_sql || SUBSTR(V_WHERE, 1, LENGTH(V_WHERE) - 3) ||' ORDER BY SV_ACC_REG.ACC_REG_ID ASC';
END IF;
--V_sql :=SUBSTR(V_sql, 1, LENGTH(V_sql) - 3);
--OPEN cur_OUT FOR V_sql USING p_ACC_REG_ID, p_PRODUCT_ID,p_STATUS,p_IS_TRANSFER,p_SO_NO,p_IS_OLD,p_IS_SEND_DPMG,p_IS_SIX_M_PROFIT_CALC;
OPEN cur_OUT FOR V_sql ;
END;
END;
好的,这个查询中有10个可选参数,null/notnull的一些可能组合是2^10=1024,因此您可以得到这个查询的1000个变体,每个变体可能需要一组不同的索引。这里很难给出合理的暗示。
在你的情况下,我会怎么做:
将应用程序部署到生产环境中
允许用户每周使用应用程序几天
登录数据库并运行以下查询您必须拥有DBA授予的相应权限
选择最有问题的查询,对其进行调优,然后在几天/几周/几个月后反复重复。
此查询将提取有关最常用的查询以及消耗资源最多的查询的基本统计信息。这里有很多统计数据,如执行情况、总运行时间、缓冲区获取等,这些数据将为您提供应用程序操作、用户行为等的总体情况,并允许您选择最差的查询进行进一步分析。
您可以进一步查询v$sql\u plan,以获取rdbms use sql\u id和plan\u hash\u value列使用的执行计划,以便对它们进行分析
select
SQL_TEXT
, SQL_FULLTEXT
, SQL_ID
, FETCHES
, EXECUTIONS
, FIRST_LOAD_TIME
, PARSE_CALLS
, DISK_READS
, BUFFER_GETS
, USER_IO_WAIT_TIME
, ROWS_PROCESSED
, OPTIMIZER_MODE
, OPTIMIZER_COST
, HASH_VALUE
, PLAN_HASH_VALUE
, CHILD_NUMBER
, CPU_TIME
, ELAPSED_TIME
, IO_INTERCONNECT_BYTES
, PHYSICAL_READ_REQUESTS
, PHYSICAL_READ_BYTES
from v$sql t
where upper(sql_text) like upper('%FROM SV_ACC_REG%LEFT JOIN GEN_PRODUCT ON SV_ACC_REG.PRODUCT_ID=GEN_PRODUCT.PRODUCT_NO%')
order by executions desc
这需要一些工作,但您可能不需要非常复杂的索引结构 很长一段时间以来,Oracle一直支持对索引进行跳过扫描,此外还支持完整索引扫描和范围扫描。这可能非常强大,但尚不清楚需要哪些确切的索引
是一篇很好的博客文章,解释了什么是跳过扫描。您可能希望先放置一些低基数列,看看它对数据是否有效。这需要一些工作,但您可能不需要非常复杂的索引结构 很长一段时间以来,Oracle一直支持对索引进行跳过扫描,此外还支持完整索引扫描和范围扫描。这可能非常强大,但尚不清楚需要哪些确切的索引
是一篇很好的博客文章,解释了什么是跳过扫描。您可能希望先放置一些低基数列,看看它是否对您的数据有效。由于SQL注入的漏洞,我建议这样的解决方案
V_sql VARCHAR2(10000):='SELECT
SV_ACC_REG.ACC_REG_ID AS ACC_REG_ID ,
SV_ACC_REG.PRODUCT_ID AS PRODUCT_ID ,
GEN_PRODUCT.FULL_NAME AS PRODUCT_NAME ,
...
SV_ACC_REG.IS_OLD AS IS_OLD ,
SV_ACC_REG.IS_NO_PROFIT_CALC AS IS_NO_PROFIT_CALC ,
SV_ACC_REG.IS_SIX_M_PROFIT_CALC AS IS_SIX_M_PROFIT_CALC ,
SV_ACC_REG.IS_SEND_DPMG ,
SV_CUSTOMER_INFO.CUSTOMER_NAME AS CUSTOMER_NAME
FROM SV_ACC_REG
LEFT JOIN GEN_PRODUCT ON SV_ACC_REG.PRODUCT_ID=GEN_PRODUCT.PRODUCT_NO
LEFT JOIN SV_CUSTOMER_INFO ON SV_ACC_REG.ACC_REG_ID = SV_CUSTOMER_INFO.ACC_REG_ID';
V_WHERE VARCHAR2(500);
cur INTEGER := DBMS_SQL.OPEN_CURSOR;
curRef SYS_REFCURSOR;
ret INTEGER;
BEGIN
IF p_ACC_REG_ID IS NOT NULL THEN
V_WHERE := V_WHERE || ' AND SV_ACC_REG.ACC_REG_ID = :p_ACC_REG_ID';
END IF;
IF p_PRODUCT_ID IS NOT NULL THEN
V_WHERE := V_WHERE || ' AND SV_ACC_REG.PRODUCT_ID = :p_PRODUCT_ID';
END IF;
IF p_STATUS IS NOT NULL THEN
V_WHERE := V_WHERE || ' AND SV_ACC_REG.STATUS = :p_STATUS';
END IF;
IF p_IS_TRANSFER IS NOT NULL THEN
V_WHERE := V_WHERE || ' AND SV_ACC_REG.IS_TRANSFER = :p_IS_TRANSFER';
END IF;
IF p_SO_NO IS NOT NULL THEN
V_WHERE := V_WHERE || ' AND SV_ACC_REG.SO_NO = :p_SO_NO';
END IF;
IF p_IS_OLD IS NOT NULL THEN
V_WHERE := V_WHERE || ' AND SV_ACC_REG.IS_OLD = :p_IS_OLD';
END IF;
IF p_IS_SEND_DPMG IS NOT NULL THEN
V_WHERE := V_WHERE || ' AND SV_ACC_REG.IS_SEND_DPMG = :p_IS_SEND_DPMG';
END IF;
IF p_IS_SIX_M_PROFIT_CALC IS NOT NULL THEN
V_WHERE := V_WHERE || ' AND IS_SIX_M_PROFIT_CALC= :p_IS_SIX_M_PROFIT_CALC';
END IF;
V_WHERE := REGEXP_REPLACE(V_WHERE, '^ AND', 'WHERE');
V_sql := V_sql || V_WHERE ||' ORDER BY SV_ACC_REG.ACC_REG_ID ASC';
DBMS_SQL.PARSE(cur, V_sql, DBMS_SQL.NATIVE);
IF p_ACC_REG_ID IS NOT NULL THEN
DBMS_SQL.BIND_VARIABLE(cur, ':p_ACC_REG_ID', p_ACC_REG_ID);
END IF;
IF p_PRODUCT_ID IS NOT NULL THEN
DBMS_SQL.BIND_VARIABLE(cur, ':p_PRODUCT_ID', p_PRODUCT_ID);
END IF;
IF p_STATUS IS NOT NULL THEN
DBMS_SQL.BIND_VARIABLE(cur, ':p_STATUS', p_STATUS);
END IF;
IF p_IS_TRANSFER IS NOT NULL THEN
DBMS_SQL.BIND_VARIABLE(cur, ':p_IS_TRANSFER', p_IS_TRANSFER);
END IF;
IF p_SO_NO IS NOT NULL THEN
DBMS_SQL.BIND_VARIABLE(cur, ':p_SO_NO', p_SO_NO);
END IF;
IF p_IS_OLD IS NOT NULL THEN
DBMS_SQL.BIND_VARIABLE(cur, ':p_IS_OLD', p_IS_OLD);
END IF;
IF p_IS_SEND_DPMG IS NOT NULL THEN
DBMS_SQL.BIND_VARIABLE(cur, ':IS_SEND_DPMG', IS_SEND_DPMG);
END IF;
IF p_IS_SIX_M_PROFIT_CALC IS NOT NULL THEN
DBMS_SQL.BIND_VARIABLE(cur, ':p_IS_SIX_M_PROFIT_CALC', p_IS_SIX_M_PROFIT_CALC );
END IF;
ret := DBMS_SQL.EXECUTE(cur);
curRef := DBMS_SQL.TO_REFCURSOR(cur);
END;
关于性能,我建议在WHERE条件下为每列创建单独的索引,即每个索引一列。Oracle能够组合索引,请参见示例,但是除非您通过INDEX_JOIN提示强制组合索引,否则这种情况可能很少发生。通常,Oracle只采用最具选择性的索引。例如,如果SV_ACC_REG.PRODUCT_ID=12345的结果仅返回几行,则其他条件/索引在性能方面不再重要
<>对于经常使用的组合,您可以考虑专用的复合索引。
列SV_ACC_REG.STATUS、SV_ACC_REG.IS_SEND_DPMG、SV_ACC_REG.IS_TRANSFER、SV_ACC_REG.IS_OLD、IS_SIX_M_price_CALC似乎基数很低,我假设它们只包含Yes和No值或类似值。考虑使用这些列。位图索引实际上是要相互组合的,这就是
他们如何最有效地工作
但是,位图索引不适用于OLTP应用程序,即当表数据经常更改时,不应使用它们删除、插入、更新。如果这些更改由多个会话同时执行,则情况会变得更糟
该功能应该可以帮助您检测无用的索引。由于SQL注入的漏洞,我建议这样的解决方案
V_sql VARCHAR2(10000):='SELECT
SV_ACC_REG.ACC_REG_ID AS ACC_REG_ID ,
SV_ACC_REG.PRODUCT_ID AS PRODUCT_ID ,
GEN_PRODUCT.FULL_NAME AS PRODUCT_NAME ,
...
SV_ACC_REG.IS_OLD AS IS_OLD ,
SV_ACC_REG.IS_NO_PROFIT_CALC AS IS_NO_PROFIT_CALC ,
SV_ACC_REG.IS_SIX_M_PROFIT_CALC AS IS_SIX_M_PROFIT_CALC ,
SV_ACC_REG.IS_SEND_DPMG ,
SV_CUSTOMER_INFO.CUSTOMER_NAME AS CUSTOMER_NAME
FROM SV_ACC_REG
LEFT JOIN GEN_PRODUCT ON SV_ACC_REG.PRODUCT_ID=GEN_PRODUCT.PRODUCT_NO
LEFT JOIN SV_CUSTOMER_INFO ON SV_ACC_REG.ACC_REG_ID = SV_CUSTOMER_INFO.ACC_REG_ID';
V_WHERE VARCHAR2(500);
cur INTEGER := DBMS_SQL.OPEN_CURSOR;
curRef SYS_REFCURSOR;
ret INTEGER;
BEGIN
IF p_ACC_REG_ID IS NOT NULL THEN
V_WHERE := V_WHERE || ' AND SV_ACC_REG.ACC_REG_ID = :p_ACC_REG_ID';
END IF;
IF p_PRODUCT_ID IS NOT NULL THEN
V_WHERE := V_WHERE || ' AND SV_ACC_REG.PRODUCT_ID = :p_PRODUCT_ID';
END IF;
IF p_STATUS IS NOT NULL THEN
V_WHERE := V_WHERE || ' AND SV_ACC_REG.STATUS = :p_STATUS';
END IF;
IF p_IS_TRANSFER IS NOT NULL THEN
V_WHERE := V_WHERE || ' AND SV_ACC_REG.IS_TRANSFER = :p_IS_TRANSFER';
END IF;
IF p_SO_NO IS NOT NULL THEN
V_WHERE := V_WHERE || ' AND SV_ACC_REG.SO_NO = :p_SO_NO';
END IF;
IF p_IS_OLD IS NOT NULL THEN
V_WHERE := V_WHERE || ' AND SV_ACC_REG.IS_OLD = :p_IS_OLD';
END IF;
IF p_IS_SEND_DPMG IS NOT NULL THEN
V_WHERE := V_WHERE || ' AND SV_ACC_REG.IS_SEND_DPMG = :p_IS_SEND_DPMG';
END IF;
IF p_IS_SIX_M_PROFIT_CALC IS NOT NULL THEN
V_WHERE := V_WHERE || ' AND IS_SIX_M_PROFIT_CALC= :p_IS_SIX_M_PROFIT_CALC';
END IF;
V_WHERE := REGEXP_REPLACE(V_WHERE, '^ AND', 'WHERE');
V_sql := V_sql || V_WHERE ||' ORDER BY SV_ACC_REG.ACC_REG_ID ASC';
DBMS_SQL.PARSE(cur, V_sql, DBMS_SQL.NATIVE);
IF p_ACC_REG_ID IS NOT NULL THEN
DBMS_SQL.BIND_VARIABLE(cur, ':p_ACC_REG_ID', p_ACC_REG_ID);
END IF;
IF p_PRODUCT_ID IS NOT NULL THEN
DBMS_SQL.BIND_VARIABLE(cur, ':p_PRODUCT_ID', p_PRODUCT_ID);
END IF;
IF p_STATUS IS NOT NULL THEN
DBMS_SQL.BIND_VARIABLE(cur, ':p_STATUS', p_STATUS);
END IF;
IF p_IS_TRANSFER IS NOT NULL THEN
DBMS_SQL.BIND_VARIABLE(cur, ':p_IS_TRANSFER', p_IS_TRANSFER);
END IF;
IF p_SO_NO IS NOT NULL THEN
DBMS_SQL.BIND_VARIABLE(cur, ':p_SO_NO', p_SO_NO);
END IF;
IF p_IS_OLD IS NOT NULL THEN
DBMS_SQL.BIND_VARIABLE(cur, ':p_IS_OLD', p_IS_OLD);
END IF;
IF p_IS_SEND_DPMG IS NOT NULL THEN
DBMS_SQL.BIND_VARIABLE(cur, ':IS_SEND_DPMG', IS_SEND_DPMG);
END IF;
IF p_IS_SIX_M_PROFIT_CALC IS NOT NULL THEN
DBMS_SQL.BIND_VARIABLE(cur, ':p_IS_SIX_M_PROFIT_CALC', p_IS_SIX_M_PROFIT_CALC );
END IF;
ret := DBMS_SQL.EXECUTE(cur);
curRef := DBMS_SQL.TO_REFCURSOR(cur);
END;
关于性能,我建议在WHERE条件下为每列创建单独的索引,即每个索引一列。Oracle能够组合索引,请参见示例,但是除非您通过INDEX_JOIN提示强制组合索引,否则这种情况可能很少发生。通常,Oracle只采用最具选择性的索引。例如,如果SV_ACC_REG.PRODUCT_ID=12345的结果仅返回几行,则其他条件/索引在性能方面不再重要
<>对于经常使用的组合,您可以考虑专用的复合索引。
列SV_ACC_REG.STATUS、SV_ACC_REG.IS_SEND_DPMG、SV_ACC_REG.IS_TRANSFER、SV_ACC_REG.IS_OLD、IS_SIX_M_price_CALC似乎基数很低,我假设它们只包含Yes和No值或类似值。考虑使用这些列。位图索引实际上是为了相互结合,这是它们最有效的工作方式
但是,位图索引不适用于OLTP应用程序,即当表数据经常更改时,不应使用它们删除、插入、更新。如果这些更改由多个会话同时执行,则情况会变得更糟
该功能应该可以帮助您检测无用的索引。通过实验,我通过这种技术获得了更好的性能。请忽略SQL注入问题。我以后再修。如果有人有更好的想法,请加上这个
IF p_REG_NO IS NOT NULL THEN
V_WHERE := V_WHERE || ' SV_ACC_REG.REG_NO ='''||p_REG_NO||''' AND';
END IF;
IF p_PRODUCT_ID IS NULL THEN
V_WHERE := V_WHERE || ' SV_ACC_REG.PRODUCT_ID IN(1,2,3,4,5,6) AND';
ELSE
V_WHERE := V_WHERE || ' SV_ACC_REG.PRODUCT_ID ='||p_PRODUCT_ID||' AND';
END IF;
IF p_STATUS IS NULL THEN
V_WHERE := V_WHERE || ' SV_ACC_REG.STATUS IN (0,1) AND';
ELSE
V_WHERE := V_WHERE || ' SV_ACC_REG.STATUS ='||p_STATUS||' AND';
END IF;
IF p_IS_TRANSFER IS NULL THEN
V_WHERE := V_WHERE || ' SV_ACC_REG.IS_TRANSFER IN(0,1) AND';
ELSE
V_WHERE := V_WHERE || ' SV_ACC_REG.IS_TRANSFER ='||p_IS_TRANSFER||' AND';
END IF;
IF p_SO_NO IS NULL THEN
BEGIN
--Select listagg(SO_NO,', ') within group(order by SO_NO) INTO V_List from GEN_SO;
V_WHERE := V_WHERE || ' SV_ACC_REG.SO_NO> 0 AND';
END;
ELSE
V_WHERE := V_WHERE || ' SV_ACC_REG.SO_NO ='||p_SO_NO||' AND';
END IF;
IF p_IS_OLD IS NULL THEN
V_WHERE := V_WHERE || ' SV_ACC_REG.IS_OLD IN (0,1) AND';
ELSE
V_WHERE := V_WHERE || ' SV_ACC_REG.IS_OLD ='||p_IS_OLD||' AND';
END IF;
IF p_IS_SEND_DPMG IS NULL THEN
V_WHERE := V_WHERE || ' SV_ACC_REG.IS_SEND_DPMG IN(0,1) AND';
ELSE
V_WHERE := V_WHERE || ' SV_ACC_REG.IS_SEND_DPMG ='||p_IS_SEND_DPMG||' AND';
END IF;
IF p_IS_SIX_M_PROFIT_CALC IS NULL THEN
V_WHERE := V_WHERE || ' SV_ACC_REG.IS_SIX_M_PROFIT_CALC IN(0,1) ';
ELSE
V_WHERE := V_WHERE || ' SV_ACC_REG.IS_SIX_M_PROFIT_CALC='||p_IS_SIX_M_PROFIT_CALC||' ';
END IF;
通过实验,我通过这种技术获得了更好的性能。请忽略SQL注入问题。我以后再修。如果有人有更好的想法,请加上这个
IF p_REG_NO IS NOT NULL THEN
V_WHERE := V_WHERE || ' SV_ACC_REG.REG_NO ='''||p_REG_NO||''' AND';
END IF;
IF p_PRODUCT_ID IS NULL THEN
V_WHERE := V_WHERE || ' SV_ACC_REG.PRODUCT_ID IN(1,2,3,4,5,6) AND';
ELSE
V_WHERE := V_WHERE || ' SV_ACC_REG.PRODUCT_ID ='||p_PRODUCT_ID||' AND';
END IF;
IF p_STATUS IS NULL THEN
V_WHERE := V_WHERE || ' SV_ACC_REG.STATUS IN (0,1) AND';
ELSE
V_WHERE := V_WHERE || ' SV_ACC_REG.STATUS ='||p_STATUS||' AND';
END IF;
IF p_IS_TRANSFER IS NULL THEN
V_WHERE := V_WHERE || ' SV_ACC_REG.IS_TRANSFER IN(0,1) AND';
ELSE
V_WHERE := V_WHERE || ' SV_ACC_REG.IS_TRANSFER ='||p_IS_TRANSFER||' AND';
END IF;
IF p_SO_NO IS NULL THEN
BEGIN
--Select listagg(SO_NO,', ') within group(order by SO_NO) INTO V_List from GEN_SO;
V_WHERE := V_WHERE || ' SV_ACC_REG.SO_NO> 0 AND';
END;
ELSE
V_WHERE := V_WHERE || ' SV_ACC_REG.SO_NO ='||p_SO_NO||' AND';
END IF;
IF p_IS_OLD IS NULL THEN
V_WHERE := V_WHERE || ' SV_ACC_REG.IS_OLD IN (0,1) AND';
ELSE
V_WHERE := V_WHERE || ' SV_ACC_REG.IS_OLD ='||p_IS_OLD||' AND';
END IF;
IF p_IS_SEND_DPMG IS NULL THEN
V_WHERE := V_WHERE || ' SV_ACC_REG.IS_SEND_DPMG IN(0,1) AND';
ELSE
V_WHERE := V_WHERE || ' SV_ACC_REG.IS_SEND_DPMG ='||p_IS_SEND_DPMG||' AND';
END IF;
IF p_IS_SIX_M_PROFIT_CALC IS NULL THEN
V_WHERE := V_WHERE || ' SV_ACC_REG.IS_SIX_M_PROFIT_CALC IN(0,1) ';
ELSE
V_WHERE := V_WHERE || ' SV_ACC_REG.IS_SIX_M_PROFIT_CALC='||p_IS_SIX_M_PROFIT_CALC||' ';
END IF;
这里的标准方法是计算出将出现哪些列的组合,然后为每个组合构建串联索引,并避免每列有一个索引。或者,我在这方面的第一个尝试是识别标题列,希望在任何给定的组合中只出现一个标题列并对它们进行索引,然后通过向这些索引中添加辅助列来微调这些索引。WHERE子句中的列的基数是多少?我假设列是_TRANSFER或STATUS的值非常少,很可能只有2行。这个表中有多少行?@APC在SV_ACC_REG表中大约560万行。这里的标准方法是计算出将出现哪些列组合,然后为每个组合构建串联索引,并避免每列有1个索引。或者,我在这方面的第一个尝试是识别标题列,希望在任何给定的组合中只出现一个标题列并对它们进行索引,然后通过向这些索引中添加辅助列来微调这些索引。WHERE子句中的列的基数是多少?我假设列是“传输”或“状态”的值非常少,很可能只有2行。此表中有多少行?@APC在SV\u ACC\u REG表中大约560万行。可能的组合数不是2^10吗?可能的组合数不是2^10吗?