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

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

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

SELECT field1, field2 FROM aview WHERE ...

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

如果table1和table2中的2个字段具有相同的名称,则会出现问题。

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

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

我错过了什么?

此致

有帮助吗?

解决方案

我不认为软件中有太多“只是坏”的东西,但是有很多东西被滥用的方式很糟糕: - )

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

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

更新:我不知道OP是否有一个特定的数据库供应商,但现在很清楚,我的上一句话并不适用于所有类型。我非常感谢user12861和Jonny Leeds指出这一点,对不起,我花了6年时间来编辑我的答案。

其他提示

虽然这里的许多注释非常好,并且引用了在查询中使用通配符的常见问题,例如在基础表发生更改时导致错误或不同结果,但尚未涵盖的另一个问题是优化。拉取表的每一列的查询往往不如仅提取实际需要的列的查询效率高。当然,有些时候你需要每一列而且它是一个主要的PIA必须全部引用它们,特别是在一个大表中,但是如果你只需要一个子集,为什么要用比你需要的更多的列来阻止查询。

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

我总是避免使用通配符。这样,如果列更改名称,我立即在视图或查询中收到错误,并且我知道在哪里修复它。如果列更改基础表中的位置,则指定视图或查询中列的顺序可以补偿此值。

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

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 *。

使用'*'进行任何生产都很糟糕。这对于一次性查询非常有用,但在生产代码中,您应始终尽可能明确。

特别是对于视图,如果基础表中添加或删除了列,则在重新编译之前视图将会出错或被破坏。

如果在视图外部不使用列,则在视图中使用 SELECT * 不会产生很大的性能开销 - 优化器会优化它们; SELECT * FROM TheView 可能会浪费带宽,就像您通过网络连接提取更多列一样。

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

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

我有一些业务流程,其中许多CTE是相互构建的,有效地从派生列的派生列构建派生列(希望有一天会被重构,因为业务合理化并简化了这些计算),以及在这种情况下,我需要每次都删除所有列,并且我使用 SELECT * - 但 SELECT * 不在基础层使用,仅在第一次CTE和最后一次。

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

这是因为你并不总是需要每个变量,而且还要确保你正在考虑你特别需要的东西。

例如,在您的网站上构建用户列表时,没有必要从数据库中获取所有哈希密码,因此选择*将是无效的。

曾几何时,我使用

创建了另一个数据库(在同一台服务器上)中的表的视图
Select * From dbname..tablename

然后有一天,一个列被添加到目标表中。在重新部署之前,视图开始返回完全错误的结果。


完全错误:没有行。

这是在Sql Server 2000上。

我推测这是因为视图捕获的syscolumns值,即使我使用了*。

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

如果我从现在起两年后出现改变你的查询的需要或愿望,我会期望在我确信自己可以搞砸之前彻底了解它。这意味着我需要了解为什么所有列都被调出。 (如果您尝试在多个上下文中重用查询,则更为明显。由于类似的原因,这通常会产生问题。)如果我在输出中看到与某些目的无关的列,我很确定我不明白它做了什么,为什么,以及改变它会带来什么后果。

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

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

似乎没有其他人提及它,但在SQL Server中,您还可以使用 schemabinding 属性。

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

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

如果您使用select *自动加入意味着您返回的数据超出了您需要的数据,因为重复连接字段中的数据。这浪费了数据库和网络资源。

如果你天真地使用调用其他视图的视图,使用select *可能会使它们表现更差(这种技术本身就不利于性能,调用你不需要的多个列会使它更糟糕)。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top