firebird icon indicating copy to clipboard operation
firebird copied to clipboard

Ignore NULL when navigating through an index to calculate MIN/MAX

Open sim1984 opened this issue 3 months ago • 0 comments

Firebird 5.0.3

CREATE TABLE TEST (
  ID BIGINT GENERATED BY DEFAULT AS IDENTITY,
  A INT,
  CONSTRAINT PK_TEST_ID PRIMARY KEY(ID)
);

CREATE INDEX TEST_A_IDX ON TEST(A);

SET TERM^;

EXECUTE BLOCK
AS
DECLARE N INTEGER = 0;
BEGIN
  WHILE (N < 100) DO
  BEGIN
    N = N + 1;
    INSERT INTO TEST(A) VALUES(:N);
  END
END^

SET TERM;^

COMMIT;

Let's try to execute the following SQL query.

SELECT MIN(A) FROM TEST;
Select Expression
    -> Aggregate
        -> Table "TEST" Access By ID
            -> Index "TEST_A_IDX" Full Scan

         MIN
============
           1

Current memory = 284733488
Delta memory = 96
Max memory = 284810128
Elapsed time = 0.002 sec
Buffers = 32768
Reads = 0
Writes = 0
Fetches = 3

Very good result. Now let's add NULL values ​​to column A.

SET TERM^;

EXECUTE BLOCK
AS
DECLARE N INTEGER = 0;
BEGIN
  WHILE (N < 1000) DO
  BEGIN
    N = N + 1;
    INSERT INTO TEST(A) VALUES(NULL);
  END
END^

SET TERM;^

COMMIT;

SELECT MIN(A) FROM TEST;
Select Expression
    -> Aggregate
        -> Table "TEST" Access By ID
            -> Index "TEST_A_IDX" Full Scan

         MIN
============
           1

Current memory = 284818048
Delta memory = 0
Max memory = 284881152
Elapsed time = 0.002 sec
Buffers = 32768
Reads = 0
Writes = 0
Fetches = 2003

Apparently, unnecessary readings of records with the NULL key are being performed. If you change the query a little, the performance will return to normal.

SELECT MIN(A) FROM TEST WHERE A > 0;
Select Expression
    -> Aggregate
        -> Filter
            -> Table "TEST" Access By ID
                -> Index "TEST_A_IDX" Range Scan (lower bound: 1/1)

         MIN
============
           1

Current memory = 284832992
Delta memory = 176
Max memory = 284881152
Elapsed time = 0.002 sec
Buffers = 32768
Reads = 0
Writes = 0
Fetches = 3

It would be nice if the optimizer itself set lower/upper bounds when calculating MIN/MAX and ignored scanning of NULL keys in the index.

sim1984 avatar Sep 19 '25 14:09 sim1984