所以我编写了一个python程序来处理一些数据处理
任务。
下面是一个非常简短的规范,使用我想要的计算语言:
parse "%s %lf %s" aa bb cc | group_by aa | quickselect --key=bb 0:5 | \
flatten | format "%s %lf %s" aa bb cc
也就是说,对于每一行,解析出一个单词、一个浮点数和另一个单词。把它们想象成一个球员ID、一个得分和一个约会。我想要每个球员的前五名得分和日期。数据大小不小,但不大;大约630兆字节。
我想知道我应该用什么真正的、可执行的语言来写它
让它同样短(如下面的Python),但要快得多。
#!/usr/bin/python
# -*- coding: utf-8; -*-
import sys
top_5 = {}
for line in sys.stdin:
aa, bb, cc = line.split()
# We want the top 5 for each distinct value of aa. There are
# hundreds of thousands of values of aa.
bb = float(bb)
if aa not in top_5: top_5[aa] = []
current = top_5[aa]
current.append((bb, cc))
# Every once in a while, we drop the values that are not in
# the top 5, to keep our memory footprint down, because some
# values of aa have thousands of (bb, cc) pairs.
if len(current) > 10:
current.sort()
current[:-5] = []
for aa in top_5:
current = top_5[aa]
current.sort()
for bb, cc in current[-5:]:
print aa, bb, cc
以下是一些示例输入数据:
3 1.5 a
3 1.6 b
3 0.8 c
3 0.9 d
4 1.2 q
3 1.5 e
3 1.8 f
3 1.9 g
以下是我从中得到的输出:
3 1.5 a
3 1.5 e
3 1.6 b
3 1.8 f
3 1.9 g
4 1.2 q
3
有七个值,因此我们删除了c
和d
值。因为他们的
bb
值将他们排在前5位。因为4
有只有一个值,它的“前5”只包含一个值。
这比在MySQL中执行相同的查询更快(至少
我们已经找到了解决问题的方法)但我很确定这是在花
大部分时间都在Python字节码解释器中。我觉得在
另一种语言,我可以让它处理数百个
每秒数千行,而不是每分钟。所以我想
用实现速度更快的语言编写它。
但我不知道该选哪种语言。
我还没有弄清楚如何在SQL中将其表示为单个查询,以及
实际上,我对MySQL的能力甚至仅仅是
select * from foo into outfile 'bar';
输入数据。C是一个明显的选择,但是像
line.split()
这样的事情,排序一个列表对于2元组,生成哈希表需要编写一些代码,
不是在标准库中,所以我最终会得到100行代码
或更多,而不是14。
C++似乎是一个更好的选择(它有字符串,地图,
对,和标准库中的向量),但它看起来像代码
会让STL更加混乱。
OCAML很好,但它是否具有相当于
我会为地图的表现感到难过吗?
普通的口齿不清可能奏效?
有没有类似于matlab的数据库计算工具?
让我把循环向下推成快速代码?有人试过吗?
(编辑:通过提供一些示例输入和输出数据来响应Davethgr8的评论,并修复了python程序中的错误!)
(补充编辑:哇,这条评论线到目前为止真是太棒了。谢谢大家!)
编辑:
有一个(谢谢,雷纳!),这里有一个will hartung的
line.split()
脚本,用于生成一些测试数据(尽管它没有真实数据的zipfian分布):BEGIN {
for (i = 0; i < 27000000; i++) {
v = rand();
k = int(rand() * 100);
print k " " v " " i;
}
exit;
}
最佳答案
我很难相信任何不了解数据的脚本(不像mysql那样预先加载了这样的信息)都会比SQL方法更快。
除了解析输入所花费的时间外,脚本还需要“保持”按数组排序等…
下面是对在SQL中应该以适当的速度工作的第一个猜测,假设表的a a、bb、cc列按该顺序有一个索引(*)。(可能的替代方案是“AA,BB DESC,CC”索引
(*)此索引是否可以聚集,不影响以下查询。是否选择集群,以及是否需要“aa、bb、cc”独立索引取决于用例、表中行的大小等。
SELECT T1.aa, T1.bb, T1.cc , COUNT(*)
FROM tblAbc T1
LEFT OUTER JOIN tblAbc T2 ON T1.aa = T2.aa AND
(T1.bb < T2.bb OR(T1.bb = T2.bb AND T1.cc < T2.cc))
GROUP BY T1.aa, T1.bb, T1.cc
HAVING COUNT(*) < 5 -- trick, remember COUNT(*) goes 1,1,2,3,...
ORDER BY T1.aa, T1.bb, T1.cc, COUNT(*) DESC
我们的想法是计算在给定的a a值内有多少个记录小于self。不过,有一个小技巧:我们需要使用左外部联接,以免丢弃具有最大bb值或最后一个bb值的记录(可能恰好是前5个)。由于左连接,计数(*)值计数1、1、2、3、4等,因此HAVING测试“为了模拟op的示例输出,order by使用count()上的desc,可以删除它以获得更传统的前5种类型的列表。另外,如果需要,可以删除选择列表中的count(),这不会影响查询的逻辑和正确排序的能力。
还要注意,这个查询在处理关系方面是确定的,即当给定的一组记录的bb值相同时(在a a组中);我认为当输入数据的顺序发生更改时,python程序可能提供稍微不同的输出,这是因为它的偶然性。排序字典的NAL截断。
真正的解决方案:基于SQL的过程方法
上面描述的自连接方法演示了如何使用声明性语句来表示OP的需求。然而,这种方法在某种意义上是幼稚的,因为它的性能大致上与每个AA“类别”中记录计数的平方和绑定在一起。(不是o(n^2),但大致是o((n/a)^2),其中a是aa列的不同值的数目),换句话说,它对数据的表现很好,这样,与给定aa值相关的记录的数目平均不会超过几十条。如果数据是这样的AA列不是选择性的,那么下面的方法是非常多的!-更合适。它利用了SQL的高效排序框架,同时实现了一种简单的算法,这种算法很难用声明的方式表达。通过在光标中向前(有时向后…)查找下一个a a值,引入一个简单的二进制搜索,这种方法可以进一步改进每个/大多数aa“类别”记录数量特别大的数据集。对于AA“类别”相对于tblabc中的总行数较低的情况,请参阅下一种方法之后的另一种方法。
DECLARE @aa AS VARCHAR(10), @bb AS INT, @cc AS VARCHAR(10)
DECLARE @curAa AS VARCHAR(10)
DECLARE @Ctr AS INT
DROP TABLE tblResults;
CREATE TABLE tblResults
( aa VARCHAR(10),
bb INT,
cc VARCHAR(10)
);
DECLARE abcCursor CURSOR
FOR SELECT aa, bb, cc
FROM tblABC
ORDER BY aa, bb DESC, cc
FOR READ ONLY;
OPEN abcCursor;
SET @curAa = ''
FETCH NEXT FROM abcCursor INTO @aa, @bb, @cc;
WHILE @@FETCH_STATUS = 0
BEGIN
IF @curAa <> @aa
BEGIN
SET @Ctr = 0
SET @curAa = @aa
END
IF @Ctr < 5
BEGIN
SET @Ctr = @Ctr + 1;
INSERT tblResults VALUES(@aa, @bb, @cc);
END
FETCH NEXT FROM AbcCursor INTO @aa, @bb, @cc;
END;
CLOSE abcCursor;
DEALLOCATE abcCursor;
SELECT * from tblResults
ORDER BY aa, bb, cc -- OR .. bb DESC ... for a more traditional order.
当aa非常不选择性时,可选择上述情况。换句话说,当我们有相对较少的AA“类别”时。其思想是浏览不同类别的列表,并为每个值运行“limit”(mysql)“top”(mssql)查询。
为了便于参考,以下代码在63秒内运行,用于6100万条记录的tblabc,除以45个a a值,在MSSQL8.0上,在相对较旧/较弱的主机上。
DECLARE @aa AS VARCHAR(10)
DECLARE @aaCount INT
DROP TABLE tblResults;
CREATE TABLE tblResults
( aa VARCHAR(10),
bb INT,
cc VARCHAR(10)
);
DECLARE aaCountCursor CURSOR
FOR SELECT aa, COUNT(*)
FROM tblABC
GROUP BY aa
ORDER BY aa
FOR READ ONLY;
OPEN aaCountCursor;
FETCH NEXT FROM aaCountCursor INTO @aa, @aaCount
WHILE @@FETCH_STATUS = 0
BEGIN
INSERT tblResults
SELECT TOP 5 aa, bb, cc
FROM tblproh
WHERE aa = @aa
ORDER BY aa, bb DESC, cc
FETCH NEXT FROM aaCountCursor INTO @aa, @aaCount;
END;
CLOSE aaCountCursor
DEALLOCATE aaCountCursor
SELECT * from tblResults
ORDER BY aa, bb, cc -- OR .. bb DESC ... for a more traditional order.
关于是否需要索引的问题。(参考操作说明)
当只运行“select*from mytable”时,表扫描实际上是最快的Appraoch,无需费心索引。然而,SQL通常更适合这种情况的主要原因(除了数据最初是在存储库中积累的,而任何外部解决方案都需要考虑导出相关数据的时间)是它可以依赖于空扫描。许多通用语言更适合处理原始处理,但它们正在与SQL进行不公平的斗争,因为它们需要重新构建SQL在其数据收集/导入阶段收集的数据的任何先前知识。由于排序通常是一项耗时且有时占用空间的任务,因此SQL及其相对较慢的处理能力通常会先于其他解决方案。
此外,即使没有预先构建的索引,现代查询优化器也可以决定一个涉及创建临时索引的计划。而且,由于排序是DDMS的固有部分,所以SQL服务器在该领域通常是高效的。
所以…SQL更好吗?
这就是说,如果我们试图比较纯ETL作业的SQL和其他语言,即处理作为输入的堆(未索引的表),以执行各种转换和过滤,那么很可能多线程的实用程序是用say c编写的,并利用高效的排序库,可能会更快。决定SQL与非SQL方法的决定性问题是数据位于何处以及最终应位于何处。如果我们仅仅是转换一个文件来提供“链”下的外部程序更适合。如果我们在SQL Server中拥有或需要数据,那么只有很少的情况下才有值得从外部导出和处理的数据。
关于python - 我可以使用什么语言来快速执行此数据库摘要任务?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/1467898/