English 中文(简体)
为什么使用 * 构建视图不好?
原标题:
  • 时间:2008-11-04 16:52:26
  •  标签:

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

假设您有一项复杂的联接,所有字段都可能在某处使用。

然后你只需选择所需的领域。

SELECT field1, field2 FROM aview WHERE ...

视图"aview"可以是 SELECT table1.*, table2.* ... FROM table1 INNER JOIN table2 ...

如果表1和表2中有两个具有相同名称的字段,我们就会有问题。

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

有了 * ,您可以在不同的情境中使用视图,因为信息已经在那里了。

我错过了什么?

问候

最佳回答

我认为在软件中没有很多东西是“很糟糕的”,但是有很多东西被以糟糕的方式误用。

您所提供的示例是为什么*可能不会给您所期望的原因之一,我认为还有其他原因。例如,如果基础表发生更改,可能添加或删除列,则使用*的视图将继续有效,但可能会破坏使用它的任何应用程序。如果您的视图明确命名了列,则在进行模式更改时更有可能有人发现问题。

On the other hand, you might actually want your view to blithely accept all changes to the underlying tables, in which case a * would be just what you want.

更新:我不知道OP是否有一个特定的数据库供应商在想,但现在清楚的是我的最后一句话并不适用于所有类型。我感激user12861和Jonny Leeds指出这一点,抱歉我花了六年多才编辑我的答案。

问题回答

虽然这里的许多评论都非常好,提到了在查询中使用通配符时出现的一个共同问题,例如如果底层表发生变化会导致错误或不同的结果,但还有一个问题未被涉及:优化。拉取表的每一列的查询往往不如只拉取实际需要的列的查询高效。当然,有时你需要每一列,并且在参考所有列,特别是在大表中的所有列时是一个巨大的麻烦,但是如果你只需要子集,为什么要用比你需要的列更多的列拖慢查询速度呢?

一个使用 "*" 的风险,不仅在视图中也在查询中,是列名可以更改或在底层表中更改位置的原因。使用通配符意味着您的视图可以轻松适应这些更改,而无需进行更改。但如果您的应用程序按位置引用列在结果集中,或者您使用返回按列名键控的结果集的动态语言,则可能会遇到难以调试的问题。

我始终避免使用通配符。这样如果某一列更改名称,我会立即在视图或查询中收到错误提示,我就知道在哪里修复它。如果某一列在底层表中更改了位置,指定视图或查询中的列顺序就可以弥补这一点。

这些其他答案都有好的观点,但至少对于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无法学习新列。根据您想要的内容,这可能是好事或坏事,但无论如何,依赖它可能不是好主意。因此,避免使用它似乎是个好主意。

对我而言,这种奇怪的行为是避免在视图中使用select *的最有说服力的原因。

评论教会了我MySQL与Oracle的行为相似(它将了解表的更改)。对我来说,这种不一致性更加不适合在视图中使用select *。

在任何制作方面使用 * 都是不好的。对于一次性查询来说很棒,但在生产代码中,您应该尽可能明确。

针对视图而言,如果底层表格添加或移除了列,视图将会出现错误或损坏,直到重新编译。

如果视图中没有使用视图之外的列,则使用SELECT *不会造成太多性能开销-优化器会优化它们; SELECT * FROM TheView可能会浪费带宽,就像任何时候通过网络连接拉取更多列一样。

事实上,我发现将我的数据仓库中的几个大表几乎所有列链接起来的视图并没有带来任何性能问题,即使只有很少的那些列被从视图外部请求。优化程序处理得很好,能够将外部的过滤条件很好地推到视图中。

然而,出于上述所有原因,我很少使用SELECT *

我有一些业务流程,在其中一些公共表达式(CTE)相互叠加,从而有效地从派生列中建立派生列,从而建立派生列(这些将有朝一日被重构,因为业务简化这些计算),在这种情况下,我需要所有列每次都传递,我使用SELECT * - 但SELECT *不在基层使用,只在第一个CTE和最后一个CTE之间使用。

SQL服务器上的情况实际上比@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 * 也是无效的。

从前,我在另一个数据库(同一台服务器上)中对一个表创建了一个视图。

Select * From dbname..tablename

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


完全不正确:没有行。

这是在SQL Server 2000上的。

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

SQL查询基本上是由程序员为在某个上下文中使用而设计的功能单元。为了长期稳定性和可维护性(可能由其他人维护),功能单元中的所有内容都应该有一个目的,并且应该合理明确(或有文档记录)它们存在的原因,特别是数据元素。

如果两年后我需要或想要更改您的查询,我希望在我有信心去更改之前能够很好地理解它。这意味着我需要了解为什么所有的列都被调用。(如果您尝试在多个上下文中重复使用查询,这更加明显。由于类似的原因,这通常是有问题的。)如果在输出中看到我无法与某些目的联系起来的列,我会确信我不了解它的作用、原因以及更改它的后果。

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

我认为这取决于你所使用的语言。如果语言或数据库驱动程序返回结果的字典(Python,Perl等)或关联数组(PHP),我喜欢使用select *。如果你通过名称而不是通过数组索引引用列名,这样做会使你的代码更容易理解。

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

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

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

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

如果你太天真,使用调用其他视图的视图,使用select * 会使它们的性能变得更糟(这是一种本身就对性能不利的技术,调用多个不需要的列会使情况更糟)。





相关问题
热门标签