Reputation: 6132
I'm trying to prove a statement for my databases-class. I've stated that using bitmap indexing, a WHERE-query can be executed faster on a table with columns containing only enum values (like gender) than when not using the bitmap indexing.
Additionally to this, we try to prove it won't help on a table with columns containing a lot of different, random values.
We created two tables like described above in Oracle SQLDeveloper, filled with information as explained above in 10000 rows. The version of Oracle is v11.
We used a procedure to store information in the tables. While the enum values randomly are set to 'S', 'M' or 'L', the values for the other table are totally random (numbers and letters).
At first, we ran the statement:
set timing on
SELECT * FROM INDEXTEST2 WHERE GENDER = 'M' AND MARRIED = 'N' AND CHILDREN = 'Y'
Repeatingly running this script ended up on taking 00.016 seconds. After this, we created an index by rightclicking the table, then creating an index. We have selected all columns and checked Bitmap
. After that, we ran the query again a few times, just to encounter the same speed: around 00.015 seconds. We also tried dropping the index, with no results. Are we missing out on something here, or did we do everything correctly and you just can't see a difference in speed?
Upvotes: 1
Views: 2475
Reputation: 11
In addition to having a large enough dataset, have a look at clearing the cache before executing each query
Upvotes: 1
Reputation: 52107
Try selecting fewer rows (relative to all the rows in the table). If a query selects a large proportion of rows in the table, Oracle's query optimizer might (correctly) decide that just doing the full table scan is in fact quicker - if it's going to have to load most or all the blocks of the table heap, it might just as well do it directly.
That aside, your test set is way too small. Try a set large enough (add a few zeros) to push the execution time beyond the resolution of your stopwatch (typically 16 ms on a Windows machine). Even better, pick a size that doesn't completely fit into cache.
But even if you do all that, you might still get some weird results because of the overall "cleverness" of Oracle. For example, Oracle 11g has introduced the query result cache which can completely skew the execution times of repeated queries.
Upvotes: 2
Reputation: 8361
The query is likely very fast even without indexes. So you cannot demonstrate the performance gain with a stop watch.
I've create a test table with 1.000.000 rows. A full table scan without any indexes takes 0.2 seconds on our machine. With bitmap indexs, it takes 0.09 seconds. However, without indexes, it scans all 2000 database blocks, and using bitmap indexes, it needs to read 60-70 blocks.
CREATE TABLE indextest (
id NUMBER NOT NULL,
gender VARCHAR2(1) NOT NULL CHECK (gender IN ('M','F')),
married VARCHAR2(1) NOT NULL CHECK (married IN ('Y','N')),
children VARCHAR2(1) NOT NULL CHECK (children IN ('Y','N'))
) NOLOGGING;
INSERT /*+ APPEND */
INTO indextest (id, gender, married, children)
WITH
q1 AS (SELECT level AS x1 FROM dual CONNECT BY level <= 1000),
q2 AS (SELECT 1000*(level-1) AS x2 FROM dual CONNECT BY level <= 1000),
q3 AS (SELECT x2+x1 AS id FROM q1, q2)
SELECT id,
CASE WHEN dbms_random.value < 0.5 THEN 'M' ELSE 'F' END as gender,
CASE WHEN dbms_random.value < 0.3 THEN 'Y' ELSE 'N' END as married,
CASE WHEN dbms_random.value < 0.2 THEN 'Y' ELSE 'N' END as children
FROM q3;
COMMIT;
EXEC DBMS_STATS.GATHER_TABLE_STATS(user, 'indextest');
ALTER TABLE indextest ADD CONSTRAINT pk_indextest PRIMARY KEY (id);
CREATE BITMAP INDEX bi_indextest_gender ON indextest(gender);
CREATE BITMAP INDEX bi_indextest_married ON indextest(married);
CREATE BITMAP INDEX bi_indextest_children ON indextest(children);
Now, if you show the query stats, for instance in SQL*Plus, you can show that using the indexes takes 60-70 blocks reads ("consistent gets"):
SET TIMING ON
SET LINE 300
SET AUTOTRACE TRACE EXPLAIN STAT
SELECT count(*) FROM indextest WHERE gender = 'M' AND married = 'N' AND children = 'Y';
Abgelaufen: 00:00:00.07
------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 6 | 63 (0)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | 6 | | |
| 2 | BITMAP CONVERSION COUNT | | 125K| 732K| 63 (0)| 00:00:01 |
| 3 | BITMAP AND | | | | | |
|* 4 | BITMAP INDEX SINGLE VALUE| BI_INDEXTEST_CHILDREN | | | | |
|* 5 | BITMAP INDEX SINGLE VALUE| BI_INDEXTEST_GENDER | | | | |
|* 6 | BITMAP INDEX SINGLE VALUE| BI_INDEXTEST_MARRIED | | | | |
------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
4 - access("CHILDREN"='Y')
5 - access("GENDER"='M')
6 - access("MARRIED"='N')
Statistiken
----------------------------------------------------------
0 recursive calls
0 db block gets
67 consistent gets
0 physical reads
0 redo size
235 bytes sent via SQL*Net to client
252 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
If you hide the indexes, Oracle needs to scan the whole table:
ALTER INDEX bi_indextest_gender INVISIBLE;
ALTER INDEX bi_indextest_married INVISIBLE;
ALTER INDEX bi_indextest_children INVISIBLE;
SELECT count(*) FROM indextest WHERE gender = 'M' AND married = 'N' AND children = 'Y';
Abgelaufen: 00:00:00.15
--------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 6 | 512 (6)| 00:00:07 |
| 1 | SORT AGGREGATE | | 1 | 6 | | |
|* 2 | TABLE ACCESS FULL| INDEXTEST | 125K| 732K| 512 (6)| 00:00:07 |
--------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("GENDER"='M' AND "MARRIED"='N' AND "CHILDREN"='Y')
Statistiken
----------------------------------------------------------
292 recursive calls
0 db block gets
2289 consistent gets
0 physical reads
0 redo size
235 bytes sent via SQL*Net to client
252 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
6 sorts (memory)
0 sorts (disk)
1 rows processed
Upvotes: 2
Reputation: 52346
Upvotes: 1