背景: 具有字段ID AUTO INCREMENT PRIMARY KEY的巨大表,以及在行插入操作时存储UNIX时间戳的另一列时间戳。该时间戳列不在任何索引中,由于性能原因,我无法对其进行索引。
情况:我们需要在特定的时间戳值之前查询这个巨大的表中的行;我们可以发出SELECT
语句并在WHERE
子句中指定该条件,但这会导致全表扫描,因为时间戳列未被索引。
建议:这两列的性质随着每行插入而增加:AUTO INCREMENT列增加,timestamp列也增加。我可以每次将表分成两个相等的行,并检查每个边界的时间戳,依此类推,直到我到达一行,然后使用该ID对它进行正常SELECT
。
此解决方案存在问题:这很难,需要大约25个查询才能完成该操作,随着表的增长,数字也会增加。
所以问题是:可以指示MySQL在原子上下文中执行该操作吗?
答案 0 :(得分:3)
我会尝试为您的主要构建一个辅助表,至少作为查询的限制基础。用......之类的东西来填充表格(显然,将indexe放在日期基础上)
create table DailyStartKey as
SELECT
DATE( FROM_UNIXTIME( YourTimeStampColumn ) ) AS DateBasis,
min( YourAutoIncColumn ) as FirstPKForDay
from
YourTable
group by
1
然后,您可以预先查询此表以获得查询中的最小PK(如果查找日期范围,则可能是最大值)。为了保持它的维护而不必继续重建它,我会在你的主表中添加一个触发器,如果尚未插入日期,则尝试插入这个DailyStartKey表。
利用这个的这种查询可能是......
select
YourTable.*
from
( select FirstPKForDay
from DailyStartKey
where DateBasis = "2011-02-12" ) StartDate,
( select FirstPKForDay
from DailyStartKey
where DateBasis = "2011-02-25" ) LastDate,
YourTable
where
YourTable.YourAutoIncColumn >= StartDate.FirstPKForDay
and YourTable.YourAutoIncColumn <= LastDate.FirstPKForDay
编辑澄清另一个TRIGGER实施。
为了防止需要继续查询“DailyStartKey”表,您可以创建另一个表,该表始终只有一条记录与创建条目的最后一天。 (或日/小时取决于您想要的粒度)。
然后,在触发器中查看是否需要新条目,只需
Select * from LastDateEntryTable where LastDate = CurrentDate
如果已找到一个,则忽略..否则,触发器应插入每日密钥表并更新“LastDateEntryTable”。
答案 1 :(得分:1)
即兴创作Drapp的想法:创建一个帮助表和一个触发器,在该表中为每一个100中添加1行。此表将等效于以下视图,但它将在datetimeBasis上有一个索引。
CREATE VIEW HundredRowsStartKey AS
SELECT
YourTimeStampColumn AS datetimeBasis,
YourAutoIncColumn AS id
FROM
YourTable
WHERE
YourAutoIncColumn % 100 = 0
;
还对最终查询详细信息进行了改进,因此它对大表的最多200行进行日期时间扫描。使用索引获取所有其他匹配的行和所需的中间数据:
因此,日期时间范围查询:
SELECT *
FROM
YourTable
WHERE
YourTimeStampColumn BETWEEN "2011-02-12-01.00.23"
AND "2011-03-15-12.00.00"
;
会变成:
WITH starting AS
SELECT
max(id) AS startLow
FROM
HundredRowsStartKey h
WHERE datetimeBasis <= "2011-02-12-01.00.23"
;
WITH ending AS
SELECT
max(id) AS endLow
FROM
HundredRowsStartKey h
WHERE datetimeBasis <= "2011-03-15-12.00.00"
;
SELECT *
FROM
YourTable
WHERE
-- these are guaranteed
( YourAutoIncColumn >= starting.startLow+100
AND YourAutoIncColumn <= ending.endLow-1
)
-- and these 200 we have to filter
OR
( ( YourAutoIncColumn BETWEEN starting.startLow
AND starting.startLow+99
OR
YourAutoIncColumn BETWEEN ending.endLow
AND ending.endLow+99
)
-- with the original filter
AND
( YourTimeStampColumn BETWEEN "2011-02-12-01.00.23"
AND "2011-03-15-12.00.00"
)
)
;
可能需要进行少量编辑以确保捕获所有边缘情况(例如,在表格中所有记录之前开始日期时间等等)
答案 2 :(得分:0)
您是否考虑过根据日期范围对表格进行分区?如果您的查询中包含日期范围&amp;表是分区的,可以避免全表扫描。您也可以对索引进行分区。
http://dev.mysql.com/doc/refman/5.1/en/partitioning-overview.html
http://dev.mysql.com/tech-resources/articles/mysql_5.1_partitions.html