SQLite,从同一表中选择最大最近值

euoag5mw  于 2023-10-23  发布在  SQLite
关注(0)|答案(1)|浏览(137)

我有一个这样的数据库模式:

CREATE TABLE callchain(
  seq_no INT,
  entry_oid INT,
  leave_oid,
  tid INT,
  pid INT,
  depth
);
CREATE UNIQUE INDEX callchain_idx on callchain(seq_no);

对于这个表中的每一行,我想选择最近的对应的下一行,其中tidpid是相同的,leave_oid不是null,entry_oid是null,seq_no大于当前seq_no(seq_no对于一个表是唯一的),并且其中深度比当前的深度小1。
我为此写了一个查询:

select *, (select min(seq_no) from callchain where seq_no > cc.seq_no and cc.pid = pid and cc.tid = tid and leave_oid not null and depth = cc.depth -1) from callchain as cc;

不幸的是,这个查询在1000000条记录上花费了太长的时间(几乎是无止境的)(我需要100000000条记录之类的东西……)
基本上,这是一个运行程序的跟踪记录。其中entry_oid在函数入口时不为null,leave_oid在函数出口时不为null。我想找到每个函数入口记录的函数出口记录。例如,有一张table:

seq_no    entry_oid  leave_oid  tid  pid  depth
--------  ---------  ---------  ---  ---  -----
2         117544                24   21   1    
3         117545                24   21   2    
4                    117556     24   21   1    
5                    117557     24   21   0    
6                    117558     24   21   -1   
7         117546                24   21   0    
8         117547                24   21   1    
9                    117559     24   21   0    
10        117548                24   21   1    
11        117549                24   21   2    
12                   117560     24   21   1    
13                   117561     24   21   0    
14                   117562     24   21   -1   
15                   117563     24   21   -2

对于行#7(入口点),对应的出口点将是行#14。
我试着写JOIN查询,如下所示:

select A.seq_no, min(B.seq_no), B.leave_oid from callchain as A inner join callchain as B on A.seq_no < B.seq_no AND A.tid=B.tid AND A.pid=B.pid AND B.leave_oid NOT NULL AND B.depth = A.depth - 1  GROUP BY A.seq_no   ORDER BY A.seq_no;

但我没有大的成功与此查询。这也需要很长的时间。:-(
对于相关子查询,正如我所理解的,数据首先被seq_no除以,然后被pid/tid/depth除以(反之亦然)。并且单一索引不足以满足这两种情况(索引seq_no或索引pid/tid/depth)。在数据被划分之后,先前构建的索引变得无效,所以SQLite引擎构建临时B树,所有这些都变得太慢了。
为什么连接查询很慢我不明白,看起来没有任何临时索引的建立,只是算法复杂度太大。
我想问,我如何重写我的查询和/或重新设计一个数据库,以快速执行我想要的查询(在几秒钟内)?
我使用SQlite引擎作为C++程序的嵌入式数据库。我想避免切换到全功能的DBMS。

qgzx9mmu

qgzx9mmu1#

增加覆盖索引

由于您的查询过滤器和连接在tidpidseq_nodepthleave_oid上,因此在这些列上添加覆盖索引将允许查询纯粹从索引中得到满足,而不会命中主表:

CREATE INDEX callchain_cover_idx ON callchain (tid, pid, seq_no, depth, leave_oid);

首先使用CTE进行过滤

在CTE中首先应用leave_oid和depth的条件,以在连接之前减少数据集:

WITH filtered AS (
  SELECT * FROM callchain
  WHERE leave_oid IS NOT NULL AND depth >= 0
)

SELECT 
  c1.seq_no, 
  MIN(c2.seq_no) as exit_seq_no,
  c2.leave_oid
FROM callchain c1
JOIN filtered c2
ON c1.tid = c2.tid AND 
   c1.pid = c2.pid AND
   c2.depth = c1.depth - 1 AND
   c2.seq_no > c1.seq_no
GROUP BY c1.seq_no
ORDER BY c1.seq_no;

使用IN子查询而不是join

这可能通过直接索引查找执行得更好:

SELECT
  c1.seq_no,
  (
    SELECT MIN(c2.seq_no) 
    FROM filtered c2
    WHERE 
      c2.tid = c1.tid AND
      c2.pid = c1.pid AND  
      c2.depth = c1.depth - 1 AND
      c2.seq_no > c1.seq_no
  ) AS exit_seq_no,
  c1.leave_oid
FROM callchain c1

相关问题