选择*vs选择列

Nee*_*asu 120 sql performance

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

如果我在不需要的情况下选择*,则可能存在网络开销.

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

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

同时,如果它提取元组,则可能存在从元组中剥离所请求列的内存消耗.

因此,如果是这种情况,选择someColumn将比select*具有更多的内存开销

mar*_*c_s 108

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

  • 既然你没有给你的数据库任何关于你想要的提示,它首先需要检查表的定义,以确定该表上的列.该查找将花费一些时间 - 在单个查询中不多 - 但随着时间的推移它会增加

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

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

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

是的,它最初需要更多的输入(像SQL Prompt for SQL Server 这样的工具甚至可以帮助你) - 但这确实是一个没有任何异常的规则:不要在生产代码中使用SELECT*.EVER.

  • 虽然在实践中与你达成一致,但在从表中获取列数据时,你肯定是正确的,因为这个问题解决了这个问题,但是我强调EVER仍然让我指出这个规则对于所有的Sql查询都不是通用的.特别是,它在EXISTS谓词之后的子查询中使用(如在`Where Exists(Select*From ...`)中使用'Select*'肯定没有问题,并且在某些圈子中被认为是最佳实践. (13认同)
  • @Charles Bretana:是的,`IF EXISTS(SELECT * ...)是一个特例-因为那里并没有真正检索到数据,但这只是对存在性的检查,因此SELECT *并不是问题所在... (3认同)

Cha*_*ana 28

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

它总是拉出一个元组,因为(在我熟悉的每个供应商RDBMS中,所有内容的基础磁盘存储结构(包括表数据)都基于定义的 I/O页面(在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)
Run Code Online (Sandbox Code Playgroud)

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

出于其他原因不使用Select *,请参阅 为什么被SELECT *认为有害?:

  • 这不是真的.最重要的一个例子是当你只需要MySQL中索引列的值时(例如,只是检查行存在),并且你正在使用MyISAM存储引擎,它将从中获取数据MYI文件,可能在内存中,甚至没有进入磁盘! (10认同)
  • 您能否进一步说明“出于许多其他原因”?因为这些对我来说还不清楚。如果性能无关紧要,为什么还要关心请求列名? (2认同)

Don*_*nie 20

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

[编辑]:访问权限.愚蠢的大脑仍在醒来.

  • 很久以前见过这个,初级程序员从表中选择*并对列顺序做出假设; 一旦其他人改变了表格,他的所有代码就会破裂.我们有什么乐趣. (11认同)
  • 一般来说,为了代码可读性而使用列顺序可能是一个坏主意,使用`SELECT*`会更加糟糕. (7认同)
  • 对于我认为乍一看不会想到的边缘情况的+1,客户端的索引和添加/更改的列. (3认同)
  • 哇,在客户端代码中按索引访问列似乎是一个非常糟糕的想法.就此而言,依赖于列以任何方式出现在结果集*中的顺序*对我来说感觉非常肮脏. (2认同)

小智 7

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

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


Ric*_*uen 6

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

毋庸置疑SELECT,除非我真的需要它,否则我没有那个专栏.让这些数据浮动 - 特别是当我选择多行时 - 只是一个麻烦.

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

  • LOB列总是我最喜欢的SELECT*风险的例子.所以我即将投票给你,直到我读到第三段.Tsk,tsk.如果其他开发人员将BLOB添加到当前没有这样一列的表中会发生什么? (20认同)

Wil*_*ung 6

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

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

现在,很明显,DB元数据被缓存在系统中,但它仍然需要处理.

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

唯一没有进行此处理的是DB正在使用预编译查询,或者缓存了先前的查询.这是使用绑定参数而不是文字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);
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

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


小智 5

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


sym*_*ean 5

这里接受的答案是错误的。当另一个问题作为此问题的副本被关闭时,我遇到了这个问题(虽然我仍在写我的答案 - grr - 因此下面的 SQL 引用了另一个问题)。

您应该始终使用 SELECT 属性、属性.... NOT SELECT *

它主要用于性能问题。

SELECT name FROM users WHERE name='John';

不是一个很有用的例子。考虑一下:

SELECT telephone FROM users WHERE name='John';
Run Code Online (Sandbox Code Playgroud)

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

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

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