我创建了一个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 '.'
那么,如何消除表格扫描并在桌面上找到准确的最大值?
答案 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_DESCRIPTION
是longtext
列,因此您提供的整数值将存储为以逗号分隔的字符串。