对于小型表而言,是否比大型表更快地查询设定数量的列?

时间:2016-12-23 11:04:52

标签: database postgresql

我正在设计一个包含高分列表的数据库,并且需要快速查找SQL查询。

高分列表中的条目与用户之间存在一对一的关系,但我不需要列表本身的所有用户信息。

所以,我想知道设计在查询数据库方面是否会更快。

设置1:

2个与1对1关系的表。

用户表: ID(PK) - 名称 - 电子邮件 - entryID(FK)

条目表: ID(PK) - 得分 - 消息 - 用户ID(FK)

查询:

SELECT score, message
FROM entries
ORDER BY score desc

设置2:

1表

用户表: ID - 姓名 - 电子邮件 - 得分 - 消息

查询:

SELECT score, message
FROM users
ORDER BY score desc

我想知道的是:当您选择列的子集时,表格中的列数是否会影响查询的速度?

3 个答案:

答案 0 :(得分:1)

如果您的表没有索引,则查询中的列数无关紧要 - 它会扫描包含所有列的块。唯一的影响是返回时间 - 发送给客户端的数据量会有所不同。

如果您计划让查询中的索引和列表包含索引列 - 那么它确实很重要。例如,使用9.6,您可以点击仅索引扫描...

啊,是的,它对于具有TOASTed值的列很重要。

简短的例子:

t=# create table s09 (i int, a text);
CREATE TABLE
t=# insert into s09 select generate_series, 'text' from generate_series(1,9999999,1);
INSERT 0 9999999
t=# analyze s09;
ANALYZE
t=# explain analyze select * from s09;
                                                   QUERY PLAN
-----------------------------------------------------------------------------------------------------------------
 Seq Scan on s09  (cost=0.00..154053.58 rows=9999858 width=9) (actual time=0.010..1712.339 rows=9999999 loops=1)
 Planning time: 0.046 ms
 Execution time: 2825.514 ms
(3 rows)

t=# explain analyze select i from s09;
                                                   QUERY PLAN
-----------------------------------------------------------------------------------------------------------------
 Seq Scan on s09  (cost=0.00..154053.58 rows=9999858 width=4) (actual time=0.010..1828.329 rows=9999999 loops=1)
 Planning time: 0.028 ms
 Execution time: 2937.231 ms
(3 rows)

答案 1 :(得分:1)

从评论中我找到了这个问题的答案。它归结为磁盘IO和缓存命中/未命中率。

如果表行很小,则每个磁盘IO读取的行数更高。因此,使用内存中的数据(选择2列并忽略其他列)将更快,因为它需要更少的磁盘访问。

因此,即使返回的数据量相同,如果您可以将行大小保持在磁盘IO读取块大小以下,您的性​​能也会略有提升。

感谢JosMac让我走上正轨。

编辑:添加分析测试用例和结果。

测试用例1,有两个不同的表但只选择得分,来自一个表的消息:

create table users (
    ID int not null primary key,
    name varchar(50) not null,
    email varchar(50) not null,
    sex int,
    age int,
    country varchar(50),
    occupation varchar(50),
    handedness int);


create table entries (
    ID int not null primary key,
    score int not null,
    message varchar(140) not null,
    userID int not null references users(ID));

create index entry_scores
on entries(score);

insert into users
select  generate_series,
    'name',
    'email@test.com',
    CASE generate_series % 3
        WHEN 0 THEN
            null
        WHEN 1 THEN
            1
        WHEN 2 THEN
            2
    END,
    generate_series,
    'Some country',
    'some occupation',
    CASE generate_series % 3
        WHEN 0 THEN
            null
        WHEN 1 THEN
            1
        WHEN 2 THEN
            2
    END from generate_series(1, 1000000, 1);

insert into entries
select generate_series, generate_series, 'some message', generate_series from generate_series(1, 1000000, 1);

explain analyze select score, message from entries;

这返回了消息:

Seq Scan on entries  (cost=0.00..9117.72 rows=176472 width=302) (actual time=0.019..393.597 rows=1000000 loops=1)

测试案例2,有一个大表但只选择得分,来自它的消息:

create table users (
    ID int not null primary key,
    name varchar(30) not null,
    email varchar(30) not null,
    sex int,
    age int,
    country varchar(50),
    occupation varchar(50),
    handedness int,
    score int not null,
    message varchar(140) not null);

create index user_scores
on users(score);

insert into users
select  generate_series,
    'name',
    'email@test.com',
    CASE generate_series % 3
        WHEN 0 THEN
            null
        WHEN 1 THEN
            1
        WHEN 2 THEN
            2
    END,
    generate_series,
    'Some country',
    'some occupation',
    CASE generate_series % 3
        WHEN 0 THEN
            null
        WHEN 1 THEN
            1
        WHEN 2 THEN
            2
    END,
    generate_series,
    'some message' from generate_series(1, 1000000, 1);

explain analyze select score, message from users;

这返回了消息:

Seq Scan on users  (cost=0.00..15857.46 rows=157146 width=302) (actual time=0.012..485.094 rows=1000000 loops=1)

这表明设置1更快,可能是因为它通过缩小行大小来节省磁盘IO读取。

编辑2:包括正确的分析评估。

答案 2 :(得分:0)

一般情况下,如果您有两个查询,其中Q1选择Q2选择的所有列,并且至少选择另一列,则其他设置相同,那么Q2将明显优于Q1,因为:

  • Q2将减少对磁盘的访问
  • Q2将有更多的数据需要加载,因为必须加载每个结果记录的给定值
  • 如果将Q1和Q2用作子查询,则要加载的列数可能会显着增加加载时间。我不确定这个,但如果RDBMS在每个案例中只执行一次子查询,我可能会错。
  • 列值将存储在内存中,这本身也需要时间和内存
  • 存储在内存中的值必须全部发送

然而,尺寸并不取决于列数,而是取决于它们的大小的平均总和。性能取决于总大小和旅行次数。一般来说,最好只从性能的角度来阅读所需的内容,但也要考虑安全性因素。

在你的问题中,我认为第一个例子是不准确的,你已经加入了现实。如果通过索引外键加入,则查询应该相当快,但不比单个表中的查询快。但是,如果数据可能导致不一致或冗余,则可能需要将数据分成几个表。因此NF应该适用于您的数据库。