2008-11-04 68 views
19

为什么使用'*'构建视图不好?为什么使用'*'来建立一个视图不好?

假设你有一个复杂的连接,并且所有的字段都可以用在某个地方。

然后您只需选择所需的字段。

SELECT field1, field2 FROM aview WHERE ... 

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

我们有一个问题,如果2场在Table 1和Table相同的名称。

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

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

我缺少什么?

问候

回答

33

我不认为有太多的软件,“只是坏”,但有很多的东西,是在恶劣的方式滥用:-)

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

在另一方面,你可能真的 您以愉快地接受 所有更改基础表,在这种情况下,*将 只是你想要什么。

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

+1

请检查我的其他评论,在SQL Server上所有的变化不一定被接受。 – user12861 2008-11-04 23:03:57

+0

此外,使用视图的代码不应该使用*,所以在视图中使用*可能是相当合理的,并且使用视图的代码将使用视图来确保它不使用*。 – 2008-11-06 03:52:43

+3

最后的评论是完全错误的(至少在sql server中) - 发生相反情况 - 对基础表的更改将被忽略。这是@ user12861提到的。在sql server中,任何使用select *的视图都需要在underyling表发生变化时删除并重新创建,否则它将默默无法显示所有列。这是一种非常糟糕的失败形式,因为它不太可能在开发环境中得到提升,因为您定期从头开始重建数据库。在添加列时,忘记删除并重建表格上的所有视图也非常容易 – 2015-04-22 11:27:15

3

这是因为你并不总是需要每一个变量,同时也确保你在想什么你特别需要。

当您在网站上构建用户列表时,将所有哈希密码从数据库中取出是没有意义的,例如,select *将是非生产性的。

3

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

Select * From dbname..tablename 

然后一天,一个列被添加到作为目标的表。在重新部署之前,视图开始返回完全不正确的结果。


完全不正确:没有行。

这是在Sql Server 2000上。

我推测,这是因为syscolumns中的值,该视图夺取了,即使我用*。

11

使用'*'作任何生产是不好的。这对于一次性查询非常有用,但在生产代码中,您应该始终尽可能明确。

对于视图中特别是,如果基础表具有添加或删除的列,该视图将或者是错误的或损坏,直到它被重新编译。

15

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

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

+2

请阅读我的其他答案,底层表中的更改并不总是反映出来,至少在SQL服务器上。 – user12861 2008-11-04 23:04:34

18

虽然这里的很多意见都非常好,引用查询,例如导致错误的或不同的结果,如果基础表更改使用通配符的一个共同的问题,还没有被覆盖的另一个问题是优化。抽取表的每一列的查询往往不如仅抽取实际需要的那些列的查询效率高。当然,有些时候你需要每一列,而且这是一个主要的PIA必须引用它们,特别是在一个大型表格中,但是如果你只需要一个子集,为什么你的查询会陷入比你需要的更多的列。

2

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

3

在视图中使用SELECT *如果列不是视图外使用不会产生太大的性能开销 - 优化器将优化出来;可能会浪费带宽,就像您随时通过网络连接拉动更多列一样。

事实上,我发现连接数据库中大量表中几乎所有列的视图并没有引入任何性能问题,即使这些列中相对较少的列是从视图外部请求的。优化器可以很好地处理这些问题,并且能够将外部筛选条件向下推送到视图中。

然而,对于上面给出的所有原因,我很少使用SELECT *

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

2

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

13

这些其他答案都有好处,但在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在添加时不了解“新”列。取决于你想要什么,这可能是一件好事或坏事,但无论如何,依靠它可能并不好。所以避免它似乎是一个好主意。

对我来说,这种怪异的行为是最有说服力的理由,以避免SELECT *在视图中。

的意见都告诉我,MySQL有类似的行为,Oracle不会(它会了解变化表)。对我来说这种不一致是所有不在视图中使用select *的理由。

2

没有人似乎已经提到这个问题,但在SQL Server中,您还可以设置与schemabinding属性你的看法。

这防止修改任何基表(包括拖放),这将影响视图定义的。

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

3

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

如果我从现在的需要或愿望,改变你的查询一起两年来,我会期望几乎完全是神交它之前,我会相信,我能惹它。这意味着我需要了解为什么所有列都被调出。 (如果你试图在多个上下文中重用查询,这更显然是正确的,出于类似的原因,这通常会带来问题。)如果我在输出中看到不能涉及某个目的的列,我很确定我不明白它做了什么,为什么,以及改变它的后果是什么。

1

如果你有联接使用select *自动不仅仅意味着你需要作为连接字段中的数据被重复你正在返回更多的数据。这浪费了数据库和网络资源。

如果您天真得足以使用调用其他视图的视图,那么使用select *可能会使它们变得更差(这是对自身性能不利的技术,调用多个不需要的多列会使它变得更多更差)。

2

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 
相关问题