关于这里的多个问题和堆栈溢出,我看到人们在评论和答案中说这select * from table
几乎总是一种反模式,没有任何解释原因。通过我可以推断出为什么它是一个反模式。我可能正在查看其他对问题有更好理解的人注意到的细节。
所以这是我的问题,为什么人们说这select *
是一种反模式。
我认为SELECT *
在 SQL Server 中不使用的最引人注目的两个原因是
当查询需要排序、散列或并行时,它们会为这些操作请求内存。内存授权的大小基于数据的大小,包括行和列。
字符串数据对此尤其有影响,因为优化器将定义长度的一半猜测为列的“完整度”。因此,对于 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”子句 - [关系] 表中的行没有内在顺序。