返回的列数会影响查询的速度吗?
Posted
技术标签:
【中文标题】返回的列数会影响查询的速度吗?【英文标题】:Does the number of columns returned affect the speed of a query? 【发布时间】:2009-05-12 12:43:37 【问题描述】:如果我有两个查询
SELECT Id, Forename, Surname
FROM Person
WHERE PersonName Like(‘%frank%’)
和
SELECT *
FROM Person
WHERE PersonName Like(‘%frank%’)
哪个查询会运行得更快?是where子句/表加入最大的因素,还是返回的列数?
我之所以这么问,是因为我正在构建一系列映射到数据库表的对象。通常每个对象都至少有这三种方法:
Select
– 选择所有内容
List
- 选择足够多的可用于填充下拉列表
Search
– 选择结果中可见的所有内容,通常大约 6 列左右。
如果每个查询都返回完全相同的列集,则代码的维护和测试应该更简单。数据库不太可能在任何给定表中超过 50,000 行,因此如果性能差异很小,那么我将节省开发时间。如果性能要跌到地板上,那我会换个角度工作。
那么,为了便于开发,SELECT *
是明智的,还是幼稚的?
【问题讨论】:
一点语法:影响查询的速度。 外交政策可以影响一些事情,但一些列不能 @Quassnoi:可爱,但我还是对的 ;) 他不是想影响速度,而是影响它。 @Andomar:这不是基本上重复Quassnoi的评论,而不影响新知识吗? 【参考方案1】:你最好避开SELECT *
SELECT columns
将只使用该索引,而SELECT *
将需要访问表记录以获取您不需要的值。对性能也不利。
【讨论】:
我同意你的大部分观点,但“列可以得到重复的名称”——这对我来说是个新闻。怎么样? @Pax:如果您在查询中有自连接,或者连接两个具有相同名称的列的表。 @Pax Select * From Person p, Contact c where c.PersonID = p.PersonID;例如,这通常会在 2 个表中包含 [Name] 我的立场是正确的 - 感谢您的说明。我不得不承认我没有考虑多表查询。 +1 抨击你的角色 :-) 我要补充一点,如果某些排除的列(但在使用 SELECT * 时列出)是 TEXT 或类似类型,它将变得非常慢。此外,使用 SELECT * 有时可能会导致一些意想不到的结果,例如***.com/questions/321468/…(我想这可能是“更改表格布局时会导致混乱”的示例之一。)【参考方案2】:SELECT *
通常不是一个好主意。它可能不会大大降低您的 DBMS 获取速度,但可能会导致通过网络传输的数据超出您的需要。
但是,使用基本上不可索引的LIKE '%frank%'
子句可能会使这变得微不足道,并且会导致全表扫描。
您可能需要考虑在数据进入数据库时对其进行清理,因为这几乎肯定会使后续查询运行得更快。
如果您使用 frank,请确保将其存储为 frank 并使用:
select x,y,z from table where name = 'frank'
如果你也想得到富兰克林,请使用:
select x,y,z from table where name like 'frank%'
这两个都可以在名称列上使用索引,"%frank%"
不能。
【讨论】:
【参考方案3】:我要在这里逆流而上,说你应该选择 *.我认为过早的优化是很多问题的根源,当你真正使用它时,你很可能会发现它不会影响你的性能。当然,从书上看,它肯定是慢了,但这并不意味着差异在实践中很重要。
需要注意的是,一些 SQL 引擎(肯定是 MS-SQL)会缓存 select *,所以如果您使用的是准备好的语句,或者拥有它的视图或存储过程,并且更改表架构,除非重新编译视图或 sp,否则它不会接受更改,因此如果您没有动态运行这些查询,这是避免这样做的一个很好的理由。
当然,这因数据库引擎而异,因此需要进行一些负载测试以确保命中不会明显很大。
【讨论】:
【参考方案4】:无论性能问题如何,始终枚举查询中的所有字段都是一种很好的做法。
如果您决定将来添加用于特定查询的 TEXT 或 BLOB 列怎么办?无论您是否需要,您的 SELECT * 都会返回额外的数据。 如果重命名列会怎样?您的 SELECT * 将始终有效,但依赖代码将被破坏。【讨论】:
【参考方案5】:对于小型项目,您通常可以使用select *
。不过,不这样做是“正确的”。在非索引查询中,您不会注意到一个表的任何明显速度差异......您唯一明显做的就是为您不阅读的列使用更多带宽。
也就是说,您会注意到仅索引查询的不同之处在于,当您只需要访问索引时,您访问的是整个表。这会在您进行连接时特别出现。
Select *
确实有用处,如果您正确使用它(例如,结合缓存,确保它是 select table.*
,并按列名寻址结果),您可以减少应用程序进行的查询。
【讨论】:
【参考方案6】:如果从大学时没记错的话(而且已经有一段时间了),选择 * 不是首选,但也不是那么糟糕 - 直到您开始加入。当你进入创建连接元组的关系代数时,每一列都会增加时间,所以如果可能的话,我肯定会避免它。
【讨论】:
【参考方案7】:表中的列数不会影响查询的性能。查询中操作的列数将。
请注意 Oracle 概念手册中的以下示例:
行格式和大小 Oracle 存储每个 数据库表的行包含 少于 256 列的数据为一 或更多行件。如果整行 可以插入单个数据 块,然后 Oracle 将该行存储为 一排片。但是,如果所有 行的数据不能插入到 单个数据块或更新到 现有行导致该行 超出其数据块,然后是 Oracle 使用多行存储行 件。一个数据块通常包含 每行只有一个行件。什么时候 Oracle 必须将一行存储在超过 一排片,它被锁在 多个区块。
当一个表超过 255 时 之后有数据的列、行 第 255 列很可能被链接 在同一块内。这就是所谓的 块内链。链式行 件被链接在一起使用 件的rowids。带块内 链接,用户接收所有数据 在同一个街区。如果行适合 块,用户看不到效果 在 I/O 性能方面,因为没有额外的 需要 I/O 操作才能检索 该行的其余部分。
但是:如果有 400 列,我 打赌大多数行都不适合 在一个街区,因此你会看到一个 更多“数据库文件顺序读取” 比通常需要。同样,我 记得史蒂夫亚当斯(或某人 很久以前)提到有一个 访问列的额外费用 “在列表的下方” - 抱歉不要 有那个链接。
【讨论】:
这是否也适用于 MS Sql?【参考方案8】:如果 person 只有 Id、Forename 和 Surname,则查询应该是等价的。但是,查询时间将与返回的列数(实际数据量)成正比。
另外,如果查询只需要这三列,您应该只要求这三列。如果您 SELECT * 并稍后更改架构,那么您基本上只是为所有查询添加额外的处理,而没有真正的额外好处。
【讨论】:
【参考方案9】:我会访问这个question,了解为什么不首选使用“Select *”构造。
根据我的经验,在 3 列表中选择 3 列与选择 * 可能不会对性能产生明显影响,但随着表变得越来越宽,您会注意到性能差异。
【讨论】:
【参考方案10】:通常,在任何情况下,您都希望远离使用
SELECT * FROM TABLE
在您的代码中。这样做会导致几个问题,其中只有一个是性能问题。我能想到的另外两个是资源利用率(如果您选择不需要的列,或者稍后有人添加列......您正在带回数据并浪费内存)和代码可读性(如果有人在您的代码中看到 SELECT * FROM...他们不一定会知道您的应用程序中实际使用了哪些列)。
只需考虑几件事...但最佳做法是不要使用它。
【讨论】:
【参考方案11】:是的。基本上:
必须从您的数据库服务器传输更多数据 数据库服务器必须获取更多数据你不应该使用 select *
【讨论】:
【参考方案12】:除了其他答案之外,请考虑 SELECT * 将返回查询中所有表的数据。开始通过 JOIN 添加其他表,您将开始看到您不想看到的东西。
我相信我也见过 SELECT * 要求实际从连接表中获取数据的情况,而不是仅使用该表上的索引来帮助缩小整体结果集的范围。不过,我想不出这样的例子。
【讨论】:
【参考方案13】:这有多个维度。一旦 * 将使您的代码更加脆弱。在以后的版本中,您更改依赖于列顺序的表格布局代码可能会中断 - 或者如果数据类型仍然匹配,则可能不会读取或修改错误的列,这可能是一个非常讨厌的问题!
此外,如果您总是请求所有列,您将需要在数据库客户端和数据库服务器上为不需要的列提供更多内存。如果表包含长字符字段、非常多的字段和/或 BLOB,这可能会非常昂贵。选择不必要的列也会使服务器的缓存受到客户端从不查看的多余内容的淹没。
所以一般来说你不应该使用它。大多数对象关系映射工具无论如何都会生成包含所有列名的 SQL,因此在开发过程中这可能无论如何都不是问题。我个人只倾向于将 * 用于必须手动输入的快速临时查询。
【讨论】:
【参考方案14】:这是正确的方法,也是最优化的。原因是您只收集所需的数据,因此在获得结果之前它会占用正确的空间(您需要什么)来存储数据。
SELECT Id, Forename, Surname
FROM Person
WHERE PersonName Like(‘%frank%’)
这是不正确的,因为它占用了未使用的字段,从而占用了更多空间来运行查询,从而减慢了结果。即使您很幸运并使用了查询中的所有字段,最好单独列出它们。这将阐明查询以及将哪些数据返回给将来可能需要修改查询的任何其他开发人员。
SELECT *
FROM Person
WHERE PersonName Like(‘%frank%’)
【讨论】:
【参考方案15】:我唯一一次使用“select *
”并不是真正意义上的“select *
”
具体来说:
select count(*)
from table
不一样
select count(ID)
from table
第一个返回表中的行数 但第二个返回具有 NOT NULL ID 值的行数。
一个微妙的区别,但值得记住。
【讨论】:
另一种可接受的 SELECT * 用法是在 EXISTS 子句的子查询中。【参考方案16】:SELECT * 会更慢,因为它必须传输更多数据。也因为已经提到的其他一些原因。由于您开始添加更多列,因此在连接表时确实会成为一个问题,而您真正想要做的就是连接以便进行过滤。
如果你真的想使用 * ,指定你想要所有列的表,比如 SELECT Person.* FROM Person...
这将缩小返回的数据量并使其更具可读性。
【讨论】:
【参考方案17】:让我扮演魔鬼的拥护者并提出一个选择 * 是更好选择的场景。假设您正在创建一个用户界面,您可以在其中获取数据集的结果并以某种形式的表格或网格显示它。您可以在 UI 中构建列以匹配数据集中的列并执行 SELECT * FROM MyView。
通过在数据库中使用视图,您可以完全控制查询返回的列,并且 UI 可以动态地显示所有列。对视图的更改将立即反映在 UI 中,而无需重新编译和重新编译显然我建议遵循先前的建议并指定视图定义中的所有列。
只是想我会补充一点,因为有时人们会教条地遵循某些规则而忘记了上下文很重要。
【讨论】:
【参考方案18】:当然。更好地命名您要检索的列。
【讨论】:
以上是关于返回的列数会影响查询的速度吗?的主要内容,如果未能解决你的问题,请参考以下文章