选择* vs选择列

时间:2010-07-05 14:45:16

标签: sql performance

如果我只需要2/3列并且我查询SELECT *而不是在select查询中提供这些列,那么是否有关于更多/更少I / O或内存的性能下降?

如果我选择*而不需要,可能会出现网络开销。

但是在select操作中,数据库引擎是否始终从磁盘中提取原子元组,还是只提取select操作中请求的那些列?

如果它总是拉取元组,那么I / O开销是相同的。

同时,如果它提取元组,可能会有一个内存消耗用于从元组中删除所请求的列。

因此,如果是这种情况,选择someColumn将比select *

具有更多的内存开销

12 个答案:

答案 0 :(得分:104)

您应该永远(从未)在生产代码中使用SELECT *有几个原因:

  • 由于您未向数据库提供有关所需内容的任何提示,因此首先需要检查表的定义以确定该表上的列。该查找将花费一些时间 - 在单个查询中不会太多 - 但随着时间的推移会增加

  • 如果您只需要2/3列,则选择1/3太多需要从磁盘检索并通过网络发送的数据

  • 如果您开始依赖数据的某些方面,例如返回的列的顺序,一旦重组表并添加新列(或删除现有列),您可能会得到一个令人讨厌的惊喜

  • 在SQL Server中
  • (不确定其他数据库),如果需要列的子集,非聚集索引总是有可能覆盖该请求(包含所需的所有列)。使用SELECT *,您就可以从一开始就放弃这种可能性。在这种特殊情况下,数据将从索引页面中检索(如果那些包含所有必需的列),因此与执行{{1}相比,磁盘I / O 内存开销会小得多查询。

是的,最初需要更多的输入(SQL Server的SQL Prompt之类的工具甚至可以帮助你) - 但实际上这是一个没有任何异常的规则的情况:不要使用SELECT *在您的生产代码中。的 EVER。

答案 1 :(得分:24)

它总是拉取一个元组(除非表格已被垂直分割 - 分成列块),所以,为了回答你问的问题,从性能角度来看并不重要。但是,由于许多其他原因(在下面),您应该始终按名称专门选择所需的列。

它总是拉出一个元组,因为(在我熟悉的每个供应商RDBMS中),所有内容(包括表数据)的底层磁盘存储结构都基于已定义的 I / O Pages < / em> (例如,在SQL Server中,每个页面为8千字节)。并且每个I / O读取或写入都是通过页面进行的。即,每次写入或读取都是完整的数据页面。

由于这种潜在的结构约束,结果是数据库中的每一行数据必须始终位于一个且只有一个页面上。它不能跨越多个数据页面(除了特殊的东西,比如blob,其中实际的blob数据存储在单独的Page-chunk中,而实际的表行列只能获得一个指针......)。但是这些例外仅仅是例外,并且通常不适用,除非在特殊情况下(对于特殊类型的数据,或针对特殊情况的某些优化)
即使在这些特殊情况下,通常,实际的表行数据本身(其中包含指向Blob的实际数据的指针,或其他),它必须存储在单个IO页面上...

异常。 Select *唯一正确的地方位于ExistsNot Exists谓词子句之后的子查询中,如:

   Select colA, colB
   From table1 t1
   Where Exists (Select * From Table2
                 Where column = t1.colA)

编辑:要解决@Mike Sherer的评论,是的,从技术上讲,这是真的,对你的特殊情况和美学有一点定义。首先,即使请求的列集是某些索引中存储的列的子集,查询处理器也必须获取存储在该索引中的每个列,而不仅仅是请求的列,原因相同 - 所有I / O必须在页面中完成,索引数据就像表数据一样存储在IO页面中。因此,如果将索引页面的“元组”定义为索引中存储的列集,则该语句仍然为真。
并且该陈述在美学上是真实的,因为关键是它根据存储在I / O页面中的内容而不是您要求的内容来获取数据,无论您是访问基表I / O页还是索引,都是如此I / O页面。

出于不使用Select *的其他原因,请参阅Why is SELECT * considered harmful?

答案 2 :(得分:20)

始终只 select您实际需要的列。选择less而不是更多的效率永远不会低,而且你也会遇到更少的意外副作用 - 比如在客户端按索引访问结果列,然后通过向表添加新列来使这些索引变得不正确。

[edit]:正在访问。愚蠢的大脑仍在醒来。

答案 3 :(得分:7)

除非您存储大量blob,否则性能不是问题。不使用SELECT *的一个重要原因是,如果您将返回的行用作元组,则列会以模式指定的顺序返回,如果更改,则必须修复所有代码。

另一方面,如果您使用字典样式访问,那么列返回的顺序无关紧要,因为您始终按名称访问它们。

答案 4 :(得分:6)

这让我想起了我正在使用的一个包含blob类型列的表;它通常包含一个JPEG图像,大小为Mb个。

毋庸置疑,我没有SELECT该列,除非我真的需要它。将这些数据浮动 - 特别是当我选择了多行时 - 只是一件麻烦事。

但是,我承认我通常会查询表中的所有列。

答案 5 :(得分:6)

在SQL选择期间,DB始终会引用表的元数据,无论它是SELECT * for SELECT a,b,c ...为什么?因为这是关于系统表格的结构和布局的信息。

必须阅读此信息有两个原因。一,简单地编译语句。它需要确保至少指定一个现有的表。此外,自上次执行语句以来,数据库结构可能已更改。

现在,显然,数据库元数据已缓存在系统中,但它仍然需要处理。

接下来,元数据用于生成查询计划。每次编译语句时都会发生这种情况。同样,这是针对缓存的元数据运行的,但它总是完成。

此处理未执行的唯一时间是数据库使用预编译查询,或缓存了以前的查询。这是使用绑定参数而不是文字SQL的参数。 “SELECT * FROM TABLE WHERE key = 1”是一个与“SELECT * FROM TABLE WHERE key =?”不同的查询。并且“1”在通话中受到约束。

数据库严重依赖页面缓存来完成工作。许多现代数据库都小到足以完全适合内存(或者,我应该说,现代内存足够大以适应许多数据库)。然后,后端的主要I / O成本是日志记录和页面刷新。

但是,如果您仍在为数据库访问磁盘,那么许多系统完成的主要优化是依赖于索引中的数据,而不是表本身。

如果你有:

CREATE TABLE customer (
    id INTEGER NOT NULL PRIMARY KEY,
    name VARCHAR(150) NOT NULL,
    city VARCHAR(30),
    state VARCHAR(30),
    zip VARCHAR(10));

CREATE INDEX k1_customer ON customer(id, name);

然后,如果您执行“SELECT id,name FROM customer WHERE id = 1”,则DB很可能会从索引中提取此数据,而不是从表中提取。

为什么呢?无论如何它都可能使用索引来满足查询(与表扫描相比),即使在where子句中没有使用'name',该索引仍然是查询的最佳选择。

现在数据库具有满足查询所需的所有数据,因此没有理由自己查看表页面。使用索引会减少磁盘流量,因为索引中的行密度与一般表中的行密度相比较高。

这是对某些数据库使用的特定优化技术的手工波形解释。许多人都有几种优化和调整技术。

最后,SELECT *对于您必须手动输入的动态查询非常有用,我从不将它用于“真实代码”。识别单个列为DB提供了可用于优化查询的更多信息,并使您可以更好地控制代码中的模式更改等。

答案 6 :(得分:4)

我认为你的问题没有确切的答案,因为你有思考性能和维护你的应用程序的设施。 Select column更符合select *,但如果您正在开发一个面向对象系统,那么您将使用object.properties并且您可以在应用的任何部分中使用属性,那么您将如果不使用select *并填充所有属性,则需要编写更多方法来获取特殊情况下的属性。您的应用需要使用select *获得良好的性能,在某些情况下,您需要使用select列来提高性能。然后,您将拥有两个更好的世界,可以在您需要性能时编写和维护应用程序和性能。

答案 7 :(得分:3)

这里接受的答案是错误的。当another question被关闭时,我遇到了这个(当我还在写我的答案时 - grr - 因此下面的SQL引用了另一个问题)。

你应该总是使用SELECT属性,属性...... NOT SELECT *

主要是针对性能问题。

  

SELECT name FROM users WHERE name ='John';

不是一个非常有用的例子。请考虑一下:

SELECT telephone FROM users WHERE name='John';

如果(姓名,电话)上有索引,则无需查询表格中的相关值即可解析查询 - 覆盖索引。

此外,假设该表有一个BLOB,其中包含用户的图片,上传的简历和电子表格...... 使用SELECT *将把所有这些信息反馈回DBMS缓冲区(从缓存中强制输出其他有用信息)。然后,它将全部发送到客户端,使用网络上的正常运行时间和客户端上的内存,以获取冗余的数据。

如果客户端以枚举数组(例如PHP的mysql_fetch_array($ x,MYSQL_NUM))检索数据,它也会导致功能问题。也许当代码被写入时,'telephone'是SELECT *返回的第三列,但是有人出现并决定在桌子上添加一个电子邮件地址,位于“电话”之前。所需的字段现在转移到第4列。

答案 8 :(得分:2)

无论采取哪种方式,都有理由做事。我在PostgreSQL上使用SELECT *很多,因为在PostgreSQL中你可以用很多东西来做一些你不能用显式列列表做的事情,特别是在存储过程中。类似地,在Informix中,继承的表树上的SELECT *可以为您提供锯齿状的行,而显式列列表则不能,因为子表中的其他列也会返回。

我在PostgreSQL中执行此操作的主要原因是它确保我获得特定于表格的格式良好的类型。这允许我获取结果并将它们用作PostgreSQL中的表类型。这也允许查询中的选项多于刚性列列表中的更多选项。

另一方面,严格的列列表为您提供应用程序级别检查,表明数据库模式在某些方面没有改变,这可能会有所帮助。 (我在另一个级别进行此类检查。)

至于性能,我倾向于使用VIEW和存储过程返回类型(然后是存储过程中的列列表)。这使我可以控制返回的类型。

但请记住,我通常使用SELECT *来对抗抽象层而不是基表。

答案 9 :(得分:2)

Reference taken from this article:

没有SELECT *: 当您使用“SELECT *”时,您正在从数据库中选择更多列,并且您的应用程序可能不会使用此列中的某些列。 这将在数​​据库系统上产生额外的成本和负载,并在网络上传输更多数据。

使用SELECT *: 如果您有特殊要求并在添加或删除列时按应用程序代码自动处理,则创建动态环境。在这种特殊情况下,您不需要更改应用程序和数据库代码,这将自动影响生产环境。在这种情况下,您可以使用“SELECT *”。

答案 10 :(得分:0)

只是为讨论添加一个细微差别,我在这里看不到:就I / O而言,如果您使用column-oriented storage数据库,那么你可以减少我的工作量/ O如果您只查询某些列。当我们转向SSD时,与面向行的存储相比,其优势可能略小,但是只有读取包含您关注的列的块b)压缩,这通常会大大减少磁盘上数据的大小因此从磁盘读取的数据量。

如果您不熟悉面向列的存储,Postgres的一个实现来自Citus Data,另一个是Greenplum,另一个是Paraccel,另一个(松散地说)是Amazon Redshift。对于MySQL而言,还有Infobright,即现已解散的InfiniDB。其他商业产品包括惠普的Vertica,Sybase IQ,Teradata ......

答案 11 :(得分:-1)

select * from table1 INTERSECT  select * from table2

等于

select distinct t1 from table1 where Exists (select t2 from table2 where table1.t1 = t2 )