所以,我有一个这样的表,大约有4800万行。
User Count
1232 12
12331 4534
... ...
... ....
这是一个只读表。不会再添加任何数据了。我需要找到两个 userid ...
之间所有用户的计数列总和我现在的查询是:
Select sum(count) from table where user between x and Y.
但每次查询需要超过2秒。我需要彻底降低它。有什么办法可以做到吗?我是否需要创建聚簇索引或您能想到的任何其他配置?
更新:我已经在用户列上有一个索引
答案 0 :(得分:1)
如果您在索引到位时性能不理想,并且您的数据库是只读的,您可能会考虑我在评论中提到的方法:预计算块的总和。它比分区更进一步:分区将能够并行计算您的总和,但是预先计算的总和会大大超过它。理想的块大小大约是表中行数的平方根。
说这是你的桌子:
CREATE TABLE foo (
user INTEGER AUTO_INCREMENT PRIMARY KEY,
cnt INTEGER
);
INSERT INTO foo (cnt) VALUES (1), (4), (9), (16), (25), (36), (49), (64), (81), (100);
现在制作一个预先计算的块总和表。为了清楚起见,我在这里使用SQL变量,你可能不需要使用它们,因为你将从另一种编程语言构造你的查询:
SET @block = 3;
CREATE TABLE foosums (
block INTEGER PRIMARY KEY,
cntsum INTEGER
)
SELECT FLOOR((user - 1) / @block) AS block, SUM(cnt) AS cntsum
FROM foo GROUP BY block;
现在,要计算@from
和@to
之间的总和,您将获取这两者之间所有完整块的总和,并添加块之前和块之后的所有单独行。在这个例子中,要添加行1..10,我们将获取块1 ... 3,块4..6,块7..9和单个行10。
SET @from=1, @to=10;
SELECT
COALESCE((
SELECT SUM(cnt)
FROM foo
WHERE user >= @from AND user < CEILING((@from - 1) / @block) * @block + 1
), 0)
+ COALESCE((
SELECT SUM(cntsum)
FROM foosums
WHERE block >= CEILING((@from - 1) / @block) AND block < FLOOR(@to / @block)
), 0)
+ COALESCE((
SELECT SUM(cnt)
FROM foo
WHERE user > FLOOR(@to / @block) * @block AND user <= @to
), 0)
AS blocked_total;
要验证所有内容是否正常工作,这里是未经优化的查询,而不是使用块总和:
SELECT SUM(cnt) AS individual_total FROM foo WHERE user >= @from AND user <= @to;
最后,可视化可帮助您准确查看优化查询所包含的数据:
SELECT * FROM foo WHERE user >= @from AND user < CEILING((@from - 1) / @block) * @block + 1;
SELECT * FROM foosums WHERE block >= CEILING((@from - 1) / @block) AND block < FLOOR(@to / @block);
SELECT * FROM foo WHERE user > FLOOR(@to / @block) * @block AND user <= @to;
*)“chunk”==“block”。我在文本之前编写了代码,并且不想改变术语:p
答案 1 :(得分:0)
一个小小的技术回答可能来自一个自学成才的人,他不熟悉一些更先进的技术。免责声明完整。这就是我所做的,如果我知道数据不会改变。
我创建一个脚本将其拆分为几十个甚至一百个单独的表,根据范围进行聚类和命名。例如,表一可以命名为#34; cluster_1_to_10000&#34;或者基于你对范围和用户数的了解。
这样,当您进行查询时,您可以根据范围使代码中的表名动态化,并节省时间过滤无聊的数据。它会使PHP?更复杂,特别是如果范围落在多个表之间,但我认为额外的30行左右的PHP代码值得每次跳过几十万行。
不确定这是否得到了解释,如果您愿意,我可以提供一些伪代码示例。
编辑伪代码
名为:&#34; table_1_to_499999&#34;,&#34; table_500000_to_999999&#34;等 您需要编写一个脚本来拆分它们并创建每个表格,并且显然保留原文,以防万一。
伪代码(抱歉不熟悉java):
Var StartTable="";
Var EndTable="";
var Table=array();
Table=//populate table from select tables statement to get them in order
//make each tables key the start value as it pulls the table list from your database
//for example
Table[1]="table_1_to_499999"
Table[500000]="table_500000_to_999999", etc.
//now you have your two user id's
user-id1 and user-id2;
var table1='';
var table2='';
var key1=0;
var key2=0;
foreach(Table as key=>val)
{
if(user_id1>key)
{
table1=val;
key1=key;
}
}
foreach(Table as key=>val)
{
if(user_id2>key)
{
table2=val;
key2=key;
}
}
if(key1==key2)
{
//do your query here, all from the same table, both id's are in the same table, so you can query either table1 or table2 to get your data
}
else if(key1>key2)
{
//query all results greater than or equal to user-id1 in table 1
//query all results less than or equal to user-id2 in table 2
//add the two results together
}
else if(key1<key2)
{
//query all results less than or equal to user-id1 in table 1
//query all results greater than or equal to user-id2 in table 2
//add the two results together
}
我认为这涵盖了它......也许我已经去吃午餐,但我认为这会增加一次复杂性,大大节省查询。你可能不得不做两个查询来提取数据,增加了一些开销,但你只需要查询一百万行而不是4800万行。减少取决于你如何拆分它们。