在按LIST(n)分区的表中,如何在没有索引的情况下快速SELECT MAX(n)?

时间:2014-08-11 20:49:10

标签: mysql database-partitioning

我创建了一个MyISAM表,它根据year列在不同的分区中存储行,每年给出自己的分区,例如:

CREATE TABLE t (
  id INT UNSIGNED AUTO_INCREMENT
, year SMALLINT(4) UNSIGNED
, ...
, PRIMARY KEY (id, year)
) ENGINE=MyISAM
PARTITION BY LIST (year) (
  PARTITION p0 VALUES IN (2000)
, PARTITION p1 VALUES IN (2001)
, ...
);

我想回答的问题很简单;表中year的最大价值是什么?换句话说:

SELECT MAX(year) FROM t;

从查看表定义看,应该有一个执行计划,该查询在恒定时间内运行,而不用重新排序PK或在year上创建新索引。所有数据库需要做的是找到被定义为具有最大值的分区,然后检查该分区中是否存在任何行;如果没有,请检查下一个最大的,依此类推。不幸的是,优化器选择进行全表扫描。

我可以轻松查询INFORMATION_SCHEMA以查找已定义分区的year的最大值:

SELECT MAX(PARTITION_DESCRIPTION)
FROM INFORMATION_SCHEMA.PARTITIONS
WHERE TABLE_NAME = 't';

但是,这只告诉我关于表中定义的分区,而不是表中存在的行;如果某些分区可能为空,则不一定会给我MAX(year)。我尝试添加EXISTS条件:

SELECT MAX(PARTITION_DESCRIPTION)
FROM INFORMATION_SCHEMA.PARTITIONS
WHERE TABLE_NAME = 't'
    AND EXISTS(SELECT 1 FROM fact_registration 
               WHERE year = PARTITION_DESCRIPTION);

这样可行,但它需要与全表扫描一样长。我还尝试在子查询中进行分区选择,但语法不会接受用户变量或引用:

SELECT MAX(PARTITION_DESCRIPTION)
FROM
    INFORMATION_SCHEMA.PARTITIONS AS P
WHERE
    TABLE_NAME = 'fact_registration'
        AND EXISTS(SELECT 1 FROM fact_registration 
                   PARTITION(P.PARTITION_DESCRIPTION));  # syntax error, unexpected '.'

那么,如何消除表格扫描并在桌面上找到准确的最大值?

1 个答案:

答案 0 :(得分:2)

事实证明,答案就像仔细查看the columns in the INFORMATION_SCHEMA.PARTITIONS table

一样简单
  

TABLE_ROWS:分区中的表行数。

     

对于分区的InnoDB表,行中给出的行数   TABLE_ROWS列只是SQL中使用的估计值   优化,可能并不总是准确。

因此,对于特别是MyISAM引擎,我们可以通过使用以下查询找到没有表扫描或任何其他索引的MAX(year)

SELECT MAX(PARTITION_DESCRIPTION) AS maxyear
FROM
    INFORMATION_SCHEMA.PARTITIONS
WHERE
    TABLE_NAME = 'fact_registration'
        AND TABLE_ROWS > 0;

请注意,这只适用于某些情况 - 如果分区中有多个值,则分区中有行的事实并不一定意味着一个或者其他价值存在;由于PARTITION_DESCRIPTIONlongtext列,因此您提供的整数值将存储为以逗号分隔的字符串。