SQLite中的随机表抽样查询

时间:2016-09-16 23:42:49

标签: sql sqlite

我可以编写典型的“SELECT A,C FROM B WHERE B.id = 10”查询,但是其他方面很少使用SQL,我需要对此查询做的事情远远超出我的SQL经验这一点。

我有一个带有主键Tid的表T和一个SQLite中的特殊列S.我想创建另一个表,它是由Tid值组成的单个列。我需要我的新表为每个可能的S值提供1000个随机的Tid值。

因此,如果我要将S分组为S,那么我的新表将代表每组中的1000个随机原则键。

什么SQLlite查询会创建这样的表?

注意:表T很大,因此性能更好的查询更好。

1 个答案:

答案 0 :(得分:2)

更新:我提出了一个比第一个更简单的解决方案。

首先,我们创建一个表来将结果放入:

CREATE TABLE result(tid integer primary key, s);

在此过程结束时,result将包含原始表格s中每个唯一t值的每组中的1000行。

基本的想法是创造一个魔术"包含一列s的特殊视图,其具有以下属性:每次我们尝试向其中插入s的任何值时,相反,来自t的1000个具有相同值的随机行系统会选择s并将其插入result。我们可以通过触发器来做到这一点:

CREATE TEMP VIEW result_view AS SELECT null AS s WHERE 0;

CREATE TEMP TRIGGER result_ins_trigger
INSTEAD OF INSERT ON result_view FOR EACH ROW
BEGIN
    INSERT INTO result(tid, s)
    SELECT * FROM t WHERE t.s=NEW.s ORDER BY random() LIMIT 1000;
END;

现在我们有了这个视图,我们尝试将s的所有不同值插入其中,触发器将完成其余的操作:

INSERT INTO result_view SELECT DISTINCT s FROM t;

请注意,如果您在t(s)上有索引,则流程会明显加快,因此可能需要先创建(然后在不需要时删除它)。

基本上,上述解决方案是使用触发器的LATERAL子查询的穷人实现。

原始回答:这是一个使用临时触发器的解决方案。首先,我们创建一个表来将结果放入:

CREATE TABLE result(tid integer primary key, s, num NOT NULL);

在此过程结束时,result将包含原始表s中每个唯一t值的每组中的1000行。该表还有一个名为num的辅助列。然后我们执行以下操作:

CREATE INDEX result_s_idx ON result(s, num);

CREATE TEMP VIEW result_view AS SELECT null AS TID, null AS s, NULL AS num WHERE 0;

CREATE TEMP TRIGGER result_ins_trigger
INSTEAD OF INSERT ON result_view FOR EACH ROW
BEGIN
    INSERT INTO result(tid, s, num)
    SELECT NEW.tid, NEW.s,
           COALESCE((SELECT max(num) FROM result WHERE s=NEW.s),0)+1 AS num
    WHERE num<=1000;
END;

INSERT INTO result_view(tid, s, num)
SELECT tid,s,null FROM t ORDER BY random();

DROP INDEX result_s_idx;

我的想法是尝试以随机顺序将t中的所有内容插入到result中,为每一行分配一个数字(存储在num列中),表示什么是该组中该元素的位置(以随机顺序)。所以例如如果num为11,则表示该行是该组的第11行。另外,如果数字大于1000,那么我们丢弃该行,因为每组只需要1000行。为了阻止先前的逻辑,我们使用触发器,以便对于我们尝试插入的每一行,它运行子查询来计算num的正确值,并在num更大时丢弃该行由于sqlite不允许在常规表上使用INSTEAD OF触发器,因此我们在虚拟临时视图上创建该触发器。最后,我们将t中的所有行插入到虚拟视图中(以便触发器以随机顺序启动)。

基本上,上述解决方案是使用触发器的row_number窗口函数的穷人实现。

最后一个问题是num的辅助列result。如果您对此感到困扰,可以通过创建没有它的新表并从result复制所有数据来删除它。