慎用distinct

使用distinct和不使用distinct到底可以造成多大损耗呢

一个稍微复杂一点的SQL,连接了5张表



--------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                              | Name                          | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                       |                               |  6685 |  4119K|       | 46354   (1)| 00:09:17 |
|*  1 |  FILTER                                |                               |       |       |       |            |          |
|   2 |   TABLE ACCESS BY INDEX ROWID          | PSP_GROUP_CAT_MASTER          |     1 |    14 |       |     2   (0)| 00:00:01 |
|*  3 |    INDEX RANGE SCAN                    | PSP_GROUP_CAT_MASTER_PK       |     1 |       |       |     1   (0)| 00:00:01 |
|*  4 |  FILTER                                |                               |       |       |       |            |          |
|   5 |   TABLE ACCESS BY INDEX ROWID          | PSP_GROUP_CAT_MASTER          |     1 |    14 |       |     2   (0)| 00:00:01 |
|*  6 |    INDEX RANGE SCAN                    | PSP_GROUP_CAT_MASTER_PK       |     1 |       |       |     1   (0)| 00:00:01 |
|   7 |  SORT UNIQUE                           |                               |  6685 |  4119K|  4472K| 45461   (1)| 00:09:06 |
|*  8 |   COUNT STOPKEY                        |                               |       |       |       |            |          |
|*  9 |    FILTER                              |                               |       |       |       |            |          |
|* 10 |     HASH JOIN RIGHT OUTER              |                               |  6685 |  4119K|       | 43424   (1)| 00:08:42 |
|  11 |      TABLE ACCESS FULL                 | PSP_IND_CAT_MASTER            |    16 |   304 |       |     3   (0)| 00:00:01 |
|* 12 |      FILTER                            |                               |       |       |       |            |          |
|* 13 |       HASH JOIN RIGHT OUTER            |                               | 15280 |  9132K|       | 43420   (1)| 00:08:42 |
|  14 |        TABLE ACCESS FULL               | PSP_IND_CAT_MASTER            |    16 |   304 |       |     3   (0)| 00:00:01 |
|* 15 |        HASH JOIN RIGHT OUTER           |                               | 34925 |    19M|       | 43417   (1)| 00:08:42 |
|  16 |         TABLE ACCESS FULL              | PSP_CORE_CSIS                 | 41677 |   447K|       |   144   (0)| 00:00:02 |
|* 17 |         HASH JOIN OUTER                |                               | 34925 |    19M|  8736K| 43272   (1)| 00:08:40 |
|* 18 |          FILTER                        |                               |       |       |       |            |          |
|* 19 |           HASH JOIN RIGHT OUTER        |                               | 34925 |  8321K|  2472K| 39196   (1)| 00:07:51 |
|  20 |            TABLE ACCESS FULL           | PSP_EXISTED_HR_CATEGORY_LIST  | 84261 |  1481K|       |   585   (1)| 00:00:08 |
|  21 |            VIEW                        |                               |   105K|    22M|       | 37303   (1)| 00:07:28 |
|* 22 |             FILTER                     |                               |       |       |       |            |          |
|* 23 |              HASH JOIN OUTER           |                               |   105K|    27M|    19M| 37303   (1)| 00:07:28 |
|* 24 |               HASH JOIN RIGHT OUTER    |                               |   120K|    18M|  4136K| 32231   (1)| 00:06:27 |
|  25 |                TABLE ACCESS FULL       | PSP_CORE_ORG_LEVEL            | 98360 |  2977K|       |  2023   (1)| 00:00:25 |
|* 26 |                HASH JOIN RIGHT OUTER   |                               |   120K|    15M|  4712K| 29192   (1)| 00:05:51 |
|  27 |                 TABLE ACCESS FULL      | PSP_CORE_ORG                  | 98360 |  3554K|       |   308   (1)| 00:00:04 |
|* 28 |                 HASH JOIN OUTER        |                               |   120K|    10M|    10M| 28051   (1)| 00:05:37 |
|* 29 |                  FILTER                |                               |       |       |       |            |          |
|* 30 |                   HASH JOIN RIGHT OUTER|                               |   120K|  9269K|  3520K| 14464   (1)| 00:02:54 |
|  31 |                    TABLE ACCESS FULL   | PSP_EXISTED_GOC_CATEGORY_LIST |   116K|  2155K|       |  1170   (1)| 00:00:15 |
|* 32 |                    TABLE ACCESS FULL   | PSP_CORE_GDW                  |   360K|    20M|       | 11891   (1)| 00:02:23 |
|  33 |                  TABLE ACCESS FULL     | PSP_EMDD_FEED_TB              |   477K|  6995K|       | 12455   (1)| 00:02:30 |
|  34 |               TABLE ACCESS FULL        | PSP_EXISTED_CATEGORY_LIST     |   268K|    29M|       |  2466   (1)| 00:00:30 |
|  35 |          TABLE ACCESS FULL             | PSP_CORE_ORG_LEVEL            | 98360 |    31M|       |  2023   (1)| 00:00:25 |

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------

 

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                                          | Name                          | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |    TQ  |IN-OUT| PQ Distrib |
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                                   |                               |  6685 |    10M|       | 13806   (1)| 00:02:46 |        |      |            |
|*  1 |  FILTER                                            |                               |       |       |       |            |          |        |      |            |
|   2 |   TABLE ACCESS BY INDEX ROWID                      | PSP_GROUP_CAT_MASTER          |     1 |    14 |       |     2   (0)| 00:00:01 |        |      |            |
|*  3 |    INDEX RANGE SCAN                                | PSP_GROUP_CAT_MASTER_PK       |     1 |       |       |     1   (0)| 00:00:01 |        |      |            |
|*  4 |  FILTER                                            |                               |       |       |       |            |          |        |      |            |
|   5 |   TABLE ACCESS BY INDEX ROWID                      | PSP_GROUP_CAT_MASTER          |     1 |    14 |       |     2   (0)| 00:00:01 |        |      |            |
|*  6 |    INDEX RANGE SCAN                                | PSP_GROUP_CAT_MASTER_PK       |     1 |       |       |     1   (0)| 00:00:01 |        |      |            |
|   7 |  SORT ORDER BY                                     |                               |  6685 |    10M|    29M| 13806   (1)| 00:02:46 |        |      |            |
|*  8 |   COUNT STOPKEY                                    |                               |       |       |       |            |          |        |      |            |
|   9 |    PX COORDINATOR                                  |                               |       |       |       |            |          |        |      |            |
|  10 |     PX SEND QC (RANDOM)                            | :TQ10014                      |  6685 |    10M|       | 13805   (1)| 00:02:46 |  Q1,14 | P->S | QC (RAND)  |
|  11 |      BUFFER SORT                                   |                               |  6685 |    10M|       |            |          |  Q1,14 | PCWP |            |
|* 12 |       COUNT STOPKEY                                |                               |       |       |       |            |          |  Q1,14 | PCWC |            |
|* 13 |        FILTER                                      |                               |       |       |       |            |          |  Q1,14 | PCWC |            |
|* 14 |         HASH JOIN RIGHT OUTER                      |                               |  6685 |    10M|       | 13805   (1)| 00:02:46 |  Q1,14 | PCWP |            |
|  15 |          BUFFER SORT                               |                               |       |       |       |            |          |  Q1,14 | PCWC |            |
|  16 |           PX RECEIVE                               |                               |    16 |   304 |       |     3   (0)| 00:00:01 |  Q1,14 | PCWP |            |
|  17 |            PX SEND BROADCAST                       | :TQ10004                      |    16 |   304 |       |     3   (0)| 00:00:01 |        | S->P | BROADCAST  |
|  18 |             TABLE ACCESS FULL                      | PSP_IND_CAT_MASTER            |    16 |   304 |       |     3   (0)| 00:00:01 |        |      |            |
|* 19 |          FILTER                                    |                               |       |       |       |            |          |  Q1,14 | PCWC |            |
|* 20 |           HASH JOIN RIGHT OUTER                    |                               | 15280 |    24M|       | 13801   (1)| 00:02:46 |  Q1,14 | PCWP |            |
|  21 |            BUFFER SORT                             |                               |       |       |       |            |          |  Q1,14 | PCWC |            |
|  22 |             PX RECEIVE                             |                               |    16 |   304 |       |     3   (0)| 00:00:01 |  Q1,14 | PCWP |            |
|  23 |              PX SEND BROADCAST                     | :TQ10005                      |    16 |   304 |       |     3   (0)| 00:00:01 |        | S->P | BROADCAST  |
|  24 |               TABLE ACCESS FULL                    | PSP_IND_CAT_MASTER            |    16 |   304 |       |     3   (0)| 00:00:01 |        |      |            |
|* 25 |            HASH JOIN RIGHT OUTER                   |                               | 34925 |    54M|       | 13798   (1)| 00:02:46 |  Q1,14 | PCWP |            |
|  26 |             BUFFER SORT                            |                               |       |       |       |            |          |  Q1,14 | PCWC |            |
|  27 |              PX RECEIVE                            |                               | 98360 |    31M|       |  2023   (1)| 00:00:25 |  Q1,14 | PCWP |            |
|  28 |               PX SEND HASH                         | :TQ10006                      | 98360 |    31M|       |  2023   (1)| 00:00:25 |        | S->P | HASH       |
|  29 |                TABLE ACCESS FULL                   | PSP_CORE_ORG_LEVEL            | 98360 |    31M|       |  2023   (1)| 00:00:25 |        |      |            |
|  30 |             PX RECEIVE                             |                               | 34925 |    43M|       | 11774   (1)| 00:02:22 |  Q1,14 | PCWP |            |
|  31 |              PX SEND HASH                          | :TQ10013                      | 34925 |    43M|       | 11774   (1)| 00:02:22 |  Q1,13 | P->P | HASH       |
|* 32 |               HASH JOIN RIGHT OUTER BUFFERED       |                               | 34925 |    43M|       | 11774   (1)| 00:02:22 |  Q1,13 | PCWP |            |
|  33 |                BUFFER SORT                         |                               |       |       |       |            |          |  Q1,13 | PCWC |            |
|  34 |                 PX RECEIVE                         |                               | 41677 |   447K|       |   144   (0)| 00:00:02 |  Q1,13 | PCWP |            |
|  35 |                  PX SEND BROADCAST                 | :TQ10002                      | 41677 |   447K|       |   144   (0)| 00:00:02 |        | S->P | BROADCAST  |
|  36 |                   TABLE ACCESS FULL                | PSP_CORE_CSIS                 | 41677 |   447K|       |   144   (0)| 00:00:02 |        |      |            |
|* 37 |                FILTER                              |                               |       |       |       |            |          |  Q1,13 | PCWC |            |
|* 38 |                 HASH JOIN RIGHT OUTER              |                               | 34925 |    42M|       | 11629   (1)| 00:02:20 |  Q1,13 | PCWP |            |
|  39 |                  BUFFER SORT                       |                               |       |       |       |            |          |  Q1,13 | PCWC |            |
|  40 |                   PX RECEIVE                       |                               | 84261 |  1481K|       |   585   (1)| 00:00:08 |  Q1,13 | PCWP |            |
|  41 |                    PX SEND BROADCAST               | :TQ10003                      | 84261 |  1481K|       |   585   (1)| 00:00:08 |        | S->P | BROADCAST  |
|  42 |                     TABLE ACCESS FULL              | PSP_EXISTED_HR_CATEGORY_LIST  | 84261 |  1481K|       |   585   (1)| 00:00:08 |        |      |            |
|  43 |                  VIEW                              |                               |   105K|   127M|       | 11044   (1)| 00:02:13 |  Q1,13 | PCWP |            |
|* 44 |                   HASH JOIN RIGHT OUTER            |                               |   105K|    27M|       | 11044   (1)| 00:02:13 |  Q1,13 | PCWP |            |
|  45 |                    PX RECEIVE                      |                               | 98360 |  2977K|       |   561   (1)| 00:00:07 |  Q1,13 | PCWP |            |
|  46 |                     PX SEND HASH                   | :TQ10011                      | 98360 |  2977K|       |   561   (1)| 00:00:07 |  Q1,11 | P->P | HASH       |
|  47 |                      PX BLOCK ITERATOR             |                               | 98360 |  2977K|       |   561   (1)| 00:00:07 |  Q1,11 | PCWC |            |
|  48 |                       TABLE ACCESS FULL            | PSP_CORE_ORG_LEVEL            | 98360 |  2977K|       |   561   (1)| 00:00:07 |  Q1,11 | PCWP |            |
|  49 |                    PX RECEIVE                      |                               |   105K|    24M|       | 10481   (1)| 00:02:06 |  Q1,13 | PCWP |            |
|  50 |                     PX SEND HASH                   | :TQ10012                      |   105K|    24M|       | 10481   (1)| 00:02:06 |  Q1,12 | P->P | HASH       |
|* 51 |                      HASH JOIN RIGHT OUTER BUFFERED|                               |   105K|    24M|       | 10481   (1)| 00:02:06 |  Q1,12 | PCWP |            |
|  52 |                       PX RECEIVE                   |                               | 98360 |  3554K|       |    86   (2)| 00:00:02 |  Q1,12 | PCWP |            |
|  53 |                        PX SEND HASH                | :TQ10009                      | 98360 |  3554K|       |    86   (2)| 00:00:02 |  Q1,09 | P->P | HASH       |
|  54 |                         PX BLOCK ITERATOR          |                               | 98360 |  3554K|       |    86   (2)| 00:00:02 |  Q1,09 | PCWC |            |
|  55 |                          TABLE ACCESS FULL         | PSP_CORE_ORG                  | 98360 |  3554K|       |    86   (2)| 00:00:02 |  Q1,09 | PCWP |            |
|  56 |                       PX RECEIVE                   |                               |   105K|    21M|       | 10395   (1)| 00:02:05 |  Q1,12 | PCWP |            |
|  57 |                        PX SEND HASH                | :TQ10010                      |   105K|    21M|       | 10395   (1)| 00:02:05 |  Q1,10 | P->P | HASH       |
|* 58 |                         FILTER                     |                               |       |       |       |            |          |  Q1,10 | PCWC |            |
|* 59 |                          HASH JOIN OUTER           |                               |   105K|    21M|       | 10395   (1)| 00:02:05 |  Q1,10 | PCWP |            |
|* 60 |                           HASH JOIN OUTER          |                               |   120K|    10M|       |  7928   (1)| 00:01:36 |  Q1,10 | PCWP |            |
|  61 |                            PX RECEIVE              |                               |   120K|  9269K|       |  4471   (1)| 00:00:54 |  Q1,10 | PCWP |            |
|  62 |                             PX SEND HASH           | :TQ10007                      |   120K|  9269K|       |  4471   (1)| 00:00:54 |  Q1,07 | P->P | HASH       |
|* 63 |                              FILTER                |                               |       |       |       |            |          |  Q1,07 | PCWC |            |
|* 64 |                               HASH JOIN RIGHT OUTER|                               |   120K|  9269K|       |  4471   (1)| 00:00:54 |  Q1,07 | PCWP |            |
|  65 |                                BUFFER SORT         |                               |       |       |       |            |          |  Q1,07 | PCWC |            |
|  66 |                                 PX RECEIVE         |                               |   116K|  2155K|       |  1170   (1)| 00:00:15 |  Q1,07 | PCWP |            |
|  67 |                                  PX SEND BROADCAST | :TQ10000                      |   116K|  2155K|       |  1170   (1)| 00:00:15 |        | S->P | BROADCAST  |
|  68 |                                   TABLE ACCESS FULL| PSP_EXISTED_GOC_CATEGORY_LIST |   116K|  2155K|       |  1170   (1)| 00:00:15 |        |      |            |
|  69 |                                PX BLOCK ITERATOR   |                               |   360K|    20M|       |  3300   (1)| 00:00:40 |  Q1,07 | PCWC |            |
|* 70 |                                 TABLE ACCESS FULL  | PSP_CORE_GDW                  |   360K|    20M|       |  3300   (1)| 00:00:40 |  Q1,07 | PCWP |            |
|  71 |                            PX RECEIVE              |                               |   477K|  6995K|       |  3456   (1)| 00:00:42 |  Q1,10 | PCWP |            |
|  72 |                             PX SEND HASH           | :TQ10008                      |   477K|  6995K|       |  3456   (1)| 00:00:42 |  Q1,08 | P->P | HASH       |
|  73 |                              PX BLOCK ITERATOR     |                               |   477K|  6995K|       |  3456   (1)| 00:00:42 |  Q1,08 | PCWC |            |
|  74 |                               TABLE ACCESS FULL    | PSP_EMDD_FEED_TB              |   477K|  6995K|       |  3456   (1)| 00:00:42 |  Q1,08 | PCWP |            |
|  75 |                           BUFFER SORT              |                               |       |       |       |            |          |  Q1,10 | PCWC |            |
|  76 |                            PX RECEIVE              |                               |   268K|    29M|       |  2466   (1)| 00:00:30 |  Q1,10 | PCWP |            |
|  77 |                             PX SEND HASH           | :TQ10001                      |   268K|    29M|       |  2466   (1)| 00:00:30 |        | S->P | HASH       |
|  78 |                              TABLE ACCESS FULL     | PSP_EXISTED_CATEGORY_LIST     |   268K|    29M|       |  2466   (1)| 00:00:30 |        |      |            |
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------

LoggableStatement

import java.io.InputStream;  
import java.io.Reader;  
import java.math.BigDecimal;  
import java.net.URL;  
import java.sql.Array;  
import java.sql.Blob;  
import java.sql.Clob;  
import java.sql.Connection;  
import java.sql.Date;  
import java.sql.NClob;
import java.sql.ParameterMetaData;  
import java.sql.PreparedStatement;  
import java.sql.Ref;  
import java.sql.ResultSet;  
import java.sql.ResultSetMetaData;  
import java.sql.RowId;
import java.sql.SQLException;  
import java.sql.SQLWarning;  
import java.sql.SQLXML;
import java.sql.Time;  
import java.sql.Timestamp;  
import java.util.ArrayList;  
import java.util.Calendar;  

import org.apache.log4j.Logger;


  

public class LoggableStatement implements PreparedStatement {  
    
    private static Logger logger = Logger.getLogger("ria_logger");
  
    private ArrayList parameterValues;  
  
    private String sqlTemplate;  
  
    private PreparedStatement wrappedStatement;  
  
    public LoggableStatement(Connection connection, String sql)  
            throws SQLException {  
        wrappedStatement = connection.prepareStatement(sql);  
        sqlTemplate = sql;  
        parameterValues = new ArrayList();  
    }  
  
    private void saveQueryParamValue(int position, Object obj) {  
        String strValue;  
        if (obj instanceof String || obj instanceof Date) {  
            // if we have a String, include '' in the saved value  
            strValue = "'" + obj + "'";  
        } else {  
            if (obj == null) {  
                strValue = "null";  
            } else {  
                strValue = obj.toString();  
            }  
        }  
        while (position >= parameterValues.size()) {  
  
            parameterValues.add(null);  
        }  
        parameterValues.set(position, strValue);  
    }  
  
    public String getQueryString() {  
        int len = sqlTemplate.length();  
        StringBuffer t = new StringBuffer(len * 2);  
  
        if (parameterValues != null) {  
            int i = 1, limit = 0, base = 0;  
  
            while ((limit = sqlTemplate.indexOf('?', limit)) != -1) {  
                t.append(sqlTemplate.substring(base, limit));  
                t.append(parameterValues.get(i));  
                i++;  
                limit++;  
                base = limit;  
            }  
            if (base < len) {  
                t.append(sqlTemplate.substring(base));  
            }  
        }  
        return t.toString();  
    }  
  
    public void addBatch() throws SQLException {  
        logger.info(getQueryString());
        wrappedStatement.addBatch();  
    }  
  
    public void clearParameters() throws SQLException {  
        wrappedStatement.clearParameters();  
    }  
  
    public boolean execute() throws SQLException {  
        return wrappedStatement.execute();  
    }  
  
    public ResultSet executeQuery() throws SQLException {  
        return wrappedStatement.executeQuery();  
    }  
  
    public int executeUpdate() throws SQLException {  
        return wrappedStatement.executeUpdate();  
    }  
  
    public ResultSetMetaData getMetaData() throws SQLException {  
        return wrappedStatement.getMetaData();  
    }  
  
    public ParameterMetaData getParameterMetaData() throws SQLException {  
        return wrappedStatement.getParameterMetaData();  
    }  
  
    public void setArray(int i, Array x) throws SQLException {  
        wrappedStatement.setArray(i, x);  
        saveQueryParamValue(i, x);  
    }  
  
    public void setAsciiStream(int parameterIndex, InputStream x, int length)  
            throws SQLException {  
        wrappedStatement.setAsciiStream(parameterIndex, x, length);  
        saveQueryParamValue(parameterIndex, x);  
    }  
  
    public void setBigDecimal(int parameterIndex, BigDecimal x)  
            throws SQLException {  
        wrappedStatement.setBigDecimal(parameterIndex, x);  
        saveQueryParamValue(parameterIndex, x);  
    }  
  
    public void setBinaryStream(int parameterIndex, InputStream x, int length)  
            throws SQLException {  
        wrappedStatement.setBinaryStream(parameterIndex, x, length);  
        saveQueryParamValue(parameterIndex, x);  
    }  
  
    public void setBlob(int i, Blob x) throws SQLException {  
        wrappedStatement.setBlob(i, x);  
        saveQueryParamValue(i, x);  
    }  
  
    public void setBoolean(int parameterIndex, boolean x) throws SQLException {  
        wrappedStatement.setBoolean(parameterIndex, x);  
        saveQueryParamValue(parameterIndex, new Boolean(x));  
    }  
  
    public void setByte(int parameterIndex, byte x) throws SQLException {  
        wrappedStatement.setByte(parameterIndex, x);  
        saveQueryParamValue(parameterIndex, new Byte(x));  
    }  
  
    public void setBytes(int parameterIndex, byte[] x) throws SQLException {  
        wrappedStatement.setBytes(parameterIndex, x);  
        saveQueryParamValue(parameterIndex, x);  
    }  
  
    public void setCharacterStream(int parameterIndex, Reader reader, int length)  
            throws SQLException {  
        wrappedStatement.setCharacterStream(parameterIndex, reader, length);  
        saveQueryParamValue(parameterIndex, reader);  
    }  
  
    public void setClob(int i, Clob x) throws SQLException {  
        wrappedStatement.setClob(i, x);  
        saveQueryParamValue(i, x);  
    }  
  
    public void setDate(int parameterIndex, Date x) throws SQLException {  
        wrappedStatement.setDate(parameterIndex, x);  
        saveQueryParamValue(parameterIndex, x);  
    }  
  
    public void setDate(int parameterIndex, Date x, Calendar cal)  
            throws SQLException {  
        wrappedStatement.setDate(parameterIndex, x, cal);  
        saveQueryParamValue(parameterIndex, x);  
    }  
  
    public void setDouble(int parameterIndex, double x) throws SQLException {  
        wrappedStatement.setDouble(parameterIndex, x);  
        saveQueryParamValue(parameterIndex, new Double(x));  
    }  
  
    public void setFloat(int parameterIndex, float x) throws SQLException {  
        wrappedStatement.setFloat(parameterIndex, x);  
        saveQueryParamValue(parameterIndex, new Float(x));  
    }  
  
    public void setInt(int parameterIndex, int x) throws SQLException {  
        wrappedStatement.setInt(parameterIndex, x);  
        saveQueryParamValue(parameterIndex, new Integer(x));  
    }  
  
    public void setLong(int parameterIndex, long x) throws SQLException {  
        wrappedStatement.setLong(parameterIndex, x);  
        saveQueryParamValue(parameterIndex, new Long(x));  
    }  
  
    public void setNull(int parameterIndex, int sqlType) throws SQLException {  
        wrappedStatement.setNull(parameterIndex, sqlType);  
        saveQueryParamValue(parameterIndex, new Integer(sqlType));  
    }  
  
    public void setNull(int paramIndex, int sqlType, String typeName)  
            throws SQLException {  
        wrappedStatement.setNull(paramIndex, sqlType, typeName);  
        saveQueryParamValue(paramIndex, new Integer(sqlType));  
    }  
  
    public void setObject(int parameterIndex, Object x) throws SQLException {  
        wrappedStatement.setObject(parameterIndex, x);  
        saveQueryParamValue(parameterIndex, x);  
    }  
  
    public void setObject(int parameterIndex, Object x, int targetSqlType)  
            throws SQLException {  
        wrappedStatement.setObject(parameterIndex, x, targetSqlType);  
        saveQueryParamValue(parameterIndex, x);  
    }  
  
    public void setObject(int parameterIndex, Object x, int targetSqlType,  
            int scale) throws SQLException {  
        wrappedStatement.setObject(parameterIndex, x, targetSqlType, scale);  
        saveQueryParamValue(parameterIndex, x);  
    }  
  
    public void setRef(int i, Ref x) throws SQLException {  
        wrappedStatement.setRef(i, x);  
        saveQueryParamValue(i, x);  
    }  
  
    public void setShort(int parameterIndex, short x) throws SQLException {  
        wrappedStatement.setShort(parameterIndex, x);  
        saveQueryParamValue(parameterIndex, new Short(x));  
    }  
  
    public void setString(int parameterIndex, String x) throws SQLException {  
        wrappedStatement.setString(parameterIndex, x);  
        saveQueryParamValue(parameterIndex, x);  
    }  
  
    public void setTime(int parameterIndex, Time x) throws SQLException {  
        wrappedStatement.setTime(parameterIndex, x);  
        saveQueryParamValue(parameterIndex, x);  
    }  
  
    public void setTime(int parameterIndex, Time x, Calendar cal)  
            throws SQLException {  
        wrappedStatement.setTime(parameterIndex, x, cal);  
        saveQueryParamValue(parameterIndex, x);  
    }  
  
    public void setTimestamp(int parameterIndex, Timestamp x)  
            throws SQLException {  
        wrappedStatement.setTimestamp(parameterIndex, x);  
        saveQueryParamValue(parameterIndex, x);  
    }  
  
    public void setTimestamp(int parameterIndex, Timestamp x, Calendar cal)  
            throws SQLException {  
        wrappedStatement.setTimestamp(parameterIndex, x, cal);  
        saveQueryParamValue(parameterIndex, x);  
    }  
  
    public void setURL(int parameterIndex, URL x) throws SQLException {  
        wrappedStatement.setURL(parameterIndex, x);  
        saveQueryParamValue(parameterIndex, x);  
    }  
  
    public void setUnicodeStream(int parameterIndex, InputStream x, int length)  
            throws SQLException {  
        wrappedStatement.setUnicodeStream(parameterIndex, x, length);  
        saveQueryParamValue(parameterIndex, x);  
    }  
  
    public void addBatch(String sql) throws SQLException {  
        wrappedStatement.addBatch(sql);  
    }  
  
    public void cancel() throws SQLException {  
        wrappedStatement.cancel();  
    }  
  
    public void clearBatch() throws SQLException {  
        wrappedStatement.clearBatch();  
    }  
  
    public void clearWarnings() throws SQLException {  
        wrappedStatement.clearWarnings();  
    }  
  
    public void close() throws SQLException {  
        wrappedStatement.close();  
    }  
  
    public boolean execute(String sql) throws SQLException {  
        return wrappedStatement.execute(sql);  
    }  
  
    public boolean execute(String sql, int autoGeneratedKeys)  
            throws SQLException {  
        return wrappedStatement.execute(sql, autoGeneratedKeys);  
    }  
  
    public boolean execute(String sql, int[] columnIndexes) throws SQLException {  
        return wrappedStatement.execute(sql, columnIndexes);  
    }  
  
    public boolean execute(String sql, String[] columnNames)  
            throws SQLException {  
        return wrappedStatement.execute(sql, columnNames);  
    }  
  
    public int[] executeBatch() throws SQLException {  
        return wrappedStatement.executeBatch();  
    }  
  
    public ResultSet executeQuery(String sql) throws SQLException {  
        return wrappedStatement.executeQuery(sql);  
    }  
  
    public int executeUpdate(String sql) throws SQLException {  
        return wrappedStatement.executeUpdate(sql);  
    }  
  
    public int executeUpdate(String sql, int autoGeneratedKeys)  
            throws SQLException {  
        return wrappedStatement.executeUpdate(sql, autoGeneratedKeys);  
    }  
  
    public int executeUpdate(String sql, int[] columnIndexes)  
            throws SQLException {  
        return wrappedStatement.executeUpdate(sql, columnIndexes);  
    }  
  
    public int executeUpdate(String sql, String[] columnNames)  
            throws SQLException {  
        return wrappedStatement.executeUpdate(sql, columnNames);  
    }  
  
    public Connection getConnection() throws SQLException {  
        return wrappedStatement.getConnection();  
    }  
  
    public int getFetchDirection() throws SQLException {  
        return wrappedStatement.getFetchDirection();  
    }  
  
    public int getFetchSize() throws SQLException {  
        return wrappedStatement.getFetchSize();  
    }  
  
    public ResultSet getGeneratedKeys() throws SQLException {  
        return wrappedStatement.getGeneratedKeys();  
    }  
  
    public int getMaxFieldSize() throws SQLException {  
        return wrappedStatement.getMaxFieldSize();  
    }  
  
    public int getMaxRows() throws SQLException {  
        return wrappedStatement.getMaxRows();  
    }  
  
    public boolean getMoreResults() throws SQLException {  
        return wrappedStatement.getMoreResults();  
    }  
  
    public boolean getMoreResults(int current) throws SQLException {  
        return wrappedStatement.getMoreResults(current);  
    }  
  
    public int getQueryTimeout() throws SQLException {  
        return wrappedStatement.getQueryTimeout();  
    }  
  
    public ResultSet getResultSet() throws SQLException {  
        return wrappedStatement.getResultSet();  
    }  
  
    public int getResultSetConcurrency() throws SQLException {  
        return wrappedStatement.getResultSetConcurrency();  
    }  
  
    public int getResultSetHoldability() throws SQLException {  
        return wrappedStatement.getResultSetHoldability();  
    }  
  
    public int getResultSetType() throws SQLException {  
        return wrappedStatement.getResultSetType();  
    }  
  
    public int getUpdateCount() throws SQLException {  
        return wrappedStatement.getUpdateCount();  
    }  
  
    public SQLWarning getWarnings() throws SQLException {  
        return wrappedStatement.getWarnings();  
    }  
  
    public void setCursorName(String name) throws SQLException {  
        wrappedStatement.setCursorName(name);  
    }  
  
    public void setEscapeProcessing(boolean enable) throws SQLException {  
        wrappedStatement.setEscapeProcessing(enable);  
    }  
  
    public void setFetchDirection(int direction) throws SQLException {  
        wrappedStatement.setFetchDirection(direction);  
    }  
  
    public void setFetchSize(int rows) throws SQLException {  
        wrappedStatement.setFetchSize(rows);  
    }  
  
    public void setMaxFieldSize(int max) throws SQLException {  
        wrappedStatement.setMaxFieldSize(max);  
    }  
  
    public void setMaxRows(int max) throws SQLException {  
        wrappedStatement.setMaxFieldSize(max);  
    }  
  
    public void setQueryTimeout(int seconds) throws SQLException {  
        wrappedStatement.setQueryTimeout(seconds);  
    }

    public void setAsciiStream(int parameterIndex, InputStream x)
            throws SQLException {
        wrappedStatement.setAsciiStream(parameterIndex, x);
        
    }

    public void setAsciiStream(int parameterIndex, InputStream x, long length)
            throws SQLException {
        wrappedStatement.setAsciiStream(parameterIndex, x, length);
        
    }

    public void setBinaryStream(int parameterIndex, InputStream x)
            throws SQLException {
        wrappedStatement.setBinaryStream(parameterIndex, x);
        
    }

    public void setBinaryStream(int parameterIndex, InputStream x, long length)
            throws SQLException {
        wrappedStatement.setBinaryStream(parameterIndex, x, length);
        
    }

    public void setBlob(int parameterIndex, InputStream inputStream)
            throws SQLException {
        wrappedStatement.setBlob(parameterIndex, inputStream);
        
    }

    public void setBlob(int parameterIndex, InputStream inputStream, long length)
            throws SQLException {
        wrappedStatement.setBlob(parameterIndex, inputStream, length);
        
    }

    public void setCharacterStream(int parameterIndex, Reader reader)
            throws SQLException {
        wrappedStatement.setCharacterStream(parameterIndex, reader);
        
    }

    public void setCharacterStream(int parameterIndex, Reader reader,
            long length) throws SQLException {
        wrappedStatement.setCharacterStream(parameterIndex, reader, length);
        
    }

    public void setClob(int parameterIndex, Reader reader) throws SQLException {
        wrappedStatement.setClob(parameterIndex, reader);
        
    }

    public void setClob(int parameterIndex, Reader reader, long length)
            throws SQLException {
        wrappedStatement.setClob(parameterIndex, reader, length);
        
    }

    public void setNCharacterStream(int parameterIndex, Reader value)
            throws SQLException {
        wrappedStatement.setNCharacterStream(parameterIndex, value);
        
    }

    public void setNCharacterStream(int parameterIndex, Reader value,
            long length) throws SQLException {
        wrappedStatement.setNCharacterStream(parameterIndex, value, length);
        
    }

    public void setNClob(int parameterIndex, NClob value) throws SQLException {
        wrappedStatement.setNClob(parameterIndex, value);
        
    }

    public void setNClob(int parameterIndex, Reader reader) throws SQLException {
        wrappedStatement.setNClob(parameterIndex, reader);
        
    }

    public void setNClob(int parameterIndex, Reader reader, long length)
            throws SQLException {
        wrappedStatement.setNClob(parameterIndex, reader, length);
        
    }

    public void setNString(int parameterIndex, String value)
            throws SQLException {
        wrappedStatement.setNString(parameterIndex, value);
        
    }

    public void setRowId(int parameterIndex, RowId x) throws SQLException {
        wrappedStatement.setRowId(parameterIndex, x);
        
    }

    public void setSQLXML(int parameterIndex, SQLXML xmlObject)
            throws SQLException {
        wrappedStatement.setSQLXML(parameterIndex, xmlObject);
        
    }

    public boolean isClosed() throws SQLException {
        return wrappedStatement.isClosed();
    }

    public boolean isPoolable() throws SQLException {
        return wrappedStatement.isPoolable();
    }

    public void setPoolable(boolean poolable) throws SQLException {
        wrappedStatement.setPoolable(poolable);
        
    }

    public boolean isWrapperFor(Class<?> iface) throws SQLException {
        return wrappedStatement.isWrapperFor(iface);
    }

    public <T> T unwrap(Class<T> iface) throws SQLException {
        return wrappedStatement.unwrap(iface);
    }

    @Override
    public void closeOnCompletion() throws SQLException {
        wrappedStatement.closeOnCompletion();
    }

    @Override
    public boolean isCloseOnCompletion() throws SQLException {
        return wrappedStatement.isCloseOnCompletion();
    }  
}  

Rule Based Optimizer 基于规则的SQL优化器

待执行的SQL语句进入数据库系统后都会进入查询优化器进行查询转换.

在Oracle 10g之前, Oracle会默认使用RBO规则

数据库会将查询条件分成15个等级, 通过判断条件(动作)的级别高低来决定那个谓词被优先执行

例如 基于rowid的查询级别是最高的为1, 全表扫描的执行级别最低为15

基于RBO优化器的一个明显缺点在于, 依靠硬编码来确定的规则很难被动态改变, 编写SQL语句的时候需要十分注意

这里如果你跟我一样使用的是10g之后的版本,我们需要使用下面的命令将优化器暂时改成RBO ( 只对当前session有效,不用再改回来)

alter session set optimizer_mode=’RULE’;

然后我执行一个执行计划

EXPLAIN PLAN FOR select * from INO_OUTAGES.OUTAGE where SUBCATEGORY=’NBN Outage’;
SELECT plan_table_output FROM TABLE(DBMS_XPLAN.DISPLAY(‘PLAN_TABLE’));

explain plan for select * from INO_SCHEDULING.IS_COMMANDS;
select * from table(dbms_xplan.display);

Cost Based Optimizer 基于成本的SQL优化器

Oracle 自7开始引入了CBO.

Oracle会根据cpu,内存以及I/O的开销来进行SQL优化

可选择率是CBO的一个核心概念

1. 可选择率

可选择率 = 指定谓词返回的结果集记录数/未使用谓词返回的结果集记录数

取值范围是 0到1 , 值越小 表明 可选择性 越好

2. 可传递性

2.1 简单谓词传递

2.2 连接谓词传递

2.3 外链接谓词传递

实际上就是 a1=a2 and a1=10 等价于 a1=10 and a2=10

数据库索引设计

为关系型数据库添加索引是一种最基础的优化方式

基础虽基础, 但其实并不简单, 记得几年前去一家国内大公司的云计算部门面试

提到索引的时候面试官说”加索引都是最基础的”, 我不赞同这种说法, 基础但并不简单

考虑下面这种情况

1 索引的原理是什么

2 我应该在哪些字段上加索引

3 我应该对每个字段使用独立的索引还是应该使用联合索引

4 加了索引之后的坏处是什么

5 哪些情况下没有索引反而要比有索引更快

下面我们进一步讨论这些问题

1 索引的原理是什么

索引的工作方式其实和view差不多, 每个索引都会构成一个单独的索引表, 这里我不想讨论B tree之类的问题

当你为字段A创建了一个索引之后,数据库会生成一个独立的索引表

2 我应该在哪些字段上加索引

根据问题1, 实际上如果你直接使用select A from Table 时,数据并不需要对主表进行查询,而是直接查询索引表,这样可以避免全表扫描

下面这个表只有一个聚簇索引(主键索引,一般来说数据是按照聚簇索引来物理排序的,磁盘上的存储位置是连续的)

不过我的表里数据只有100条左右,所以看不出耗时差别

EXPLAIN PLAN FOR 
select * from INO_OUTAGES.OUTAGE;SELECT plan_table_output FROM TABLE(DBMS_XPLAN.DISPLAY(‘PLAN_TABLE’));EXPLAIN PLAN FOR 
select ID from INO_OUTAGES.OUTAGE;SELECT plan_table_output FROM TABLE(DBMS_XPLAN.DISPLAY(‘PLAN_TABLE’));

检索所有列时数据库会进行全表扫描

只检索索引列时, 只扫描索引表

如果是既查询索引列,又有额外的非索引列, 实际上也会进行全表扫描

EXPLAIN PLAN FOR 
select ID,DURATION,DESCRIPTION from INO_OUTAGES.OUTAGE;SELECT plan_table_output FROM TABLE(DBMS_XPLAN.DISPLAY(‘PLAN_TABLE’));

接下来看看谓词的情况

EXPLAIN PLAN FOR 
select * from INO_OUTAGES.OUTAGE where id=2146;SELECT plan_table_output FROM TABLE(DBMS_XPLAN.DISPLAY(‘PLAN_TABLE’));

可以看到当谓词是索引列时, 没有进行全表扫描 ( 不过如果你使用id<2146这种范围查询,oracle还是会使用全表扫描的方式)

还有order by 之类的谓词需要注意

根据上面的测试结果, 只有当 select用到的查询列, where使用的谓词, order by的谓词全部均为索引列时, 数据库才会使用索引表进行查询

如果一个查询需要经常被调用(此时可以忽略索引带来的更新和空间消耗), 最好将select,where,order by等使用的列全部添加索引

但是我们还会有下面的疑问

3 我应该对每个字段使用独立的索引还是应该使用联合索引

当KEY, AREA_KEY, MANDATOR_KEY为联合索引,你尝试select这三列时,会得到一个INDEX FAST FULL SCAN

区别在于

索引快速全扫描的执行结果不一定是有序的。这是因为索引快速全扫描时Oracle是根据索引行在磁盘上的物理存储顺序来扫描,而不是根据索引行的逻辑顺序来扫描的,所以扫描结果才不一定有序, INDEX FAST FULL SCAN的性能要大大优于INDEX FULL SCAN

EXPLAIN PLAN FOR 
select KEY, AREA_KEY, MANDATOR_KEY from INO_ONCONFIG.LOOKUP_OPTIONS;SELECT plan_table_output FROM TABLE(DBMS_XPLAN.DISPLAY(‘PLAN_TABLE’));

谓词方面复杂一些

第一个例子, 如果不使用联合索引中全部的3个列,会得到一个全表扫描

EXPLAIN PLAN FOR 
select * from INO_ONCONFIG.LOOKUP_OPTIONS where AREA_KEY=’IFMS_SYMPTOM_CODE’ and MANDATOR_KEY=’PLATFORM’;SELECT plan_table_output FROM TABLE(DBMS_XPLAN.DISPLAY(‘PLAN_TABLE’));

第二个例子, 如果使用全部的三个索引列做谓词,会得到一个索引表扫描

EXPLAIN PLAN FOR 
select * from INO_ONCONFIG.LOOKUP_OPTIONS where KEY >’1′ and AREA_KEY=’INVOICE_INTERVALS’ and MANDATOR_KEY=’PLATFORM’;SELECT plan_table_output FROM TABLE(DBMS_XPLAN.DISPLAY(‘PLAN_TABLE’));

上面这个例子如果连select列都为索引列的话你甚至可以只使用索引表,不用查询主表

EXPLAIN PLAN FOR 
select AREA_KEY,MANDATOR_KEY from INO_ONCONFIG.LOOKUP_OPTIONS where KEY >’1′ and AREA_KEY=’INVOICE_INTERVALS’ and MANDATOR_KEY=’PLATFORM’;SELECT plan_table_output FROM TABLE(DBMS_XPLAN.DISPLAY(‘PLAN_TABLE’));

4 加了索引之后的坏处是什么

增加了存储空间和插入及修改数据时造成的索引维护

5 哪些情况下没有索引反而要比有索引更快

这里有一种极端情况就是数据库所使用的硬盘随机读操作比较慢,而连续读比较快

假设谓词是 id in (1,10,13,45,234,676,233,234,67,89), 整个表的容量是100M

对于现代硬盘读取100M可能只需要1秒, 而使用索引时,由于先扫描索引表,然后通过索引产生10次随机硬盘读取, 如果每次平均随机读时间超过了100毫秒, 则使用索引反而会消耗更多时间

Oracle 并发查询

需要打开计时器的话可以参考

set timing on;

在开始之前需要强调的是

任何并发操作都是以硬件换速度, 如果没有足够的硬件不要轻易使用并发,另外如果是大访问量的网站系统请慎用,数据库并发查询一般还是用在夜晚的大批量batch批处理上

1. 直接改表

ALTER TABLE INO_ONCONFIG.LOOKUP_OPTIONS PARALLEL 4;

要关闭的话使用直接NOPARALLEL参数或者直接设置线程数为1好像也可以

ALTER TABLE INO_ONCONFIG.LOOKUP_OPTIONS NOPARALLEL;

ALTER TABLE INO_ONCONFIG.LOOKUP_OPTIONS PARALLEL 1;

2. 使用hint

需要注意的是如果需要指定schema名的话hint的时候需要把schema名去掉

select /*+ parallel(LOOKUP_OPTIONS 8) */ count(*) from INO_ONCONFIG.LOOKUP_OPTIONS;

下面我使用一个关联查询的sql来看看并行与非并行的区别

(我这里并发的消耗要比直接全表扫描join更大)

EXPLAIN PLAN FOR 
select   * from INO_OUTAGES.OUTAGE o
  join INO_OUTAGES.BASE_LOOKUP b on o.id=b.OUTAGE_ID;
SELECT plan_table_output FROM TABLE(DBMS_XPLAN.DISPLAY(‘PLAN_TABLE’));EXPLAIN PLAN FOR 
select /*+ parallel(o 4) */ /*+ parallel(b 4) */  * from INO_OUTAGES.OUTAGE o
  join INO_OUTAGES.BASE_LOOKUP b on o.id=b.OUTAGE_ID;
SELECT plan_table_output FROM TABLE(DBMS_XPLAN.DISPLAY(‘PLAN_TABLE’));

PX 进程: Parallel Execution Slaves

Parallel to Serial(P->S): 并发到串行

Parallel to Parallel(P->P):并发到并发

Parallel Combined with parent(PCWP): 同一个从属进程执行的并行操作,同时父操作也是并行的。

Parallel Combined with Child(PCWC): 同一个从属进程执行的并行操作,子操作也是并行的。

Serial to Parallel(S->P): 串行到并发

用户进程发起请求之后, oracle会使用一个QC进程(Query Coordinator)来协调这个用户进程与oracle进程之间的工作

它会决定这个请求的并发度,控制执行过程,汇总结果等 ( 上图中的PX COORDINATOR)

一般来说你只会有一个QC, 不过如果使用了类似UNIONALL这样的操作,就会出现多个QC

每个QC都包含一个树形结构, 这个树包含若干DFO节点(Data Flow Operator) , DFO是可以并行执行的单元, QC持有一到两组QSS(Query Slaves Set),每个QSS包含若干QS(Query Slaves),QSS会扮演生产者和消费者的角色,(每个QSS可能同为生产者,也可能同为消费者,也可能一个生产者一个消费者), 我们通常所说的并行度指的是一组QSS里并行的QS个数,所以如果oracle使用了两组QSS,那么实际运行的线程数会是并行度的两倍

TQ(table queue), 意为表队列。它是一个逻辑上的概念。在并行执行中,用TQ来实现query slave set 之间以及query slave set 和QC之间数据的传递和重新分布。可以简单认为就是存储并行执行过程中的中间结果的临时表

简单归纳一下

oracle进程

    -> 启动1个QC或者多个QC

          -> 每个QC = 一个DFO树

          -> 每个QC = 最多2个QSS (查看TQ列)

              -> 每个QSS = 若干QS

所以上图中

启动一个QC,也就是一个DFO树( 参考id 1 )

有两组QSS(TQ列的Q10,01和Q10,02) ( id 2,3,4,5 为Q10,02   id 9,10,11,12为Q10,01)

Q10,01为生产者 (它在执行TABLE ACCESS FULL和 PX BLOCK ITERATOR id 11,12) , Q10,01 先以并行的方式对OUTAGE table进行了全表扫描

同时, Q10,02在一开始也作为生产者扫描了BASE_LOOKUP 表, 然后转为消费者在id 03 处对两个结果集进行了join, 然后输出了合并后的结果

为了观察的更细致一些使用union all关键字可以得到两个QC的结果

EXPLAIN PLAN FOR 
select /*+ parallel(o 4) */ /*+ parallel(b 4) */  o.id,b.OUTAGE_ID,o.OUTAGE_TYPE from INO_OUTAGES.OUTAGE o
  left join INO_OUTAGES.BASE_LOOKUP b on o.id=b.OUTAGE_ID
union all
select /*+ parallel(o 4) */ /*+ parallel(h 4) */  h.id,h.OUTAGE_ID,o.OUTAGE_TYPE from INO_OUTAGES.OUTAGE o
  left join INO_OUTAGES.HISTORY h on o.id=h.OUTAGE_ID
;
SELECT plan_table_output FROM TABLE(DBMS_XPLAN.DISPLAY(‘PLAN_TABLE’));