“SELECT *”为什么是反模式

Tho*_* E. 5 best-practices

关于这里的多个问题和堆栈溢出,我看到人们在评论和答案中说这select * from table几乎总是一种反模式,没有任何解释原因。通过我可以推断出为什么它一个反模式。我可能正在查看其他对问题有更好理解的人注意到的细节。

所以这是我的问题,为什么人们说这select *是一种反模式。

Eri*_*ing 7

我认为SELECT *在 SQL Server 中不使用的最引人注目的两个原因是

  1. 内存授予
  2. 索引使用

内存授予

当查询需要排序、散列或并行时,它们会为这些操作请求内存。内存授权的大小基于数据的大小,包括行和列。

字符串数据对此尤其有影响,因为优化器将定义长度的一半猜测为列的“完整度”。因此,对于 VARCHAR 100,它是 50 个字节 * 行数。

以 Stack Overflow 为例,如果我对 Users 表运行这些查询:

SELECT TOP 1000 DisplayName
FROM dbo.Users AS u
ORDER BY u.Reputation;

SELECT TOP 1000 DisplayName, u.Location
FROM dbo.Users AS u
ORDER BY u.Reputation;
Run Code Online (Sandbox Code Playgroud)

DisplayName 是 NVARCHAR 40,Location 是 NVARCHAR 100。

如果没有信誉索引,SQL Server 需要自行对数据进行排序。

坚果

但它的内存几乎翻了一番。

显示名称:

坚果

显示名称、位置:

坚果

这会变得更糟SELECT *,要求 8.2 GB 的内存:

坚果

它这样做是为了处理需要通过 Sort 运算符传递的大量数据,包括长度为 MAX 的 AboutMe 列。

坚果

索引使用

如果我在用户表上有这个索引:

CREATE NONCLUSTERED INDEX ix_Users
    ON dbo.Users
(
    CreationDate ASC,
    Reputation ASC,
    Id ASC 
 );
Run Code Online (Sandbox Code Playgroud)

我有这个查询,有一个与索引匹配的 WHERE 子句,但不包括/包括查询选择的所有列......

SELECT u.*, 
       p.Id AS [PostId]
FROM   dbo.Users AS u
JOIN   dbo.Posts AS p
ON p.OwnerUserId = u.Id
WHERE  u.CreationDate > '20171001'
       AND u.Reputation > 100
       AND p.PostTypeId = 1
ORDER BY u.Id
Run Code Online (Sandbox Code Playgroud)

优化器可能会选择不使用带键查找的窄索引,而是只扫描聚集索引。

坚果

您要么必须创建一个非常宽的索引,要么尝试重写以选择窄索引,即使使用窄索引会导致查询速度更快。

坚果

CX:

 SQL Server Execution Times:
   CPU time = 6374 ms,  elapsed time = 4165 ms.
Run Code Online (Sandbox Code Playgroud)

数控:

 SQL Server Execution Times:
   CPU time = 1623 ms,  elapsed time = 875 ms.
Run Code Online (Sandbox Code Playgroud)


Phi*_* W. -2

两个主要原因:

首先,您的数据库必须找出正在查询的表上可用的列。这是额外的开销,并且在[旧版本的]较旧的 DBMS 中,如果保存此信息的系统目录表存在大量争用,这可能会成为[非常]显着的性能瓶颈。

其次,它假设您了解有关这些表的一切,并且您始终会了解。数据库本质上是共享资源,随着时间的推移,将有许多人对其进行处理,更改数据结构以跟上不断变化的需求。今天,您可以创建一个包含两列(例如“用户名”和“密码哈希”)的表,并将其用作应用程序登录屏幕的核心。一切都运行良好很长时间......直到有人[其他]出现并把几十个额外的“blob”字段塞进你整洁的小桌子,其中包含巨大的数据或照片或其他什么。突然间,您的登录屏幕速度慢得像爬行一样,因为查询正在拉回所有(“*”)那些额外的、巨大的、网络流量膨胀的字段,而它根本不感兴趣

另外,您可以编写取决于返回字段的顺序的代码(您不应该这样做,但可以)。这在报告圈子中最为常见,特别是当数据被“移交给”用户选择的电子表格程序时。当表结构发生变化时,字段可以“移动”,并且您(他们的?)的数据“突然”开始以“错误”的顺序到达。

始终按照您需要的顺序询问您需要的列。

并且,为了完整起见,请记住“order by”子句 - [关系] 表中的行没有内在顺序

  • 我认为第一段不正确。即使您显式指定所有列,数据库也必须查找它们以验证它们是否存在、它们的数据类型和访问权限。将“*”扩展为值列表很可能是其中最小的部分 (4认同)