blob: a777b004ca2b6d841b4ed3ff05393b86fdd848ba [file] [log] [blame]
/*
* Class org.apache.derbyTesting.functionTests.tests.lang.SelectivityTest
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.derbyTesting.functionTests.tests.lang;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
import junit.framework.Test;
import org.apache.derbyTesting.junit.BaseJDBCTestCase;
import org.apache.derbyTesting.junit.BaseTestSuite;
import org.apache.derbyTesting.junit.CleanDatabaseTestSetup;
import org.apache.derbyTesting.junit.DatabasePropertyTestSetup;
import org.apache.derbyTesting.junit.JDBC;
import org.apache.derbyTesting.junit.RuntimeStatisticsParser;
import org.apache.derbyTesting.junit.SQLUtilities;
public class SelectivityTest extends BaseJDBCTestCase {
public SelectivityTest(String name) {
super(name);
}
public static Test suite() {
Properties props = new Properties();
// first disable the automatic statistics gathering so we get
// clean statistics
// then switch the statement cache size to 0, so that doesn't
// interfere and previous tests' left-overs are gone.
props.setProperty("derby.storage.indexStats.auto", "false");
props.setProperty("derby.language.statementCacheSize", "0");
// set the props, and boot the db
Test test = new DatabasePropertyTestSetup(
new BaseTestSuite(SelectivityTest.class), props, true);
return new CleanDatabaseTestSetup(test) {
protected void decorateSQL(Statement s) throws SQLException
{
s.executeUpdate("create table two (x int)");
s.executeUpdate("insert into two values (1),(2)");
s.executeUpdate("create table ten (x int)");
s
.executeUpdate("insert into ten values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10)");
s.executeUpdate("create table twenty (x int)");
s
.executeUpdate("insert into twenty values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(13),(14),(15),(16),(17),(18),(19),(20)");
s
.executeUpdate("create table hundred (x int generated always as identity, dc int)");
s
.executeUpdate("insert into hundred (dc) select t1.x from ten t1, ten t2");
s
.executeUpdate("create table template (id int not null generated always as identity, two int, twenty int, hundred int)");
// 4000 rows
s
.executeUpdate("insert into template (two, twenty, hundred) select two.x, twenty.x, hundred.x from two, twenty, hundred");
s.executeUpdate("create index template_two on template(two)");
s
.executeUpdate("create index template_twenty on template(twenty)");
// 20 distinct values
s
.executeUpdate("create index template_22 on template(twenty,two)");
s
.executeUpdate("create unique index template_id on template(id)");
s
.executeUpdate("create index template_102 on template(hundred,two)");
s
.executeUpdate("create table test (id int, two int, twenty int, hundred int)");
s.executeUpdate("create index test_id on test(id)");
s.executeUpdate("insert into test select * from template");
s.executeUpdate("create view showstats as "
+ "select cast (conglomeratename as varchar(20)) indexname, "
+ "cast (statistics as varchar(40)) stats, "
+ "creationtimestamp createtime, "
+ "colcount ncols "
+ "from sys.sysstatistics, sys.sysconglomerates "
+ "where conglomerateid = referenceid");
ResultSet statsrs = s
.executeQuery("select indexname, stats, ncols from showstats order by indexname, stats, createtime, ncols");
JDBC.assertFullResultSet(statsrs, new String[][] {
{"TEMPLATE_102","numunique= 100 numrows= 4000","1"},
{"TEMPLATE_102","numunique= 200 numrows= 4000","2"},
{"TEMPLATE_22","numunique= 20 numrows= 4000","1"},
{"TEMPLATE_22","numunique= 40 numrows= 4000","2"},
{"TEMPLATE_TWENTY","numunique= 20 numrows= 4000","1"},
{"TEMPLATE_TWO","numunique= 2 numrows= 4000","1"}});
s
.executeUpdate("CALL SYSCS_UTIL.SYSCS_UPDATE_STATISTICS('APP','TEMPLATE',null)");
s
.executeUpdate("CALL SYSCS_UTIL.SYSCS_UPDATE_STATISTICS('APP','TEST',null)");
statsrs = s
.executeQuery("select indexname, stats, ncols from showstats order by indexname, stats, createtime, ncols");
JDBC.assertFullResultSet(statsrs, new String[][] {
{"TEMPLATE_102","numunique= 100 numrows= 4000","1"},
{"TEMPLATE_102","numunique= 200 numrows= 4000","2"},
{"TEMPLATE_22","numunique= 20 numrows= 4000","1"},
{"TEMPLATE_22","numunique= 40 numrows= 4000","2"},
{"TEMPLATE_TWENTY","numunique= 20 numrows= 4000","1"},
{"TEMPLATE_TWO","numunique= 2 numrows= 4000","1"},
{"TEST_ID","numunique= 4000 numrows= 4000","1"},
});
s.executeUpdate("create table t1 " +
"(id int generated always as identity, " +
"two int, twenty int, hundred varchar(3))");
s.executeUpdate("insert into t1 (hundred, twenty, two) " +
"select CAST(CHAR(hundred.x) AS VARCHAR(3)), " +
"twenty.x, two.x from hundred, twenty, two");
s.executeUpdate("create table t2 " +
"(id int generated always as identity, " +
"two int, twenty int, hundred varchar(3))");
s.executeUpdate("insert into t2 (hundred, twenty, two) " +
"select CAST(CHAR(hundred.x) AS VARCHAR(3)) , " +
"twenty.x, two.x from hundred, twenty, two");
s.executeUpdate("create table t3 " +
"(id int generated always as identity, " +
"two int, twenty int, hundred varchar(3))");
s.executeUpdate("insert into t3 (hundred, twenty, two) " +
"select CAST(CHAR(hundred.x) AS VARCHAR(3)), " +
"twenty.x, two.x from hundred, twenty, two");
s.executeUpdate("create index t1_hundred on t1(hundred)");
s.executeUpdate("create index t1_two_twenty on t1(two,twenty)");
s.executeUpdate("create index " +
"t1_twenty_hundred on t1(twenty, hundred)");
s.executeUpdate("create index t2_hundred on t2(hundred)");
s.executeUpdate("create index t2_two_twenty on t2(two,twenty)");
s.executeUpdate("create index t2_twenty_hundred on t2(twenty, hundred)");
s.executeUpdate("create index t3_hundred on t3(hundred)");
s.executeUpdate("create index t3_two_twenty on t3(two,twenty)");
s.executeUpdate("create index t3_twenty_hundred on t3(twenty, hundred)");
s.executeUpdate("CALL SYSCS_UTIL.SYSCS_UPDATE_STATISTICS" +
"('APP','T1',null)");
s.executeUpdate("CALL SYSCS_UTIL.SYSCS_UPDATE_STATISTICS" +
"('APP','T2',null)");
s.executeUpdate("CALL SYSCS_UTIL.SYSCS_UPDATE_STATISTICS" +
"('APP','T3',null)");
statsrs = s.executeQuery(
"select indexname, stats, ncols from showstats " +
"where indexname like 'T1%' " +
"order by indexname, stats");
JDBC.assertFullResultSet(statsrs, new String[][] {
{"T1_HUNDRED","numunique= 100 numrows= 4000","1"},
{"T1_TWENTY_HUNDRED","numunique= 20 numrows= 4000","1"},
{"T1_TWENTY_HUNDRED","numunique= 2000 numrows= 4000","2"},
{"T1_TWO_TWENTY","numunique= 2 numrows= 4000","1"},
{"T1_TWO_TWENTY","numunique= 40 numrows= 4000","2"}});
statsrs = s.executeQuery(
"select indexname, stats, ncols from showstats " +
"where indexname like 'T2%' order by indexname, stats");
JDBC.assertFullResultSet(statsrs, new String[][] {
{"T2_HUNDRED","numunique= 100 numrows= 4000","1"},
{"T2_TWENTY_HUNDRED","numunique= 20 numrows= 4000","1"},
{"T2_TWENTY_HUNDRED","numunique= 2000 numrows= 4000","2"},
{"T2_TWO_TWENTY","numunique= 2 numrows= 4000","1"},
{"T2_TWO_TWENTY","numunique= 40 numrows= 4000","2"}});
statsrs = s.executeQuery(
"select indexname, stats, ncols from showstats " +
"where indexname like 'T3%' order by indexname, stats");
JDBC.assertFullResultSet(statsrs, new String[][] {
{"T3_HUNDRED","numunique= 100 numrows= 4000","1"},
{"T3_TWENTY_HUNDRED","numunique= 20 numrows= 4000","1"},
{"T3_TWENTY_HUNDRED","numunique= 2000 numrows= 4000","2"},
{"T3_TWO_TWENTY","numunique= 2 numrows= 4000","1"},
{"T3_TWO_TWENTY","numunique= 40 numrows= 4000","2"}});
s.executeUpdate("create table scratch_table" +
"(id int, two int, twenty int, hundred int)");
s.executeUpdate("insert into scratch_table select " +
"id, two, twenty, CAST(CHAR(hundred) AS INTEGER) " +
"from t1");
s.executeUpdate("create index st_all on scratch_table" +
"(two, twenty, hundred)");
s.executeUpdate("CALL SYSCS_UTIL.SYSCS_UPDATE_STATISTICS" +
"('APP','SCRATCH_TABLE',null)");
s.executeUpdate("create table complex" +
"(id int generated always as identity, " +
"two int, twenty int, hundred int, a int, b int)");
s.executeUpdate(
"insert into complex (two, twenty, hundred, a, b) " +
"select two.x, twenty.x, hundred.x, two.x, twenty.x " +
"from two, twenty, hundred");
s.executeUpdate("create index complexind on complex" +
"(two, twenty, hundred, a, b)");
s.executeUpdate("CALL SYSCS_UTIL.SYSCS_UPDATE_STATISTICS" +
"('APP','COMPLEX',null)");
}
};
}
public void testSingleColumnSelectivity() throws SQLException {
// choose whatever plan you want but the row estimate should be.
//(n * n) * 0.5
// join on two, template inner, all rows.
Connection conn = getConnection();
Statement s = createStatement();
s.executeUpdate("CALL SYSCS_UTIL.SYSCS_UPDATE_STATISTICS" +
"('APP','TEMPLATE',NULL)");
s.executeUpdate("CALL SYSCS_UTIL.SYSCS_UPDATE_STATISTICS" +
"('APP','TEST',NULL)");
s.executeUpdate("call SYSCS_UTIL.SYSCS_SET_RUNTIMESTATISTICS(1)");
s.executeQuery("select template.id from --DERBY-PROPERTIES joinOrder=fixed\n"
+ "test, template where test.two = template.two").close();
checkEstimatedRowCount(conn,8020012.5);
// choose hash join. Selectivity should be the same
// join on two. template inner, hash join
s.executeQuery("select template.id from " +
"--DERBY-PROPERTIES joinOrder=fixed \n" +
"test, template --DERBY-PROPERTIES joinStrategy=hash \n" +
"where test.two = template.two").close();
checkEstimatedRowCount(conn,8020012.5);
RuntimeStatisticsParser rtsp =
SQLUtilities.getRuntimeStatisticsParser(s);
assertTrue(rtsp.usedHashJoin());
// choose NL join, no index. Selectivity should be the same
// join on two. template inner, NL, no index, all rows.
s.executeQuery("select template.id from " +
"--DERBY-PROPERTIES joinOrder=fixed \n" +
"test, template --DERBY-PROPERTIES joinStrategy=nestedLoop, " +
"index=null \n" +
"where test.two = template.two").close();
checkEstimatedRowCount(conn,8020012.5);
rtsp = SQLUtilities.getRuntimeStatisticsParser(s);
assertFalse(rtsp.usedHashJoin());
// choose NL join, index template_two. Selectivity should be the same
// join on two. template inner, NL, index=two, all rows.
s.executeQuery("select template.id from " +
"--DERBY-PROPERTIES joinOrder=fixed \n" +
"test, template --DERBY-PROPERTIES joinStrategy=nestedLoop, " +
"index=template_two \n" +
"where test.two = template.two").close();
checkEstimatedRowCount(conn,8020012.5);
rtsp = SQLUtilities.getRuntimeStatisticsParser(s);
assertTrue(rtsp.usedSpecificIndexForIndexScan("TEMPLATE", "TEMPLATE_TWO"));
// do joins on 20
// first NL
// join on twenty. template inner, NL, index=template_twenty, all rows.
s.executeQuery("select template.id from " +
"--DERBY-PROPERTIES joinOrder=fixed \n" +
"test, template --DERBY-PROPERTIES joinStrategy=nestedLoop, " +
"index=template_twenty \n" +
"where test.twenty = template.twenty").close();
// Rowcount should be same as testSingleColumnSelectivityHash
checkEstimatedRowCount(conn,802001.25);
rtsp = SQLUtilities.getRuntimeStatisticsParser(s);
assertTrue(rtsp.usedSpecificIndexForIndexScan("TEMPLATE", "TEMPLATE_TWENTY"));
// join on 20 but use index 20_2
// cost as well as selectivity should be divided using selectivity
// cost should same as template_twenty, or just a shade more...
// join on twenty. template inner, NL, index=template_22, all rows
s.executeQuery("select template.id from " +
"--DERBY-PROPERTIES joinOrder=fixed \n" +
"test, template --DERBY-PROPERTIES joinStrategy=nestedLoop, " +
"index=template_22 \n" +
"where test.twenty = template.twenty").close();
checkEstimatedRowCount(conn,802001.25);
rtsp = SQLUtilities.getRuntimeStatisticsParser(s);
assertTrue(rtsp.usedSpecificIndexForIndexScan("TEMPLATE", "TEMPLATE_22"));
// join on twenty but no index
// note: the original test had this comment:
// 'rc should be divided using selectivity. cost should be way different'
// however, it seems the ec is identical.
// join on twenty, template inner, NL, index=null, all rows
s.executeQuery("select template.id from " +
"--DERBY-PROPERTIES joinOrder=fixed \n" +
"test, template --DERBY-PROPERTIES joinStrategy=nestedLoop, " +
"index=null \n" +
"where test.twenty = template.twenty").close();
checkEstimatedRowCount(conn,802001.25);
// still single column, try stuff on 100 but with extra qualification
// on outer table.
// row count is 100 * 4000 * 0.01 = 4000
// join on hundred.
// template inner, NL, index=template_102, 100 rows from outer
s.executeQuery("select template.id from " +
"--DERBY-PROPERTIES joinOrder=fixed \n" +
"test, template --DERBY-PROPERTIES joinStrategy=nestedLoop, " +
"index=template_102 \n" +
"where test.hundred = template.hundred and test.id <= 100").close();
// note: original cloudscape result was expecting 3884.85 here.
checkEstimatedRowCount(conn,3924.9);
rtsp = SQLUtilities.getRuntimeStatisticsParser(s);
assertTrue(rtsp.usedSpecificIndexForIndexScan("TEST", "TEST_ID"));
assertTrue(rtsp.usedSpecificIndexForIndexScan("TEMPLATE", "TEMPLATE_102"));
// join on hundred.
// template inner, NL, index=null, 100 rows from outer
s.executeQuery("select template.id from " +
"--DERBY-PROPERTIES joinOrder=fixed \n" +
"test, template --DERBY-PROPERTIES joinStrategy=nestedLoop, " +
"index=null \n" +
"where test.hundred = template.hundred and test.id <= 100").close();
checkEstimatedRowCount(conn,3924.9);
rtsp = SQLUtilities.getRuntimeStatisticsParser(s);
assertTrue(rtsp.usedSpecificIndexForIndexScan("TEST", "TEST_ID"));
// join on hundred.
// template inner, hash, index=null, 100 rows from outer.
s.executeQuery("select template.id from " +
"--DERBY-PROPERTIES joinOrder=fixed \n" +
"test, template --DERBY-PROPERTIES joinStrategy=hash, " +
"index=null \n" +
"where test.hundred = template.hundred and test.id <= 100").close();
checkEstimatedRowCount(conn,3924.9);
rtsp = SQLUtilities.getRuntimeStatisticsParser(s);
assertTrue(rtsp.usedHashJoin());
assertTrue(rtsp.usedSpecificIndexForIndexScan("TEST", "TEST_ID"));
}
public void testMultiPredicate() throws SQLException {
// multi predicate tests.
// first do a oin involving twenty and two
// forde use of a simngle column index to do the join
// the row count should involve statistics from both 10 and 2 though...
// row count should 4K * 4K * 1/40 = 400,000
// cost doesn't show up in output but should depend on the index
// being used (verify by hand before checking in.)
setAutoCommit(false);
Connection conn = getConnection();
Statement s = createStatement();
// join on twenty/two. template inner, hash, index=null, all rows.
s.executeUpdate("CALL SYSCS_UTIL.SYSCS_UPDATE_STATISTICS" +
"('APP','TEST',NULL)");
s.executeUpdate("CALL SYSCS_UTIL.SYSCS_UPDATE_STATISTICS" +
"('APP','TEMPLATE',NULL)");
s.executeUpdate("call SYSCS_UTIL.SYSCS_SET_RUNTIMESTATISTICS(1)");
s.executeQuery("select template.id from " +
"--DERBY-PROPERTIES joinOrder=fixed \n" +
"test, template --DERBY-PROPERTIES joinStrategy=hash, " +
"index=null \n" +
"where test.twenty = template.twenty " +
"and test.two = template.two").close();
checkEstimatedRowCount(conn,401000.625);
RuntimeStatisticsParser rtsp =
SQLUtilities.getRuntimeStatisticsParser(s);
assertTrue(rtsp.usedHashJoin());
// join on twenty/two. template inner, NL, index=template_two, all rows
s.executeQuery("select template.id from " +
"--DERBY-PROPERTIES joinOrder=fixed \n" +
"test, template --DERBY-PROPERTIES joinStrategy=nestedLoop, " +
"index=template_two \n" +
"where test.twenty = template.twenty " +
"and test.two = template.two").close();
checkEstimatedRowCount(conn,401000.625);
rtsp = SQLUtilities.getRuntimeStatisticsParser(s);
assertTrue(rtsp.usedSpecificIndexForIndexScan("TEMPLATE", "TEMPLATE_TWO"));
// join on twenty/two.
// template inner, NL, index=template_twenty, all rows.
s.executeQuery("select template.id from " +
"--DERBY-PROPERTIES joinOrder=fixed \n" +
"test, template --DERBY-PROPERTIES joinStrategy=nestedLoop, " +
"index=template_twenty \n" +
"where test.twenty = template.twenty " +
"and test.two = template.two").close();
checkEstimatedRowCount(conn,401000.625);
rtsp = SQLUtilities.getRuntimeStatisticsParser(s);
assertTrue(rtsp.usedSpecificIndexForIndexScan("TEMPLATE", "TEMPLATE_TWENTY"));
// join on twenty/two. template inner, NL, index=template_22, all rows.
s.executeQuery("select template.id from " +
"--DERBY-PROPERTIES joinOrder=fixed \n" +
"test, template --DERBY-PROPERTIES joinStrategy=nestedLoop, " +
"index=template_22 \n" +
"where test.twenty = template.twenty " +
"and test.two = template.two").close();
checkEstimatedRowCount(conn,401000.625);
rtsp = SQLUtilities.getRuntimeStatisticsParser(s);
assertTrue(rtsp.usedSpecificIndexForIndexScan("TEMPLATE", "TEMPLATE_22"));
// multi predicate tests continued
// drop index twenty, two -- use above predicates
// should be smart enough to figure out the selectivity by
// combining twenty and two.
s.executeUpdate("drop index template_22");
// join on twenty/two. index twenty_two dropped.
// template inner, hash, index=null, all rows.
s.executeQuery("select template.id from " +
"--DERBY-PROPERTIES joinOrder=fixed \n" +
"test, template --DERBY-PROPERTIES joinStrategy=hash, " +
"index=null \n" +
"where test.twenty = template.twenty " +
"and test.two = template.two").close();
checkEstimatedRowCount(conn,401000.625);
rtsp = SQLUtilities.getRuntimeStatisticsParser(s);
assertTrue(rtsp.usedHashJoin());
// join on twenty/two. index twenty_two dropped.
// template inner, NL, index=template_two, all rows.'
s.executeQuery("select template.id from " +
"--DERBY-PROPERTIES joinOrder=fixed \n" +
"test, template --DERBY-PROPERTIES joinStrategy=nestedLoop, " +
"index=template_two \n" +
"where test.twenty = template.twenty " +
"and test.two = template.two").close();
checkEstimatedRowCount(conn,401000.625);
rtsp = SQLUtilities.getRuntimeStatisticsParser(s);
assertTrue(rtsp.usedSpecificIndexForIndexScan("TEMPLATE", "TEMPLATE_TWO"));
// join on twenty/two. index twenty_two dropped.
// template inner, NL, index=template_twenty, all rows.
s.executeQuery("select template.id from " +
"--DERBY-PROPERTIES joinOrder=fixed \n" +
"test, template --DERBY-PROPERTIES joinStrategy=nestedLoop, " +
"index=template_twenty \n" +
"where test.twenty = template.twenty " +
"and test.two = template.two").close();
checkEstimatedRowCount(conn,401000.625);
rtsp = SQLUtilities.getRuntimeStatisticsParser(s);
assertTrue(rtsp.usedSpecificIndexForIndexScan("TEMPLATE", "TEMPLATE_TWENTY"));
s.executeUpdate("drop index template_two");
// we only have index template_twenty
// for the second predicate we should use 0.1 instead of 0.5
// thus reducing earlier row count by a factor of 5
// 80,000 instead of 400,000
// join on twenty/two. index twenty_two and two dropped.
// template inner, NL, index=null, all rows.
s.executeQuery("select template.id from " +
"--DERBY-PROPERTIES joinOrder=fixed \n" +
"test, template --DERBY-PROPERTIES joinStrategy=nestedLoop, " +
"index=null \n" +
"where test.twenty = template.twenty " +
"and test.two = template.two").close();
checkEstimatedRowCount(conn,80200.12500000001);
// join on twenty/two. index twenty_two and two dropped.
// template inner, NL, index=template_twenty, all rows.
s.executeQuery("select template.id from " +
"--DERBY-PROPERTIES joinOrder=fixed \n" +
"test, template --DERBY-PROPERTIES joinStrategy=nestedLoop, " +
"index=template_twenty \n" +
"where test.twenty = template.twenty " +
"and test.two = template.two").close();
checkEstimatedRowCount(conn,80200.12500000001);
rtsp = SQLUtilities.getRuntimeStatisticsParser(s);
assertTrue(rtsp.usedSpecificIndexForIndexScan("TEMPLATE", "TEMPLATE_TWENTY"));
// now drop index template_twenty
// selectivity should become 0.1 * 0.1 = 0.01
// 16 * 10^6 * .01 = 160,000
s.executeUpdate("drop index template_twenty");
// join on twenty/two. all indexes dropped.
// template inner, NL, index=null, all rows.
s.executeQuery("select template.id from " +
"--DERBY-PROPERTIES joinOrder=fixed \n" +
"test, template --DERBY-PROPERTIES joinStrategy=nestedLoop, " +
"index=null \n" +
"where test.twenty = template.twenty " +
"and test.two = template.two").close();
checkEstimatedRowCount(conn,160400.25000000003);
rollback();
}
public void testTwoWayJoins() throws SQLException {
setAutoCommit(false);
Connection conn = getConnection();
Statement s = createStatement();
// throw in aditional predicates
// see that the optimizer does the right thing
// index on template_102. join on hundred, constant predicate on two.
// should be able to use statistics for hundred_two to com up with
// row estimate.
// selectivity should be 0.01 * 0.5 = 0.005
// row count is 16*10^6 * 0.005 = 8*10^4.
// join on hundred. constant pred on two. NL, index=null, all rows.
s.executeUpdate("CALL SYSCS_UTIL.SYSCS_UPDATE_STATISTICS" +
"('APP','TEMPLATE',NULL)");
s.executeUpdate("CALL SYSCS_UTIL.SYSCS_UPDATE_STATISTICS" +
"('APP','TEST',NULL)");
s.executeUpdate("call SYSCS_UTIL.SYSCS_SET_RUNTIMESTATISTICS(1)");
s.executeQuery("select template.id from " +
"--DERBY-PROPERTIES joinOrder=fixed \n" +
"test, template --DERBY-PROPERTIES joinStrategy=nestedLoop, " +
"index=null \n" +
"where test.hundred = template.hundred " +
"and 1 = template.two").close();
checkEstimatedRowCount(conn,80200.125);
// just retry above query with different access paths
// row count shouldn't change!
// join on hundred. constant pred on two.
// NL, index=template_102, all rows.
s.executeUpdate("call SYSCS_UTIL.SYSCS_SET_RUNTIMESTATISTICS(1)");
s.executeQuery("select template.id from " +
"--DERBY-PROPERTIES joinOrder=fixed \n" +
"test, template --DERBY-PROPERTIES joinStrategy=nestedLoop, " +
"index=template_102 \n" +
"where test.hundred = template.hundred " +
"and 1 = template.two").close();
checkEstimatedRowCount(conn,80200.125);
RuntimeStatisticsParser rtsp =
SQLUtilities.getRuntimeStatisticsParser(s);
assertTrue(rtsp.usedSpecificIndexForIndexScan("TEMPLATE", "TEMPLATE_102"));
// hundred and twenty
// we can use statistics for 100,2 to get selectivity for 100 and
// twenty and twenty to get selectivity for 20
// selectivity should 0.01 * 0.05 = 0.0005 -> 80,000
// join on hundred. constant pred on twenty.
// NL, index=null, all rows.
s.executeQuery("select template.id from " +
"--DERBY-PROPERTIES joinOrder=fixed \n" +
"test, template --DERBY-PROPERTIES joinStrategy=nestedLoop, " +
"index=null \n" +
"where test.hundred = template.hundred " +
"and 1 = template.twenty").close();
checkEstimatedRowCount(conn,8020.0125);
// 'join on hundred. constant pred on twenty.
// NL, index=template_102 all rows.
s.executeQuery("select template.id from " +
"--DERBY-PROPERTIES joinOrder=fixed \n" +
"test, template --DERBY-PROPERTIES joinStrategy=nestedLoop, " +
"index=template_102 \n" +
"where test.hundred = template.hundred " +
"and 1 = template.twenty").close();
checkEstimatedRowCount(conn,8020.0125);
rtsp = SQLUtilities.getRuntimeStatisticsParser(s);
assertTrue(rtsp.usedSpecificIndexForIndexScan("TEMPLATE", "TEMPLATE_102"));
}
public void testThreeWayJoins() throws SQLException {
setAutoCommit(false);
Connection conn = getConnection();
Statement s = createStatement();
// t1 x t2 yields 8000 rows.
// x t3 yields 8*4 * 10^6 /2 = 16*10^6
s.executeUpdate("CALL SYSCS_UTIL.SYSCS_UPDATE_STATISTICS" +
"('APP','T1',NULL)");
s.executeUpdate("CALL SYSCS_UTIL.SYSCS_UPDATE_STATISTICS" +
"('APP','T2',NULL)");
s.executeUpdate("CALL SYSCS_UTIL.SYSCS_UPDATE_STATISTICS" +
"('APP','T3',NULL)");
s.executeUpdate("call SYSCS_UTIL.SYSCS_SET_RUNTIMESTATISTICS(1)");
s.executeQuery("select t1.id from " +
"--DERBY-PROPERTIES joinOrder=fixed \n" +
"t1, t2, t3 " +
"where t1.hundred = t2.hundred " +
"and t1.twenty = t2.twenty and " +
"t2.two = t3.two").close();
checkEstimatedRowCount(conn,1.606007503125E7);
RuntimeStatisticsParser rtsp =
SQLUtilities.getRuntimeStatisticsParser(s);
assertTrue(rtsp.findString("Table Scan ResultSet for T1", 1));
assertTrue(rtsp.findString("Bit set of columns fetched={0, 2, 3}", 1));
assertTrue(rtsp.findString("Hash Scan ResultSet for T2", 1));
assertTrue(rtsp.findString("Bit set of columns fetched={1, 2, 3}", 1));
assertTrue(rtsp.findString(
"Hash Scan ResultSet for T3 using index T3_TWO_TWENTY", 1));
// t1 x t2 -> 16 * 10^4.
// x t3 -> 32 * 10^7
// additional pred -> 32 * 10^5
s.executeQuery("select t1.id from " +
"--DERBY-PROPERTIES joinOrder=fixed \n" +
"t1, t2, t3 " +
"where t1.hundred = t2.hundred " +
"and t2.two = t3.two and " +
"t1.hundred = t3.hundred").close();
checkEstimatedRowCount(conn,3212015.00625);
rtsp = SQLUtilities.getRuntimeStatisticsParser(s);
assertTrue(rtsp.findString("Table Scan ResultSet for T1", 1));
assertTrue(rtsp.findString("Bit set of columns fetched={0, 3}", 1));
assertTrue(rtsp.findString("Hash Scan ResultSet for T2", 1));
assertTrue(rtsp.findString("Bit set of columns fetched={1, 3}", 1));
assertTrue(rtsp.findString("Hash Scan ResultSet for T3", 1));
assertTrue(rtsp.findString("Bit set of columns fetched={1, 3}", 1));
// variations on above query; try different join strategies
s.executeQuery("select t1.id from " +
"--DERBY-PROPERTIES joinOrder=fixed \n" +
"t1, t2, t3 --DERBY-PROPERTIES joinStrategy=hash \n" +
"where t1.hundred = t2.hundred " +
"and t2.two = t3.two " +
"and t1.hundred = t3.hundred").close();
checkEstimatedRowCount(conn,3212015.00625);
rtsp = SQLUtilities.getRuntimeStatisticsParser(s);
assertTrue(rtsp.findString("Table Scan ResultSet for T1", 1));
assertTrue(rtsp.findString("Bit set of columns fetched={0, 3}", 1));
assertTrue(rtsp.findString("Hash Scan ResultSet for T2", 1));
assertTrue(rtsp.findString("Bit set of columns fetched={1, 3}", 1));
assertTrue(rtsp.findString("Hash Scan ResultSet for T3", 1));
assertTrue(rtsp.findString("Bit set of columns fetched={1, 3}", 1));
s.executeQuery("select t1.id from " +
"--DERBY-PROPERTIES joinOrder=fixed \n" +
"t1, t2, t3 --DERBY-PROPERTIES joinStrategy=nestedLoop \n" +
"where t1.hundred = t2.hundred " +
"and t2.two = t3.two " +
"and t1.hundred = t3.hundred").close();
checkEstimatedRowCount(conn,3212015.00625);
rtsp = SQLUtilities.getRuntimeStatisticsParser(s);
assertTrue(rtsp.findString("Table Scan ResultSet for T1", 1));
assertTrue(rtsp.findString("Bit set of columns fetched={0, 3}", 1));
assertTrue(rtsp.findString("Hash Scan ResultSet for T2", 1));
assertTrue(rtsp.findString("Bit set of columns fetched={1, 3}", 1));
assertTrue(rtsp.usedSpecificIndexForIndexScan("T3", "T3_HUNDRED"));
assertTrue(rtsp.findString("Bit set of columns fetched=All", 1));
s.executeQuery("select t1.id from " +
"--DERBY-PROPERTIES joinOrder=fixed \n" +
"t1, t2 --DERBY-PROPERTIES joinStrategy=hash \n, t3 " +
"where t1.hundred = t2.hundred " +
"and t2.two = t3.two " +
"and t1.hundred = t3.hundred").close();
checkEstimatedRowCount(conn,3212015.00625);
rtsp = SQLUtilities.getRuntimeStatisticsParser(s);
assertTrue(rtsp.findString("Table Scan ResultSet for T1", 1));
assertTrue(rtsp.findString("Bit set of columns fetched={0, 3}", 1));
assertTrue(rtsp.findString("Hash Scan ResultSet for T2", 1));
assertTrue(rtsp.findString("Bit set of columns fetched={1, 3}", 1));
assertTrue(rtsp.findString("Hash Scan ResultSet for T3", 1));
assertTrue(rtsp.findString("Bit set of columns fetched={1, 3}", 1));
s.executeQuery("select t1.id from " +
"--DERBY-PROPERTIES joinOrder=fixed \n" +
"t1, t2 --DERBY-PROPERTIES joinStrategy=hash \n, t3 " +
"where t1.hundred = t2.hundred " +
"and t2.two = t3.two " +
"and t1.hundred = t3.hundred").close();
checkEstimatedRowCount(conn,3212015.00625);
rtsp = SQLUtilities.getRuntimeStatisticsParser(s);
assertTrue(rtsp.findString("Table Scan ResultSet for T1", 1));
assertTrue(rtsp.findString("Bit set of columns fetched={0, 3}", 1));
assertTrue(rtsp.findString("Hash Scan ResultSet for T2", 1));
assertTrue(rtsp.findString("Bit set of columns fetched={1, 3}", 1));
assertTrue(rtsp.findString("Hash Scan ResultSet for T3", 1));
assertTrue(rtsp.findString("Bit set of columns fetched={1, 3}", 1));
// duplicate predicates; this time t1.hundred=?
// will show up twice when t1 is optimized at the end
// selectivity should be same as above
s.executeQuery("select t1.id from " +
"--DERBY-PROPERTIES joinOrder=fixed \n" +
"t2, t3, t1 " +
"where t1.hundred = t2.hundred " +
"and t2.two = t3.two " +
"and t1.hundred = t3.hundred").close();
checkEstimatedRowCount(conn,3212015.00625);
rtsp = SQLUtilities.getRuntimeStatisticsParser(s);
assertTrue(rtsp.findString("Table Scan ResultSet for T2", 1));
assertTrue(rtsp.findString("Bit set of columns fetched={1, 3}", 1));
assertTrue(rtsp.findString("Hash Scan ResultSet for T3", 1));
assertTrue(rtsp.findString("Bit set of columns fetched={1, 3}", 1));
assertTrue(rtsp.findString("Hash Scan ResultSet for T1", 1));
assertTrue(rtsp.findString("Bit set of columns fetched={0, 3}", 1));
// variations on above query; try different join strategies
s.executeQuery("select t1.id from " +
"--DERBY-PROPERTIES joinOrder=fixed \n" +
"t3, t2, t1 --DERBY-PROPERTIES joinStrategy=hash \n" +
"where t1.hundred = t2.hundred " +
"and t2.two = t3.two " +
"and t1.hundred = t3.hundred").close();
checkEstimatedRowCount(conn,3212015.00625);
rtsp = SQLUtilities.getRuntimeStatisticsParser(s);
assertTrue(rtsp.findString("Table Scan ResultSet for T3", 1));
assertTrue(rtsp.findString("Bit set of columns fetched={1, 3}", 1));
assertTrue(rtsp.findString("Hash Scan ResultSet for T2", 1));
assertTrue(rtsp.findString("Bit set of columns fetched={1, 3}", 1));
assertTrue(rtsp.findString("Hash Scan ResultSet for T1", 1));
assertTrue(rtsp.findString("Bit set of columns fetched={0, 3}", 1));
s.executeQuery("select t1.id from " +
"--DERBY-PROPERTIES joinOrder=fixed \n" +
"t3, t2, t1 --DERBY-PROPERTIES joinStrategy=nestedLoop \n" +
"where t1.hundred = t2.hundred " +
"and t2.two = t3.two " +
"and t1.hundred = t3.hundred").close();
checkEstimatedRowCount(conn,3212015.00625);
rtsp = SQLUtilities.getRuntimeStatisticsParser(s);
assertTrue(rtsp.findString("Table Scan ResultSet for T3", 1));
assertTrue(rtsp.findString("Bit set of columns fetched={1, 3}", 1));
assertTrue(rtsp.findString("Hash Scan ResultSet for T2", 1));
assertTrue(rtsp.findString("Bit set of columns fetched={1, 3}", 1));
assertTrue(rtsp.usedSpecificIndexForIndexScan("T1", "T1_HUNDRED"));
assertTrue(rtsp.findString("Bit set of columns fetched=All", 1));
s.executeQuery("select t1.id from " +
"--DERBY-PROPERTIES joinOrder=fixed \n" +
"t2, t3 --DERBY-PROPERTIES joinStrategy=nestedLoop \n, t1 " +
"where t1.hundred = t2.hundred " +
"and t2.two = t3.two " +
"and t1.hundred = t3.hundred").close();
checkEstimatedRowCount(conn,3212015.00625);
rtsp = SQLUtilities.getRuntimeStatisticsParser(s);
assertTrue(rtsp.findString("Table Scan ResultSet for T2", 1));
assertTrue(rtsp.findString("Bit set of columns fetched={1, 3}", 1));
assertTrue(rtsp.usedSpecificIndexForIndexScan("T3", "T3_HUNDRED"));
assertTrue(rtsp.findString("Bit set of columns fetched=All", 1));
assertTrue(rtsp.findString("Hash Scan ResultSet for T1", 1));
assertTrue(rtsp.findString("Bit set of columns fetched={0, 3}", 1));
s.executeQuery("select t1.id from " +
"--DERBY-PROPERTIES joinOrder=fixed \n" +
"t3, t2 --DERBY-PROPERTIES joinStrategy=hash \n, t1 " +
"where t1.hundred = t2.hundred " +
"and t2.two = t3.two " +
"and t1.hundred = t3.hundred").close();
checkEstimatedRowCount(conn,3212015.00625);
rtsp = SQLUtilities.getRuntimeStatisticsParser(s);
assertTrue(rtsp.findString("Table Scan ResultSet for T3", 1));
assertTrue(rtsp.findString("Bit set of columns fetched={1, 3}", 1));
assertTrue(rtsp.findString("Hash Scan ResultSet for T2", 1));
assertTrue(rtsp.findString("Bit set of columns fetched={1, 3}", 1));
assertTrue(rtsp.findString("Hash Scan ResultSet for T1", 1));
assertTrue(rtsp.findString("Bit set of columns fetched={0, 3}", 1));
// some more variations on the above theme
// some constant predicates thrown in.
// remember hundred is a char column
// -- for some reason if you give the constant
// as a numeric argument it doesn't recognize that
// as a constant start/stop value for the index
// The error is that the types must be comparable.
s.executeQuery("select t1.id from " +
"--DERBY-PROPERTIES joinOrder=fixed \n" +
"t2, t3, t1 " +
"where t1.hundred = t2.hundred " +
"and t2.two = t3.two " +
"and t1.hundred = t3.hundred " +
"and t1.hundred='1'").close();
checkEstimatedRowCount(conn,30458.025);
rtsp = SQLUtilities.getRuntimeStatisticsParser(s);
assertTrue(rtsp.usedSpecificIndexForIndexScan("T2", "T2_HUNDRED"));
assertTrue(rtsp.findString("Bit set of columns fetched=All", 1));
assertTrue(rtsp.findString("Hash Scan ResultSet for T3", 1));
assertTrue(rtsp.findString("Bit set of columns fetched={1, 3}", 1));
assertTrue(rtsp.usedSpecificIndexForIndexScan("T1", "T1_HUNDRED"));
assertTrue(rtsp.findString("Bit set of columns fetched=All", 1));
// we have t1.100=t2.100 and t1.100=t3.100, so
// t2.100=t3.100 is redundant.
// row count shouldn't factor in the redundant predicate.
// row count should be 3200000.0
s.executeQuery("select t1.id from " +
"--DERBY-PROPERTIES joinOrder=fixed \n" +
"t2, t3, t1 " +
"where t1.hundred = t2.hundred " +
"and t2.two = t3.two " +
"and t1.hundred = t3.hundred " +
"and t2.hundred = t3.hundred").close();
checkEstimatedRowCount(conn,3212015.00625);
rtsp = SQLUtilities.getRuntimeStatisticsParser(s);
assertTrue(rtsp.findString("Table Scan ResultSet for T2", 1));
assertTrue(rtsp.findString("Bit set of columns fetched={1, 3}", 1));
assertTrue(rtsp.findString("Hash Scan ResultSet for T3", 1));
assertTrue(rtsp.findString("Bit set of columns fetched={1, 3}", 1));
assertTrue(rtsp.findString("Hash Scan ResultSet for T1", 1));
assertTrue(rtsp.findString("Bit set of columns fetched={0, 3}", 1));
// slightly different join predicates-- use composite stats.
// t1 x t2 --> 16 * 10.4.
// x t3 --> 16 * 10.4 * 4000 * 1/40 = 16*10.6
s.executeQuery("select t1.id from " +
"--DERBY-PROPERTIES joinOrder=fixed \n" +
"t2, t3, t1 " +
"where t1.hundred = t2.hundred " +
"and t2.two = t3.two " +
"and t2.twenty = t3.twenty").close();
checkEstimatedRowCount(conn,1.606007503125E7);
rtsp = SQLUtilities.getRuntimeStatisticsParser(s);
assertTrue(rtsp.findString("Table Scan ResultSet for T2", 1));
assertTrue(rtsp.findString("Bit set of columns fetched={1, 2, 3}", 1));
assertTrue(rtsp.findString(
"Hash Scan ResultSet for T3 using index T3_TWO_TWENTY", 1));
assertTrue(rtsp.findString("Bit set of columns fetched={0, 1}", 1));
assertTrue(rtsp.findString("Hash Scan ResultSet for T1", 1));
assertTrue(rtsp.findString("Bit set of columns fetched={0, 3}", 1));
// same as above but muck around with join order.
s.executeQuery("select t1.id from " +
"--DERBY-PROPERTIES joinOrder=fixed \n" +
"t1, t2, t3 " +
"where t1.hundred = t2.hundred " +
"and t2.two = t3.two " +
"and t2.twenty = t3.twenty").close();
checkEstimatedRowCount(conn,1.606007503125E7);
rtsp = SQLUtilities.getRuntimeStatisticsParser(s);
assertTrue(rtsp.findString("Table Scan ResultSet for T1", 1));
assertTrue(rtsp.findString("Bit set of columns fetched={0, 3}", 1));
assertTrue(rtsp.findString("Hash Scan ResultSet for T2", 1));
assertTrue(rtsp.findString("Bit set of columns fetched={1, 2, 3}", 1));
assertTrue(rtsp.findString(
"Hash Scan ResultSet for T3 using index T3_TWO_TWENTY", 1));
assertTrue(rtsp.findString("Bit set of columns fetched={0, 1}", 1));
s.executeQuery("select t1.id from " +
"--DERBY-PROPERTIES joinOrder=fixed \n" +
"t2, t1, t3 " +
"where t1.hundred = t2.hundred " +
"and t2.two = t3.two " +
"and t2.twenty = t3.twenty").close();
checkEstimatedRowCount(conn,1.606007503125E7);
rtsp = SQLUtilities.getRuntimeStatisticsParser(s);
assertTrue(rtsp.findString("Table Scan ResultSet for T2", 1));
assertTrue(rtsp.findString("Bit set of columns fetched={1, 2, 3}", 1));
assertTrue(rtsp.findString("Hash Scan ResultSet for T1", 1));
assertTrue(rtsp.findString("Bit set of columns fetched={0, 3}", 1));
assertTrue(rtsp.findString(
"Hash Scan ResultSet for T3 using index T3_TWO_TWENTY", 1));
assertTrue(rtsp.findString("Bit set of columns fetched={0, 1}", 1));
s.executeQuery("select t1.id from " +
"--DERBY-PROPERTIES joinOrder=fixed \n" +
"t1, t3, t2 " +
"where t1.hundred = t2.hundred " +
"and t2.two = t3.two " +
"and t2.twenty = t3.twenty").close();
checkEstimatedRowCount(conn,1.606007503125E7);
rtsp = SQLUtilities.getRuntimeStatisticsParser(s);
assertTrue(rtsp.findString("Table Scan ResultSet for T1", 1));
assertTrue(rtsp.findString("Bit set of columns fetched={0, 3}", 1));
assertTrue(rtsp.usedSpecificIndexForIndexScan("T3", "T3_TWO_TWENTY"));
assertTrue(rtsp.findString("Bit set of columns fetched={0, 1}", 1));
assertTrue(rtsp.findString("Hash Scan ResultSet for T2", 1));
assertTrue(rtsp.findString("Bit set of columns fetched={1, 2, 3}", 1));
s.executeQuery("select t1.id from " +
"--DERBY-PROPERTIES joinOrder=fixed \n" +
"t3, t2, t1 " +
"where t1.hundred = t2.hundred " +
"and t2.two = t3.two " +
"and t2.twenty = t3.twenty").close();
checkEstimatedRowCount(conn,1.606007503125E7);
rtsp = SQLUtilities.getRuntimeStatisticsParser(s);
assertTrue(rtsp.usedSpecificIndexForIndexScan("T3", "T3_TWO_TWENTY"));
assertTrue(rtsp.findString("Bit set of columns fetched={0, 1}", 1));
assertTrue(rtsp.findString("Hash Scan ResultSet for T2", 1));
assertTrue(rtsp.findString("Bit set of columns fetched={1, 2, 3}", 1));
assertTrue(rtsp.findString("Hash Scan ResultSet for T1", 1));
assertTrue(rtsp.findString("Bit set of columns fetched={0, 3}", 1));
s.executeQuery("select t1.id from " +
"--DERBY-PROPERTIES joinOrder=fixed \n" +
"t3, t1, t2 " +
"where t1.hundred = t2.hundred " +
"and t2.two = t3.two " +
"and t2.twenty = t3.twenty").close();
checkEstimatedRowCount(conn,1.606007503125E7);
rtsp = SQLUtilities.getRuntimeStatisticsParser(s);
assertTrue(rtsp.usedSpecificIndexForIndexScan("T3", "T3_TWO_TWENTY"));
assertTrue(rtsp.findString("Bit set of columns fetched={0, 1}", 1));
assertTrue(rtsp.findString("Table Scan ResultSet for T1", 1));
assertTrue(rtsp.findString("Bit set of columns fetched={0, 3}", 1));
assertTrue(rtsp.findString("Hash Scan ResultSet for T2", 1));
assertTrue(rtsp.findString("Bit set of columns fetched={1, 2, 3}", 1));
// and just for fun, what would we have gotten without statistics.
s.executeQuery("select t1.id from " +
"--DERBY-PROPERTIES useStatistics=false, joinOrder=fixed \n" +
"t3, t1, t2 " +
"where t1.hundred = t2.hundred " +
"and t2.two = t3.two " +
"and t2.twenty = t3.twenty").close();
checkEstimatedRowCount(conn,6.4240300125000015E7);
rtsp = SQLUtilities.getRuntimeStatisticsParser(s);
assertTrue(rtsp.usedSpecificIndexForIndexScan("T3", "T3_TWO_TWENTY"));
assertTrue(rtsp.findString("Bit set of columns fetched={0, 1}", 1));
assertTrue(rtsp.findString("Table Scan ResultSet for T1", 1));
assertTrue(rtsp.findString("Bit set of columns fetched={0, 3}", 1));
assertTrue(rtsp.findString("Hash Scan ResultSet for T2", 1));
assertTrue(rtsp.findString("Bit set of columns fetched={1, 2, 3}", 1));
}
public void testScratch() throws SQLException {
// make sure we do a good job of stats on 1/3
setAutoCommit(false);
Connection conn = getConnection();
Statement s = createStatement();
s.executeUpdate("CALL SYSCS_UTIL.SYSCS_UPDATE_STATISTICS" +
"('APP','T1',NULL)");
s.executeUpdate("CALL SYSCS_UTIL.SYSCS_UPDATE_STATISTICS" +
"('APP','SCRATCH_TABLE',NULL)");
s.executeUpdate("call SYSCS_UTIL.SYSCS_SET_RUNTIMESTATISTICS(1)");
// Note: The original test did the first query *after* the following:
// since the statistics (rowEstimates) are not precise, force a
// checkpoint to force out all the row counts to the container header,
// and for good measure do a count which will update the row counts
// exactly.
// s.executeUpdate("CALL SYSCS_UTIL.SYSCS_CHECKPOINT_DATABASE()");
// But if that's executed, the estimatedRowCount becomes: 2582648.45
// Without the checkpoint, the following select counts are unnecessary.
// assertTableRowCount("T1", 4000);
// assertTableRowCount("SCRATCH_TABLE", 4000);
// preds are on columns 1 and 3
// should use default stats for 100 (0.1) and 0.5 for two
// 16*10.6 * 5*10.-2 = 80*10.4
s.executeQuery("select s.id from " +
"--DERBY-PROPERTIES joinOrder=fixed \n" +
"t1, scratch_table s " +
"where t1.two = s.two " +
"and s.hundred = CAST(CHAR(t1.hundred) AS INTEGER)").close();
checkEstimatedRowCount(conn,802001.25);
RuntimeStatisticsParser rtsp =
SQLUtilities.getRuntimeStatisticsParser(s);
assertTrue(rtsp.usedHashJoin());
// preds are on column 2.
// 0.1 -> 16*10.5
s.executeQuery("select s.id from " +
"--DERBY-PROPERTIES joinOrder=fixed \n" +
"t1, scratch_table s " +
"where t1.twenty = s.twenty").close();
checkEstimatedRowCount(conn,1604002.5);
rtsp = SQLUtilities.getRuntimeStatisticsParser(s);
assertTrue(rtsp.usedSpecificIndexForIndexScan("T1", "T1_TWO_TWENTY"));
assertTrue(rtsp.usedHashJoin());
// preds are on column 2,3.
// 0.01 -> 16*10.4
s.executeQuery("select s.id from " +
"--DERBY-PROPERTIES joinOrder=fixed \n" +
"t1, scratch_table s " +
"where t1.twenty = s.twenty " +
"and s.hundred = CAST(CHAR(t1.hundred) AS INTEGER)").close();
checkEstimatedRowCount(conn,160400.2500000);
rtsp = SQLUtilities.getRuntimeStatisticsParser(s);
assertTrue(rtsp.usedSpecificIndexForIndexScan("T1", "T1_TWENTY_HUNDRED"));
assertTrue(rtsp.usedHashJoin());
}
public void testStatMatcher() throws SQLException {
// test of statistics matcher algorithm; make sure that we choose the
// best statistics (the weight stuff in predicatelist)
setAutoCommit(false);
Connection conn = getConnection();
Statement s = createStatement();
s.executeUpdate("CALL SYSCS_UTIL.SYSCS_UPDATE_STATISTICS" +
"('APP','T1',NULL)");
s.executeUpdate("CALL SYSCS_UTIL.SYSCS_UPDATE_STATISTICS" +
"('APP','T2',NULL)");
s.executeUpdate("call SYSCS_UTIL.SYSCS_SET_RUNTIMESTATISTICS(1)");
// 2,20,100
s.executeQuery("select t1.id from t1, t2 " +
"where t1.two = t2.two " +
"and t1.twenty = t2.twenty " +
"and t1.hundred = t2.hundred").close();
checkEstimatedRowCount(conn,4010.00625);
RuntimeStatisticsParser rtsp =
SQLUtilities.getRuntimeStatisticsParser(s);
assertTrue(rtsp.usedHashJoin());
// now muck around with the order of the predicates
// 2,100,20
s.executeQuery("select t1.id from t1, t2 " +
"where t1.two = t2.two " +
"and t1.hundred = t2.hundred " +
"and t1.twenty = t2.twenty").close();
checkEstimatedRowCount(conn,4010.00625);
rtsp = SQLUtilities.getRuntimeStatisticsParser(s);
assertTrue(rtsp.usedHashJoin());
// 100,20,2
s.executeQuery("select t1.id from t1, t2 " +
"where t1.hundred = t2.hundred " +
"and t1.twenty = t2.twenty " +
"and t1.two = t2.two").close();
checkEstimatedRowCount(conn,4010.00625);
rtsp = SQLUtilities.getRuntimeStatisticsParser(s);
assertTrue(rtsp.usedHashJoin());
// 100,2,20
s.executeQuery("select t1.id from t1, t2 " +
"where t1.hundred = t2.hundred " +
"and t1.two = t2.two " +
"and t1.twenty = t2.twenty").close();
checkEstimatedRowCount(conn,4010.00625);
rtsp = SQLUtilities.getRuntimeStatisticsParser(s);
assertTrue(rtsp.usedHashJoin());
s.executeQuery("select t1.id from t1, t2 " +
"where t1.twenty = t2.twenty " +
"and t1.hundred = t2.hundred " +
"and t1.two = t2.two").close();
checkEstimatedRowCount(conn,4010.00625);
rtsp = SQLUtilities.getRuntimeStatisticsParser(s);
assertTrue(rtsp.usedHashJoin());
s.executeQuery("select t1.id from t1, t2 " +
"where t1.twenty = t2.twenty " +
"and t1.two = t2.two " +
"and t1.hundred = t2.hundred").close();
checkEstimatedRowCount(conn,4010.00625);
rtsp = SQLUtilities.getRuntimeStatisticsParser(s);
assertTrue(rtsp.usedHashJoin());
}
// Beetle was the bug system for Cloudscape, the forerunner
// of Derby. The bug report described a query that was hitting an Error:
// XJ001: Java exception: '2 >=2: java.lang.ArrayIndexOutOfBoundsException
// on a specific query; when running the same query with DERBY-PROPERTIES
// useStatistics=false the same query worked correctly.
// The fix is in org.apache.derby.impl.sql.compile.PredicateList
// referencing beetle 4321.
public void testBeetle4321() throws SQLException {
// test of statistics matcher algorithm; make sure that we choose the
// best statistics (the weight stuff in predicatelist)
setAutoCommit(false);
Connection conn = getConnection();
Statement s = createStatement();
s.executeUpdate("CALL SYSCS_UTIL.SYSCS_UPDATE_STATISTICS" +
"('APP','COMPLEX',NULL)");
s.executeUpdate("CALL SYSCS_UTIL.SYSCS_UPDATE_STATISTICS" +
"('APP','TEMPLATE',NULL)");
s.executeUpdate("call SYSCS_UTIL.SYSCS_SET_RUNTIMESTATISTICS(1)");
s.executeQuery("select t10.two from complex t10, template t20 " +
"where t10.two = 1 " +
"and t10.hundred = 2 " +
"and t10.a = 2 " +
"and t10.b = 2").close();
checkEstimatedRowCount(conn,7945.920000000);
RuntimeStatisticsParser rtsp =
SQLUtilities.getRuntimeStatisticsParser(s);
assertTrue(rtsp.usedSpecificIndexForIndexScan("COMPLEX", "COMPLEXIND"));
assertTrue(rtsp.usedSpecificIndexForIndexScan("TEMPLATE", "TEMPLATE_TWO"));
}
public void testBasic() throws SQLException {
// basic test for update statistics; make sure that statistics with
// correct values are created and dropped and such.
setAutoCommit(false);
Statement s = createStatement();
s.executeUpdate("CALL SYSCS_UTIL.SYSCS_SET_RUNTIMESTATISTICS(1)");
// first on int, multi-column
s.executeUpdate("create table tbasic1 " +
"(c1 int generated always as identity, c2 int, c3 int)");
for (int i=1; i<5 ; i++)
{
for (int j=1 ; j<3 ; j++)
{
for (int c=0; c<2 ; c++)
s.executeUpdate(
"insert into tbasic1 values " +
"(default, " + i + ", " + j + ")");
}
}
// create index should automatically create stats.
s.executeUpdate("create index t1_c1c2 on tbasic1 (c1, c2)");
ResultSet statsrs = s.executeQuery(
"select indexname, stats, ncols from showstats " +
"where indexname like 'T1_C1C2%' " +
"order by indexname, stats, ncols");
JDBC.assertFullResultSet(statsrs, new String[][] {
{"T1_C1C2","numunique= 16 numrows= 16","1"},
{"T1_C1C2","numunique= 16 numrows= 16","2"}});
// index dropped stats should be dropped.
s.executeUpdate("drop index t1_c1c2");
statsrs = s.executeQuery(
"select indexname, stats, ncols from showstats " +
"where indexname like 'T1_C1C2%' order by indexname");
JDBC.assertEmpty(statsrs);
// second part of the test.
// check a few extra types.
s.executeUpdate("create table tbasic2 " +
"(i int not null, " +
"vc varchar(32) not null, " +
"dt date, ch char(20), " +
"constraint pk primary key (i, vc))");
s.executeUpdate("create index tbasic2_i on tbasic2(i)");
s.executeUpdate("create index tbasic2_ch_dt on tbasic2(ch, dt)");
s.executeUpdate("create index tbasic2_dt_vc on tbasic2(dt, vc)");
// do normal inserts.
s.executeUpdate(
"insert into tbasic2 values (1, 'one', '2001-01-01', 'one')");
s.executeUpdate(
"insert into tbasic2 values (2, 'two', '2001-01-02', 'two')");
s.executeUpdate(
"insert into tbasic2 values (3, 'three', '2001-01-03', 'three')");
s.executeUpdate(
"insert into tbasic2 values (1, 'two', '2001-01-02', 'one')");
s.executeUpdate(
"insert into tbasic2 values (1, 'three', '2001-01-03', 'one')");
s.executeUpdate(
"insert into tbasic2 values (2, 'one', '2001-01-01', 'two')");
// figure out the name of the primary key's backing index
statsrs = s.executeQuery(
"select conglomeratename from sys.sysconglomerates " +
"where conglomeratename like 'SQL%'");
statsrs.next();
String backIndName = statsrs.getString("conglomeratename");
s.executeUpdate("CALL SYSCS_UTIL.SYSCS_UPDATE_STATISTICS" +
"('APP','TBASIC2','" + backIndName + "')");
statsrs = s.executeQuery(
"select indexname, stats, ncols from showstats " +
"where indexname like 'SQL%' " +
"order by indexname, stats, ncols");
JDBC.assertFullResultSet(statsrs, new String[][] {
{backIndName,"numunique= 3 numrows= 6","1"},
{backIndName,"numunique= 6 numrows= 6","2"}});
s.executeUpdate("CALL SYSCS_UTIL.SYSCS_UPDATE_STATISTICS" +
"('APP','TBASIC2','TBASIC2_I')");
statsrs = s.executeQuery(
"select indexname, stats, ncols from showstats " +
"where indexname = 'TBASIC2_I' order by indexname");
JDBC.assertFullResultSet(statsrs, new String[][] {
{"TBASIC2_I","numunique= 3 numrows= 6","1"}});
// do another insert then just updstat for whole table.
s.executeUpdate(
"insert into tbasic2 values(2, 'three', '2001-01-03', 'two')");
s.executeUpdate("CALL SYSCS_UTIL.SYSCS_UPDATE_STATISTICS" +
"('APP','TBASIC2',null)");
// make sure that stats are correct.
statsrs = s.executeQuery(
"select indexname, stats, ncols from showstats " +
"where indexname = 'TBASIC2_I' order by indexname");
JDBC.assertFullResultSet(statsrs, new String[][] {
{"TBASIC2_I","numunique= 3 numrows= 7","1"}});
statsrs = s.executeQuery(
"select count(*) from (select distinct i from tbasic2) t");
JDBC.assertFullResultSet(statsrs, new String[][] {{"3"}});
statsrs = s.executeQuery(
"select indexname, stats, ncols from showstats " +
"where indexname = 'TBASIC2_CH_DT' order by indexname, stats");
JDBC.assertFullResultSet(statsrs, new String[][] {
{"TBASIC2_CH_DT","numunique= 3 numrows= 7","1"},
{"TBASIC2_CH_DT","numunique= 7 numrows= 7","2"}});
statsrs = s.executeQuery(
"select count(*) from (select distinct ch from tbasic2) t");
JDBC.assertFullResultSet(statsrs, new String[][] {{"3"}});
statsrs = s.executeQuery(
"select count(*) from (select distinct ch, dt from tbasic2) t");
JDBC.assertFullResultSet(statsrs, new String[][] {{"7"}});
statsrs = s.executeQuery(
"select indexname, stats, ncols from showstats " +
"where indexname = 'TBASIC2_DT_VC' " +
"order by indexname, stats, ncols");
JDBC.assertFullResultSet(statsrs, new String[][] {
{"TBASIC2_DT_VC","numunique= 3 numrows= 7","1"},
{"TBASIC2_DT_VC","numunique= 3 numrows= 7","2"}});
statsrs = s.executeQuery(
"select count(*) from (select distinct dt from tbasic2) t");
JDBC.assertFullResultSet(statsrs, new String[][] {{"3"}});
statsrs = s.executeQuery(
"select count(*) from (select distinct dt, vc from tbasic2) t");
JDBC.assertFullResultSet(statsrs, new String[][] {{"3"}});
statsrs = s.executeQuery(
"select stats, ncols from showstats " +
"where indexname like 'SQL%' order by stats, ncols");
JDBC.assertFullResultSet(statsrs, new String[][] {
{"numunique= 3 numrows= 7","1"},
{"numunique= 7 numrows= 7","2"}});
// delete everything from t2, do bulkinsert see what happens.
assertUpdateCount(s, 7, "delete from tbasic2");
// no material impact on stats
// note; the test didn't actually confirm, here's the expected now
statsrs = s.executeQuery(
"select indexname, stats, ncols from showstats " +
"where indexname like 'TBASIC2%' " +
"order by indexname, stats, ncols");
JDBC.assertFullResultSet(statsrs, new String[][] {
{"TBASIC2_CH_DT","numunique= 3 numrows= 7","1"},
{"TBASIC2_CH_DT","numunique= 7 numrows= 7","2"},
{"TBASIC2_DT_VC","numunique= 3 numrows= 7","1"},
{"TBASIC2_DT_VC","numunique= 3 numrows= 7","2"},
{"TBASIC2_I","numunique= 3 numrows= 7","1"}});
s.executeUpdate("CALL SYSCS_UTIL.SYSCS_UPDATE_STATISTICS" +
"('APP','TBASIC2',null)");
statsrs = s.executeQuery(
"select indexname, stats, ncols from showstats " +
"where indexname like 'TBASIC2%' " +
"order by indexname, stats, ncols");
JDBC.assertEmpty(statsrs);
// Note: the original (Cloudscape) test did a 'bulkinsert'.
// this is now only possible internally, and is used in
// the SYSCS_IMPORT_DATA system procedures.
// Possibly this test can be added onto by moving the
// value intended to be inserted into a data file, and calling
// SYSCS_UTIL.SYSCS_IMPORT_DATA using that.
// The row inserted was:
// "values (2, 'one', '2001-01-01', 'two')");
// subsequently, there was a bulk insert-replace, this is
// no longer supported. We could update the row.
// the replacement was of row:
// "(2, 'one', '2001-01-01', 'two'), " +
// by
// "(1, 'one', '2001-01-01', 'two')");
s.executeUpdate("drop table tbasic2");
// various alter table operations to ensure correctness.
// 1. add and drop constraint.
s.executeUpdate("create table tbasic3 " +
"(x int not null generated always as identity," +
" y int not null, z int)");
s.executeUpdate(
"insert into tbasic3 (y,z) values " +
"(1,1),(1,2),(1,3),(1,null),(2,1),(2,2),(2,3),(2,null)");
// first alter table to add primary key;
s.executeUpdate("alter table tbasic3 " +
"add constraint pk_tbasic3 primary key (x,y)");
statsrs = s.executeQuery(
"select conglomeratename from sys.sysconglomerates " +
"where conglomeratename like 'SQL%'");
statsrs.next();
backIndName = statsrs.getString("conglomeratename");
statsrs = s.executeQuery(
"select stats, ncols from showstats " +
"where indexname like '" + backIndName + "' " +
"order by stats, ncols");
JDBC.assertFullResultSet(statsrs, new String[][] {
{"numunique= 8 numrows= 8","1"},
{"numunique= 8 numrows= 8","2"}});
// now drop the constraint
s.executeUpdate("alter table tbasic3 drop constraint pk_tbasic3");
statsrs = s.executeQuery(
"select stats, ncols from showstats " +
"where indexname like '" + backIndName + "' " +
"order by stats, ncols");
JDBC.assertEmpty(statsrs);
// try compress with tons of rows. you can never tell
// what a few extra pages can do :)
for (int i=0; i<9 ; i++)
s.executeUpdate("insert into tbasic3(y,z) select y,z from tbasic3");
statsrs = s.executeQuery("select count(*) from tbasic3");
JDBC.assertFullResultSet(statsrs, new String[][] {{"4096"}});
s.executeUpdate("create index tbasic3_xy on tbasic3(x,y)");
statsrs = s.executeQuery(
"select indexname, stats, ncols from showstats " +
"where indexname like 'TBASIC3_XY%' " +
"order by indexname, stats, ncols");
JDBC.assertFullResultSet(statsrs, new String[][] {
{"TBASIC3_XY","numunique= 4096 numrows= 4096","1"},
{"TBASIC3_XY","numunique= 4096 numrows= 4096","2"}});
s.executeUpdate("delete from tbasic3 where z is null");
s.executeUpdate("call SYSCS_UTIL.SYSCS_COMPRESS_TABLE" +
"('APP', 'TBASIC3', 0)");
statsrs = s.executeQuery(
"select indexname, stats, ncols from showstats " +
"where indexname like 'TBASIC3_XY%' " +
"order by indexname, stats, ncols");
JDBC.assertFullResultSet(statsrs, new String[][] {
{"TBASIC3_XY","numunique= 3072 numrows= 3072","1"},
{"TBASIC3_XY","numunique= 3072 numrows= 3072","2"}});
s.executeUpdate("drop table tbasic3");
s.executeUpdate("create table tbasic4 " +
"(x int, y int, z int)");
s.executeUpdate("insert into tbasic4 values (1,1,1)");
s.executeUpdate("insert into tbasic4 values (1,2,1)");
s.executeUpdate("insert into tbasic4 values (1,1,2)");
s.executeUpdate("create index tbasic4_x on tbasic4(x)");
s.executeUpdate("create index tbasic4_xy on tbasic4(x,y)");
s.executeUpdate("create index tbasic4_yz on tbasic4(y,z)");
statsrs = s.executeQuery(
"select indexname, stats, ncols from showstats " +
"where indexname like 'TBASIC4%' " +
"order by indexname, stats, ncols");
JDBC.assertFullResultSet(statsrs, new String[][] {
{"TBASIC4_X","numunique= 1 numrows= 3","1"},
{"TBASIC4_XY","numunique= 1 numrows= 3","1"},
{"TBASIC4_XY","numunique= 2 numrows= 3","2"},
{"TBASIC4_YZ","numunique= 2 numrows= 3","1"},
{"TBASIC4_YZ","numunique= 3 numrows= 3","2"}});
// if we drop column x, then stats for tbasic4_x should get dropped
// index tbasic4_xy should get rebuilt to only be on y. so one of the
// stats should be recreated. and tbasic4_yz shouldn remain in its
// entirety.
s.executeUpdate("alter table tbasic4 drop column x");
statsrs = s.executeQuery(
"select indexname, stats, ncols from showstats " +
"where indexname like 'TBASIC4%' " +
"order by indexname, stats, ncols");
JDBC.assertFullResultSet(statsrs, new String[][] {
{"TBASIC4_XY","numunique= 2 numrows= 3","1"},
{"TBASIC4_YZ","numunique= 2 numrows= 3","1"},
{"TBASIC4_YZ","numunique= 3 numrows= 3","2"}});
s.executeUpdate("drop table tbasic4");
// test re tbasic5 were intended to exercise Cloudscape's
// stored prepared statements. This is not supported in Derby
// it also does some drop statistics, but there are already
// other tests that do this. So on to tbasic6.
s.executeUpdate("create table tbasic6 " +
"(i int generated always as identity," +
" j varchar(10))");
s.executeUpdate("create index tbasic6_i on tbasic6(i)");
s.executeUpdate("create index tbasic6_j on tbasic6(j)");
s.executeUpdate("create index tbasic6_ji on tbasic6(j,i)");
char[] alphabet = {'a','b','c','d','e','f','g','h','i'};
for (int i=0; i<alphabet.length-1 ; i++)
s.executeUpdate("insert into tbasic6 " +
"values (default, '" + alphabet[i] + "')");
for (int i=0; i<alphabet.length ; i++)
s.executeUpdate("insert into tbasic6 " +
"values (default, '" + alphabet[i] + "')");
for (int i=0; i<alphabet.length-1 ; i++)
s.executeUpdate("insert into tbasic6 " +
"values (default, '" + alphabet[i] + "')");
s.executeUpdate("CALL SYSCS_UTIL.SYSCS_UPDATE_STATISTICS" +
"('APP','TBASIC6','TBASIC6_J')");
s.executeUpdate("CALL SYSCS_UTIL.SYSCS_UPDATE_STATISTICS" +
"('APP','TBASIC6',NULL)");
statsrs = s.executeQuery(
"select indexname, stats, ncols from showstats " +
"where indexname like 'TBASIC6%' " +
"order by indexname, stats, ncols");
JDBC.assertFullResultSet(statsrs, new String[][] {
{"TBASIC6_I","numunique= 25 numrows= 25","1"},
{"TBASIC6_J","numunique= 9 numrows= 25","1"},
{"TBASIC6_JI","numunique= 25 numrows= 25","2"},
{"TBASIC6_JI","numunique= 9 numrows= 25","1"}});
s.executeUpdate("delete from TBASIC6");
// make the 17th row the same as the 16th;
// make sure when we switch to the next group fetch
// we handle the case correctly.
for (int i=0; i<17 ; i++)
s.executeUpdate("insert into tbasic6 values (default, 'a')");
s.executeUpdate("CALL SYSCS_UTIL.SYSCS_UPDATE_STATISTICS" +
"('APP','TBASIC6',NULL)");
statsrs = s.executeQuery(
"select indexname, stats, ncols from showstats " +
"where indexname like 'TBASIC6%' " +
"order by indexname, stats, ncols");
JDBC.assertFullResultSet(statsrs, new String[][] {
{"TBASIC6_I","numunique= 17 numrows= 17","1"},
{"TBASIC6_J","numunique= 1 numrows= 17","1"},
{"TBASIC6_JI","numunique= 1 numrows= 17","1"},
{"TBASIC6_JI","numunique= 17 numrows= 17","2"}});
s.executeUpdate("drop table tbasic6");
// table with no rows.
s.executeUpdate("create table et (x int, y int)");
s.executeUpdate("create index etx on et(x)");
s.executeUpdate("create index ety on et(y)");
s.executeUpdate("CALL SYSCS_UTIL.SYSCS_UPDATE_STATISTICS" +
"('APP','ET','ETX')");
s.executeUpdate("CALL SYSCS_UTIL.SYSCS_UPDATE_STATISTICS" +
"('APP','ET',NULL)");
statsrs = s.executeQuery(
"select indexname, stats, ncols from showstats " +
"where indexname like 'ET%' " +
"order by indexname, stats, ncols");
JDBC.assertEmpty(statsrs);
s.executeUpdate("drop table et");
// tests for nulls.
s.executeUpdate("create table null_table (x int, y varchar(2))");
s.executeUpdate("create index nt_x on null_table(x desc)");
for (int i=1; i<4 ; i++)
s.executeUpdate("insert into null_table " +
"values (" + i + ", '" + alphabet[i-1] + "')");
for (int c=0; c<2 ; c++)
{
for (int i=1; i<4 ; i++)
s.executeUpdate("insert into null_table " +
"values (null, '" + alphabet[i-1] + "')");
}
s.executeUpdate("CALL SYSCS_UTIL.SYSCS_UPDATE_STATISTICS" +
"('APP','NULL_TABLE',NULL)");
statsrs = s.executeQuery(
"select indexname, stats, ncols from showstats " +
"where indexname = 'NT_X' " +
"order by indexname, stats, ncols");
JDBC.assertFullResultSet(statsrs, new String[][] {
{"NT_X","numunique= 9 numrows= 9","1"}});
// try composite null keys (1,null) is unique from (1,null)
// as is (null,1) from (null,1)
s.executeUpdate("drop index nt_x");
s.executeUpdate("create index nt_yx on null_table(y,x)");
// the first key y has 3 unique values.
// the second key y,x has 9 unique values because of nulls.
statsrs = s.executeQuery(
"select indexname, stats, ncols from showstats " +
"where indexname = 'NT_YX' " +
"order by indexname, stats, ncols");
JDBC.assertFullResultSet(statsrs, new String[][] {
{"NT_YX","numunique= 3 numrows= 9","1"},
{"NT_YX","numunique= 9 numrows= 9","2"}});
}
// drop any tables created during testBasic
protected void tearDown() throws Exception {
Statement s = createStatement();
try {
s.execute("drop table tbasic1");
s.execute("drop table tbasic2");
s.execute("drop table tbasic3");
s.execute("drop table tbasic4");
s.execute("drop table tbasic6");
s.execute("drop table et");
s.execute("drop table null_table");
} catch (SQLException sqle) {
// if it doesn't work, never mind, we'll assume the
// cleanDatabaseSetup will deal with it.
}
s.close();
super.tearDown();
}
}