24

为什么使用 '*' 构建视图不好?

假设您有一个复杂的连接,并且所有字段都可能在某处使用。

然后你只需要选择需要的字段。

SELECT field1, field2 FROM aview WHERE ...

视图“aview”可能是SELECT table1.*, table2.* ... FROM table1 INNER JOIN table2 ...

如果 table1 和 table2 中的 2 个字段具有相同的名称,我们就会遇到问题。

这仅仅是在视图中使用'*'不好的原因吗?

使用“*”,您可以在不同的上下文中使用视图,因为信息就在那里。

我错过了什么?

问候

4

14 回答 14

37

我认为软件中没有太多“糟糕”的东西,但是有很多东西被滥用了:-)

您给出的示例是 * 可能无法给您期望的原因的原因,我认为还有其他原因。例如,如果基础表发生更改,可能会添加或删除列,使用 * 的视图将继续有效,但可能会破坏任何使用它的应用程序。如果您的视图已明确命名列,那么在进行架构更改时有人会发现问题的可能性更大。

另一方面,您可能实际上希望您的视图愉快地接受对基础表的所有更改,在这种情况下,* 正是您想要的。

更新:我不知道 OP 是否考虑到特定的数据库供应商,但现在很明显,我的最后一句话并不适用于所有类型。我很感谢 user12861 和 Jonny Leeds 指出了这一点,很抱歉我花了 6 年时间来编辑我的答案。

于 2008-11-04T16:56:57.720 回答
19

尽管这里的许多评论都非常好,并且提到了在查询中使用通配符的一个常见问题,例如如果基础表发生更改会导致错误或不同的结果,但另一个没有涉及的问题是优化。提取表的每一列的查询往往不如只提取您实际需要的那些列的查询那么有效。诚然,有时您需要每一列,而主要的 PIA 必须全部引用它们,尤其是在大表中,但如果您只需要一个子集,为什么要用比您需要的更多的列来阻止查询。

于 2008-11-04T17:05:51.073 回答
17

*“ ”不仅在视图中而且在查询中都有风险的另一个原因是列可以更改名称或更改基础表中的位置。使用通配符意味着您的视图无需更改即可轻松适应此类更改。但是,如果您的应用程序在结果集中按位置引用列,或者如果您使用返回以列名作为键的结果集的动态语言,您可能会遇到难以调试的问题。

我一直避免使用通配符。这样,如果列更改名称,我会立即在视图或查询中收到错误,并且我知道在哪里修复它。如果列在基础表中的位置发生变化,则指定视图或查询中列的顺序可以弥补这一点。

于 2008-11-04T17:01:30.287 回答
13

这些其他答案都有好点,但至少在 SQL Server 上它们也有一些错误点。试试这个:

create table temp (i int, j int)
go
create view vtemp as select * from temp
go
insert temp select 1, 1
go
alter table temp add k int
go
insert temp select 1, 1, 1
go
select * from vtemp

SQL Server 在添加时不会了解“新”列。这取决于你想要什么,这可能是好事也可能是坏事,但无论哪种方式,依赖它可能都不好。所以避免它似乎是一个好主意。

对我来说,这种奇怪的行为是避免在视图中选择 * 的最令人信服的理由。

这些评论告诉我 MySQL 有类似的行为,而 Oracle 没有(它将了解表的更改)。这种不一致对我来说是不在视图中使用 select * 的更多理由。

于 2008-11-04T20:30:16.357 回答
11

使用 '*' 进行任何生产都是不好的。它非常适合一次性查询,但在生产代码中,您应该始终尽可能明确。

特别是对于视图,如果基础表添加或删除了列,则在重新编译之前,视图要么是错误的,要么是损坏的。

于 2008-11-04T16:57:40.870 回答
4

SELECT *如果列不在视图外使用,在视图内使用不会产生太多性能开销——优化器会优化它们;SELECT * FROM TheView可能会浪费带宽,就像您通过网络连接拉更多列时一样。

事实上,我发现链接我的数据仓库中的许多大表中的几乎所有列的视图根本没有引入任何性能问题,即使从视图外部请求的这些列相对较少。优化器处理得很好,并且能够很好地将外部过滤条件下推到视图中。

但是,由于上述所有原因,我很少使用SELECT *.

我有一些业务流程,其中许多 CTE 构建在彼此之上,有效地从派生列的派生列构建派生列(希望有一天随着业务合理化和简化这些计算而被重构),在这种情况下,我每次都需要通过所有列,并且我使用SELECT *- 但SELECT *不在基础层使用,仅在第一个 CTE 和最后一个 CTE 之间使用。

于 2008-11-04T17:42:23.713 回答
4

SQL Server 上的情况实际上比@user12861 所暗示的答案更糟糕:如果您SELECT *对多个表使用,将列添加到查询中早期引用的表实际上会导致您的视图假装返回新列的值的旧列。请参见下面的示例:

-- create two tables
CREATE TABLE temp1 (ColumnA INT, ColumnB DATE, ColumnC DECIMAL(2,1))
CREATE TABLE temp2 (ColumnX INT, ColumnY DATE, ColumnZ DECIMAL(2,1))
GO


-- populate with dummy data
INSERT INTO temp1 (ColumnA, ColumnB, ColumnC) VALUES (1, '1/1/1900', 0.5)
INSERT INTO temp2 (ColumnX, ColumnY, ColumnZ) VALUES (1, '1/1/1900', 0.5)
GO


-- create a view with a pair of SELECT * statements
CREATE VIEW vwtemp AS 
SELECT *
FROM temp1 INNER JOIN temp2 ON 1=1
GO


-- SELECT showing the columns properly assigned
SELECT * FROM vwTemp 
GO


-- add a few columns to the first table referenced in the SELECT 
ALTER TABLE temp1 ADD ColumnD varchar(1)
ALTER TABLE temp1 ADD ColumnE varchar(1)
ALTER TABLE temp1 ADD ColumnF varchar(1)
GO


-- populate those columns with dummy data
UPDATE temp1 SET ColumnD = 'D', ColumnE = 'E', ColumnF = 'F'
GO


-- notice that the original columns have the wrong data in them now, causing any datatype-specific queries (e.g., arithmetic, dateadd, etc.) to fail
SELECT *
FROM vwtemp
GO

-- clean up
DROP VIEW vwTemp
DROP TABLE temp2
DROP TABLE temp1
于 2015-10-16T18:51:11.243 回答
3

这是因为您并不总是需要每个变量,并且还要确保您正在考虑您的具体需求。

例如,在您的站点上构建用户列表时,将所有散列密码从数据库中取出是没有意义的,因此选择 * 将是徒劳的。

于 2008-11-04T16:55:42.373 回答
3

曾几何时,我针对另一个数据库(在同一台服务器上)中的表创建了一个视图

Select * From dbname..tablename

然后有一天,将一列添加到目标表中。视图开始返回完全不正确的结果,直到重新部署。


完全不正确:没有行。

这是在 Sql Server 2000 上。

我推测这是因为视图已经捕获了 syscolumns 值,即使我使用了 *.

于 2008-11-04T16:57:20.887 回答
3

SQL 查询基本上是程序员设计的用于某些上下文的功能单元。为了长期稳定性和可支持性(可能由您以外的其他人),功能单元中的所有内容都应该是有目的的,并且应该合理地明显(或记录)为什么存在 - 特别是数据的每个元素。

如果两年后我需要或渴望改变你的查询,我希望在我确信我能搞砸之前彻底了解它。这意味着我需要了解为什么所有列都被调出。(如果您试图在多个上下文中重用查询,则这一点更明显。出于类似的原因,这通常是有问题的。)如果我要在输出中看到与某些目的无关的列,我很确定我不明白它做了什么,为什么,以及改变它的后果是什么。

于 2008-11-06T03:41:32.053 回答
2

使用 *. 一些代码认证引擎将此标记为警告,并建议您仅明确引用必要的列。使用 * 可能会导致性能问题,因为您可能只需要一些列而不是全部。但是,另一方面,在某些情况下使用 * 是理想的。想象一下,无论如何,使用您提供的示例,对于此视图 (aview),您将始终需要这些表中的所有列。将来,当添加列时,您无需更改视图。根据您处理的情况,这可能是好是坏。

于 2008-11-04T17:19:04.870 回答
2

我认为这取决于您使用的语言。当语言或 DB 驱动程序返回结果的 dict(Python、Perl 等)或关联数组(PHP)时,我更喜欢使用 select *。如果您通过名称而不是数组中的索引来引用列,它会使您的代码更容易理解。

于 2008-11-04T18:32:12.490 回答
2

似乎没有其他人提到它,但在 SQL Server 中,您还可以使用schemabinding属性设置您的视图。

这可以防止修改任何会影响视图定义的基表(包括删除它们)。

在某些情况下,这可能对您有用。我意识到我还没有完全回答你的问题,但我想我还是会强调它。

于 2008-11-05T23:44:03.203 回答
1

如果您使用 select * 进行连接,则自动意味着您返回的数据比您需要的更多,因为连接字段中的数据会重复。这是对数据库和网络资源的浪费。

如果您天真地使用调用其他视图的视图,则使用 select * 会使它们的性能更差(这种技术本身不利于性能,调用不需要的多个列会使情况变得更糟)。

于 2010-12-07T15:41:28.667 回答