SQLite用于VIEW的多个SCAN TABLE

时间:2017-11-17 01:33:46

标签: sql database sqlite

我的 EXPLAIN QUERY PLAN 表示将会多次扫描VIEW 下的表格;情况确实如此,我可以避免吗?

这里有一个类似于我的生产数据的人为举例,它显示了两个已组成的运动队的问题,在这个世界里,我们只信任名字以 D 开头的人,< strong> E ,或 F

CREATE TABLE people (id INTEGER PRIMARY KEY, name TEXT);
CREATE TABLE trust (glob TEXT);
CREATE VIEW trusted_people AS
  SELECT * FROM people JOIN trust ON name GLOB glob;

CREATE TABLE teams (
  keeper INTEGER REFERENCES people(id),
  winger INTEGER REFERENCES people(id),
  scorer INTEGER REFERENCES people(id)
);

INSERT INTO people(name) VALUES ('alf'), ('bob'), ('chad'), ('don'), ('ed'), ('frank');
INSERT INTO trust(glob) VALUES ('d*'), ('e*'), ('f*');
INSERT INTO teams VALUES (1, 2, 3), (4, 5, 6);

EXPLAIN QUERY PLAN SELECT tk.name, tw.name, ts.name FROM teams
  JOIN trusted_people AS tk ON teams.keeper = tk.id
  JOIN trusted_people AS tw ON teams.winger = tw.id
  JOIN trusted_people AS ts ON teams.scorer = ts.id
;

-- SCAN TABLE teams
-- SEARCH TABLE people USING INTEGER PRIMARY KEY (rowid=?)
-- SEARCH TABLE people USING INTEGER PRIMARY KEY (rowid=?)
-- SEARCH TABLE people USING INTEGER PRIMARY KEY (rowid=?)
-- SCAN TABLE trust
-- SCAN TABLE trust
-- SCAN TABLE trust

1 个答案:

答案 0 :(得分:0)

通常,在关系SQL中,视图的执行效果与其基础查询一样。对于视图trusted_people,在加入此视图时无法使用索引,因为这是派生表。因此,联合可能不会如此顺利。更糟糕的是,你加入了这个观点三次。

但是我发现我认为数据库架构中存在问题。我认为通过对teams表进行以下更改,您可以将整个查询减少到只有两个连接:

team_id | player_id | role
1       | 1         | 'keeper'
1       | 2         | 'winger'
1       | 3         | 'scorer'
2       | 4         | 'keeper'
2       | 5         | 'winger'
2       | 6         | 'scorer'

现在,以下查询应该产生您想要的结果:

SELECT t1.*
FROM teams t1
INNER JOIN people p
    ON t1.player_id = p.id
INNER JOIN trust t2
    ON p.name GLOB t2.glob;

如果您想要每个团队的每个匹配玩家的CSV列表,您可以使用:

SELECT
    t1.team_id,
    GROUP_CONCAT(p.name) AS players
FROM teams t1
INNER JOIN people p
    ON t1.player_id = p.id
INNER JOIN trust t2
    ON p.name GLOB t2.glob
GROUP BY
    t1.team_id;

请注意,peopletrust之间的第二次联接仍然可以从某些索引调整中受益,但仅仅进行此更改可能会使性能保持在您可以容忍的水平。