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

可能存在网络开销如果我确实不需要*。

但是在选择操作中,数据库引擎是否总是从磁盘中提取原子元组,还是仅提取在选择操作中请求的那些列? />
如果总是拉出一个元组,那么I / O开销是相同的。

同时,从元组中剥离请求的列可能会占用内存,

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

具有更多的内存开销。

评论

您是否在询问特定的RDBMS? SELECT查询的执行/处理方式可能因数据库而异。

顺便说一句,在PostgreSQL中,如果您说CREATE VIEW foo_view AS SELECT * FROM foo ;,然后稍后在表foo中添加列,则这些列将不会按预期自动显示在foo_view中。换句话说,*在此上下文中仅在视图创建时扩展一次,而不是按SELECT扩展一次。由于ALTER TABLE带来的复杂性,我想(在实践中)*被认为是有害的。

@JoeyAdams-不仅是PostgresQL,这也是Oracle的行为。

@OMG小马:我不知道类似的帖子。但是,这些并不是真正的模仿。 @Lèsemajesté:我说的是通用RDBMS。与任何特定的供应商无关@Joey Adams:嗯,我知道*是不安全的。只想讨论有关的性能问题。

为什么SELECT *被视为有害的可能重复项?

#1 楼

它总是拉一个元组(表被垂直分割的情况除外-分成几列),因此,要回答您提出的问题,从性能的角度来看并不重要。但是,由于许多其他原因,(在下面)您应始终按名称专门选择想要的那些列。

它总是提取一个元组,因为(在我熟悉的每个供应商RDBMS中),所有内容(包括表数据)的底层磁盘存储结构都基于已定义的I / O页(在例如,每个页的SQL Server为8 KB)。并且每个I / O读或写都是按页进行的。也就是说,每次写或读都是完整的数据页。

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

EXCEPTION。 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 *,请参阅为什么SELECT *被认为是有害的? :

评论


您确定“总是拉一个元组”吗?嗯,好的,我是对的。如果那样的话,select *将比select列具有更少的内存开销,但I / O开销相同。所以如果我们留下网络开销。如果*的开销小于select列的开销,则选择*

– Neel Basu
2010年7月5日14:56



这不是真的。我想到的一个例子是,当您只需要MySQL中索引列的值(例如,仅用于检查行是否存在),并且您正在使用MyISAM存储引擎时,它将从数据库中获取数据。 MYI文件,该文件可能在内存中,甚至没有进入磁盘!

– Mike Sherov
2010年7月5日在14:57

是的,如果请求的元组集在内存中,则不会有I / O,但是那是特殊情况。那么什么是夏日。如果我选择一些索引列,那么整个元组都不会读取?否则将读取整个元组?

– Neel Basu
2010年7月5日在15:07

我不确定MySQL的缓存方式,但是在SQL Server和Oracle中,即使数据在内存缓存中,它仍然使用与从磁盘访问数据时相同的Page structre访问它。这意味着每页数据需要一个内存I / O,与磁盘上的I / O完全相同。 (当然,内存I / O比磁盘I / O快得多)。确实,这是缓存设计的目标,以使访问过程完全独立于数据的位置。

–查尔斯·布雷塔纳(Charles Bretana)
2010年7月5日15:11



您能否进一步说明“出于许多其他原因”?因为那些对我来说还不清楚。如果性能无关紧要,为什么还要关心请求列名?

–丹尼斯
17年4月17日在18:57

#2 楼

有几个原因使您绝对不能(永远不要)在生产代码中使用SELECT *


因为您没有给数据库任何有关所需内容的提示,因此首先需要检查表的定义,以确定该表上的列。该查找将花费一些时间-在单个查询中花费不多-但随着时间的推移会累加
如果您只需要2/3的列,则您选择的1/3的数据太多,需要检索从磁盘并通过网络发送
,如果您开始依赖数据的某些方面,例如返回列的顺序,如果需要表的子集,则在重新组织表并在SQL Server中添加新列(或删除现有列)后,您可能会感到讨厌。
在SQL Server中(不确定其他数据库)列,总是有一个非聚集索引可能会覆盖该请求(包含所有需要的列)。使用SELECT *,您从一开始就放弃了这种可能性。在这种特殊情况下,将从索引页中检索数据(如果它们包含所有必要的列),因此与进行SELECT *....查询相比,磁盘I / O和内存开销将少得多。

是的,最初需要花更多的时间输入(SQL Prompt for SQL Server之类的工具甚至可以为您提供帮助)-但这确实是一种有规则而无一例外的情况:永远不要在生产代码中使用SELECT *。永远。

评论


在实践中与您达成一致的同时,从表中获取列数据时,在所有情况下您都是正确的,因为此问题解决了这一点),尽管您对EVER的重视促使我指出该规则并非对所有Sql查询都是通用的。具体来说,它在EXISTS谓词之后的子查询中使用(例如,在“存在(Select * From ...)”中),使用Select *当然不是问题,在某些圈子中,这被认为是最佳实践。

–查尔斯·布雷塔纳(Charles Bretana)
2010年7月5日在16:04



@Charles Bretana:是的,IF EXISTS(SELECT * ...是一个特例-既然那里并没有真正检索到数据,但这只是对存在性的检查,因此SELECT *并不是这里的问题...

– marc_s
2010年7月5日在16:39

如果我正在开发一个API,可以从我的一张表中检索数据怎么办?由于我不知道用户感兴趣的数据,因此我认为SELECT *是否可以接受?

–西蒙·本格森(Simon Bengtsson)
14年2月16日在22:19

@SimonBengtsson:我仍然会反对这一点-假设您不想在表中的特定列中显示某些“管理”数据,而您不想向客户公开这些数据?我总是会明确指定要获取的列的列表

– marc_s
2014-2-17在5:28

确实如此。查询专门为与API一起使用而设置的视图时该怎么办?

–西蒙·本格森(Simon Bengtsson)
2014-2-17在10:28

#3 楼

您应该始终仅对实际需要的列进行select。选择更少而不是更多的效率永远不会降低效率,而且还会遇到更少的意外副作用-例如按索引访问客户端的结果列,然后通过向表中添加新列来使那些索引变得不正确。 />
[edit]:意味着访问。愚蠢的大脑仍在醒来。

评论


我认为乍看之下不会想到的边缘情况是+1,即客户端的索引和增加/更改的列。

– Tomas Aschan
2010年7月5日在14:57

是的,但是对于常见的列使用数字索引是吗?如果使用ORM,我总是使用字符串键或属性名称访问列数据。

–Lèsemajesté
2010年7月5日在15:04

很久以前就看到了这一点,初级程序员从一个表中选择了*并假设了列的顺序;别人换了桌子后,他所有的代码都坏了。我们有什么乐趣。

– Paul McKenzie
2010年7月5日在15:06

通常,仅出于代码可读性的目的而使用列顺序可能是一个坏主意,将SELECT *与它一起使用则加倍不利。

–Lèsemajesté
2010年7月5日15:31



哇,在客户端代码中按索引访问列似乎是个糟糕的主意。因此,依靠列以任何方式出现在结果集中的顺序对我来说是非常肮脏的。

–马特·彼得森(Matt Peterson)
2010年7月8日在3:29

#4 楼

除非存储大的Blob,否则性能不是问题。不使用SELECT *的最大原因是,如果您使用返回的行作为元组,则列会以架构碰巧指定的任何顺序返回,并且如果更改,则必须修复所有代码。

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

#5 楼

这立即让我想到了我正在使用的表,其中包含blob类型的列;它通常包含JPEG图像,大小为Mb

不用说,除非我真正需要它,否则我不会SELECT。使数据浮动(特别是当我选择多行时)只是一个麻烦。

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

评论


LOB列始终是我最喜欢的SELECT *危险示例。因此,我要为你投票,直到我阅读了第三段。 sk,t。如果其他开发人员将BLOB添加到当前没有此类列的表中,会发生什么情况?

– APC
2010年7月5日在14:59

@APC,希望我能更多地支持您的评论。想想您可怜的同事,他只想添加一列而不会导致性能严重下降!想想一下,当他们在几个小时后发现您无辜的选择*时,他们会多么生气。

– Mike Sherov
2010年7月5日在15:04

@ user256007,是的,即使没有BLOB ... BLOB也仅说明了极端示例。检查我对Charles的回答,有时候选择特定的列可以使您甚至不使用磁盘就可以从内存中获取数据!

– Mike Sherov
2010年7月5日在15:09

@Richard,我认为它们非常适合优化数据库性能,而不是您主要关心的问题,这是99%的时间。与大多数框架一样,它们倾向于将事物概括化以实现更快的开发,同时牺牲纯性能。正如Knuth所说:“过早的优化是万恶之源。”当您到达需要担心选择列与选择*的性能的问题时(向Twitter询问RoR),您可以担心它,然后对其进行优化。如果该框架不足以支持该框架,那么我会说您使用了错误的框架。

– Mike Sherov
2010年7月5日在15:30

@ user256007-一般规则是“不要使用SELECT *”。marc_s的答案完全是为什么。

– APC
2010年7月5日在15:37

#6 楼

在执行SQL选择期间,DB总是会引用表的元数据,而不管对SELECT a,b,c而言是否为SELECT *。为什么?因为这是系统上表的结构和布局的信息所在。

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

现在,显然,数据库元数据已缓存在系统中,但仍需要处理。 >
接下来,元数据用于生成查询计划。每次编译语句时也会发生这种情况。同样,这是针对缓存的元数据运行的,但是它始终会完成。

只有当数据库使用预编译查询或缓存了先前的查询时,才完成此处理。这是使用绑定参数而不是文字SQL的参数。 “ SELECT * FROM TABLE WHERE键= 1”与“ SELECT * FROM TABLE WHERE键=?”不同。并且在调用上绑定了“ 1”。

DB严重依赖页面缓存来完成工作。许多现代数据库的大小足以完全容纳在内存中(或者,也许我应该说,现代内存足以容纳许多数据库)。然后,您在后端的主要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,请命名FROM customer WHERE id = 1“,则您的DB很可能会从索引而不是从表中提取此数据。

为什么?无论如何,它都可能会使用索引来满足查询(相对于表扫描),即使在where子句中未使用“名称”,该索引仍将是查询的最佳选择。

现在数据库具有满足查询所需的所有数据,因此没有必要直接访问表页面。使用索引可以减少磁盘访问量,因为索引中的行密度通常比表中的高。

这是一些数据库使用的特定优化技术的简单说明。许多工具都有几种优化和调整技术。

最后,SELECT *对于必须手动键入的动态查询很有用,我永远不会将其用于“真实代码”。识别各个列为DB提供了更多信息,可用于优化查询,并让您更好地控制代码以防架构更改等。

评论


我会否决了您的答案,只是因为您将NOT NULL与PRIMARY KEY一起使用。您有充分的理由这样写吗?

–茉莉
15年1月27日,0:23

#7 楼

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

#8 楼

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

您应该始终使用SELECT属性,属性...。非选择*

主要用于性能问题。


从用户那里选择名称WHERE name ='John';


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

SELECT telephone FROM users WHERE name='John';


如果(名称,电话)上有索引,则可以解析查询而不必从表中查找相关值-

此外,假设该表具有一个包含用户图片的BLOB,一个上载的CV和一个电子表格...
使用SELECT *将拉出所有这些信息返回到DBMS缓冲区(从缓存中排除其他有用信息)。然后,所有这些都将使用网络上的正常运行时间和客户端上用于存储多余数据的内存发送给客户端。

如果客户端以枚举数组的形式检索数据,也会导致功能问题(例如PHP的mysql_fetch_array($ x,MYSQL_NUM))。也许当代码写为“电话”时,这是SELECT *返回的第三列,但随后有人出现,决定将一个电子邮件地址添加到表中,位于“电话”之前。现在,所需的字段已移至第四列。

#9 楼

无论哪种方式都有做事的理由。我在PostgreSQL上经常使用SELECT *,因为在PostgreSQL中可以使用SELECT *做很多事情,而对于显式列列表则不能做,尤其是在存储过程中。类似地,在Informix中,在继承的表树上执行SELECT *会给您带来锯齿状的行,而显式的列列表却不能,因为还返回了子表中的其他列。

我在PostgreSQL中这样做的主要原因是确保我得到了特定于表格的格式正确的类型。这使我可以获取结果并将其用作PostgreSQL中的表类型。与刚性列列表相比,这在查询中还提供了更多的选项。

另一方面,刚性列列表为您提供了应用程序级检查,以确保数据库架构在某些情况下没有发生变化方式,这可能会有所帮助。 (我在另一个级别上进行了此类检查。)关于性能,我倾向于使用VIEWs和存储过程返回类型(然后在存储过程中使用列列表)。这使我可以控制返回什么类型。

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

#10 楼

摘自本文的参考文献:

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

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

#11 楼

只是为了在这里没有看到的讨论中增加一些细微差别:就I / O而言,如果您正在使用具有面向列存储的数据库,则仅查询某些内容就可以减少很多I / O。列。随着我们转向固态硬盘,与面向行的存储相比,它的好处可能会更小,但是有a)只读取包含您关心的列的块b)压缩,这通常会大大减少磁盘上数据的大小,因此从磁盘读取的数据量。

如果您不熟悉面向列的存储,那么Postgres的一种实现来自Citus Data,另一种是Greenplum,另一种是Paraccel,另一种(松散地说)是Amazon。红移。对于MySQL,有Infobright,即已经不复存在的InfiniDB。其他商业产品包括HP的Vertica,Sybase IQ,Teradata ...

#12 楼

select * from table1 INTERSECT  select * from table2


等于

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


评论


您能否通过突出显示代码并按Ctrl + K来格式化代码

– WhatsThePoint
17年7月17日在11:59