什么是不使用select *的原因?

我见过很多人声称你应该专门在你的select查询中命名你想要的每一列。

假设我要使用所有的列,为什么我不使用SELECT *

即使考虑问题* SQL查询 – 从视图中select*或从视图 * selectcol1,col2,… colN ,我不认为这是一个完全重复,因为我从一个稍微不同的angular度来处理这个问题。

我们的原则之一是不要在时间之前进行优化。 考虑到这一点,似乎使用SELECT *应该是首选的方法,直到它被certificate是一个资源问题,或架构几乎成立。 正如我们所知道的,直到发展完全完成才会发生。

这就是说,有一个压倒一切的问题,不使用SELECT *

不过早优化的引用的本质是去简单和直接的代码, 然后使用探查器指出热点,然后可以优化,以高效。

当你使用select *的时候,你不可能进行configuration文件,所以你不会写出清晰而直接的代码,并且违背了引用的精神。 select *是反模式。


所以select列并不是一个过早的优化。 一些事情从我的头顶….

  1. 如果在SQL语句中指定列,则如果从表中删除该列并执行查询,SQL执行引擎将会出错。
  2. 您可以更轻松地扫描正在使用该列的代码。
  3. 你应该总是写查询来带回最less量的信息。
  4. 正如其他人提到,如果你使用序列列访问,你不应该使用select *
  5. 如果你的SQL语句join表,select*会给你所有连接表中的所有列

推论是使用select *

  1. 应用程序使用的列是不透明的
  2. DBA和他们的查询分析器无法帮助您的应用程序性能不佳
  3. 发生更改时代码更易碎
  4. 您的数据库和networking正在遭受损失,因为他们正在带回太多的数据(I / O)
  5. 数据库引擎优化是最小的,因为无论(逻辑)如何都将带回所有数据。

编写正确的SQL就像编写Select *一样简单。 所以真正的懒惰的人写了适当的SQL,因为他们不想重新访问代码,并试图记住他们做了什么。 他们不想向DBA解释每一点代码。 他们不想向客户解释为什么应用程序像狗一样运行。

如果您的代码依赖于特定顺序的列,那么当表发生更改时,您的代码将会中断。 另外,当您select*时,您可能会从表中获取太多,特别是如果表中有二进制字段。

仅仅因为你现在正在使用所有的列,并不意味着别人不会添加额外的列到表中。

它还增加了计划执行caching的开销,因为它必须获取有关表的元数据以知道哪些列在*中。

一个主要原因是,如果您添加/删除表中的列,正在进行SELECT *调用的任何查询/过程现在将获得比预期更多或更less的数据列。

  1. 在一个迂回的方式,你打破了严格打字的模块化规则,只要有可能。 显式几乎普遍更好。

  2. 即使您现在需要表格中的每一列,以后也可能会添加更多,这样在您每次运行查询时都会被拉下来,并且可能会影响性能。 它伤害了performance,因为

    • 你通过电线拉动更多的数据; 和
    • 因为您可能会挫败优化器将数据从索引中提取出来的能力(查询所有属于索引的列的查询),而不是在表中查找

何时使用select *

当你明确地需要表格中的每一列时,而不是需要表格中的每一列在你写入查询的时候都存在。 例如,如果正在编写一个需要显示表的全部内容的数据库pipe理应用程序(不pipe他们碰巧是什么),您可以使用这种方法。

有几个原因:

  1. 如果数据库中的列数发生了变化,并且您的应用程序期望有一个特定的数字…
  2. 如果数据库中列的顺序发生变化,并且您的应用程序期望它们处于特定顺序,则…
  3. 内存开销。 8个不必要的INTEGER列会增加32个字节的浪费内存。 这听起来不是很多,但这是为每个查询和INTEGER是小列types之一…额外的列更可能是VARCHAR或TEXT列,这加起来更快。
  4. networking开销。 与内存开销有关:如果我发出30,000个查询,并有8个不必要的INTEGER列,我浪费了960kB的带宽。 VARCHAR和TEXT列可能会相当大。

注意:我在上面的例子中select了INTEGER,因为它们有4字节的固定大小。

如果您的应用程序使用SELECT *获取数据,并且数据库中的表结构发生更改(例如删除列),那么您的应用程序将在您引用缺less字段的每个位置都失败。 如果您在查询中包含所有列,那么您的应用程序将在希望获取数据的地方(希望)中断,从而使修复更容易。

这就是说,有很多情况下,SELECT *是可取的。 一个是我经常遇到的情况,我需要将整个表复制到另一个数据库(例如SQL Server到DB2)。 另一个是通常编写的用于显示表格的应用程序(即,不具有任何特定表格的任何知识)。

当我在SQL Server 2005的视图中使用select *时,我实际上注意到了一个奇怪的行为。

运行以下查询,你会看到我的意思。

 IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[starTest]') AND type in (N'U')) DROP TABLE [dbo].[starTest] CREATE TABLE [dbo].[starTest]( [id] [int] IDENTITY(1,1) NOT NULL, [A] [varchar](50) NULL, [B] [varchar](50) NULL, [C] [varchar](50) NULL ) ON [PRIMARY] GO insert into dbo.starTest select 'a1','b1','c1' union all select 'a2','b2','c2' union all select 'a3','b3','c3' go IF EXISTS (SELECT * FROM sys.views WHERE object_id = OBJECT_ID(N'[dbo].[vStartest]')) DROP VIEW [dbo].[vStartest] go create view dbo.vStartest as select * from dbo.starTest go go IF EXISTS (SELECT * FROM sys.views WHERE object_id = OBJECT_ID(N'[dbo].[vExplicittest]')) DROP VIEW [dbo].[vExplicittest] go create view dbo.[vExplicittest] as select a,b,c from dbo.starTest go select a,b,c from dbo.vStartest select a,b,c from dbo.vExplicitTest IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[starTest]') AND type in (N'U')) DROP TABLE [dbo].[starTest] CREATE TABLE [dbo].[starTest]( [id] [int] IDENTITY(1,1) NOT NULL, [A] [varchar](50) NULL, [B] [varchar](50) NULL, [D] [varchar](50) NULL, [C] [varchar](50) NULL ) ON [PRIMARY] GO insert into dbo.starTest select 'a1','b1','d1','c1' union all select 'a2','b2','d2','c2' union all select 'a3','b3','d3','c3' select a,b,c from dbo.vStartest select a,b,c from dbo.vExplicittest 

比较最后2个select语句的结果。 我相信你会看到的是Select *引用索引而不是名称的结果。

如果你重build视图,它将再次正常工作。

编辑

我已经添加了一个单独的问题, 在SQL Server 2005 *中有趣的行为 * “select * from table”vs“select colA,colB,etc. from table”查看更详细的行为。

您可以连接两个表并使用第二个表中的列A。 如果以后将列A添加到第一个表(具有相同的名称但可能不同的含义),则很可能会从第一个表中获取值,而不是像第二个表中那样获取值。 如果您明确指定要select的列,则不会发生这种情况。

当然,如果忘记将新列添加到每个select子句,那么指定列有时也会导致错误。 如果每次执行查询都不需要新列时,可能需要一些时间才能发现错误。

我明白你要去哪里就过早的优化,但这只是一个点。 目的是为了避免一开始就不必要的优化。 你的表是不是索引? 你会用nvarchar(4000)来存储邮政编码吗?

正如其他人所指出的那样,还有其他的积极因素来指定您打算在查询中使用的每一列(例如可维护性)。

当你指定专栏时,你也将自己绑定到一组特定的专栏,让自己变得不那么灵活,使得Feuerstein无论在哪里都能够翻身。 只是一个想法。

SELECT *并不总是邪恶的。 在我看来,至less。 我经常使用它来返回整个表的dynamic查询,加上一些计算的字段。

例如,我想从“普通”表中计算地理几何,即没有任何几何字段的表,但是包含坐标的字段。 我使用postgresql和它的空间扩展postgis。 但是这个原则适用于许多其他情况。

一个例子:

  • 地点表,坐标存储在标记为x,y,z的字段中:

    CREATE TABLE places(place_id integer,x numeric(10,3),y numeric(10,3),z numeric(10,3),description varchar);

  • 让我们用几个示例值来提供它:

    INSERT INTO places(place_id,x,y,z,description)VALUES
    (1,2,295,48.863,64,“巴黎,Place de l'Étoile”),
    (2,2.945,48.858,40,'巴黎,埃菲尔铁塔'),
    (3,0.373,43.958,90,“避孕套,圣皮埃尔大教堂”);

  • 我希望能够使用一些GIS客户端来映射这个表的内容。 正常的方法是将一个几何字段添加到表中,并根据坐标构build几何。 但我更喜欢得到一个dynamic的查询:这样,当我改变坐标(更正,更多的准确性等),映射的对象实际上dynamic地移动。 所以这里是SELECT *的查询:

    创build或更换VIEW places_points AS
    select *,
    GeomFromewkt('SRID = 4326; POINT('|| x ||''|| y ||''|| z ||')')
    从地方;

    对于GeomFromewkt()函数的使用请参阅postgis。

  • 结果如下:

    SELECT * FROM places_points;

  place_id |  x |  y |  z | 描述|  geomfromewkt                            
 ---------- + ------- + -------- + -------- + ------------- ----------------- + -------------------------------- ------------------------------------  
         1 |  2.295 |  48.863 |  64.000 | 巴黎,Place de l'Étoile|  01010000A0E61000005C8FC2F5285C02405839B4C8766E48400000000000005040  
         2 |  2.945 |  48.858 |  40.000 | 巴黎,艾菲尔铁塔|  01010000A0E61000008FC2F5285C8F0740E7FBA9F1D26D48400000000000004440
         3 |  0.373 |  43.958 |  90.000 | 避孕套,CathédraleSt-Pierre |  01010000A0E6100000AC1C5A643BDFD73FB4C876BE9FFA45400000000000805640
 (3个)

现在任何GIS程序都可以使用最右边的列来正确映射点。

  • 如果在将来有些字段被添加到表格中,不用担心,我只需要再次运行相同的VIEW定义。

我希望VIEW的定义可以保持原样,用*,但不是这样的:这是如何通过postgresql内部存储的:

SELECT places.place_id,places.x,places.y,places.z,places.description,geomfromewkt(((((('SRID = 4326; POINT(':: text || places.x)||'': :text)|| places.y)||'':: text)|| places.z)||')':: text)AS geomfromewkt FROM places;

即使您使用每一列,但通过数字索引来处理行数组,如果稍后再添加一行,也会遇到问题。

所以基本上这是一个可维护性的问题! 如果您不使用*select器,则不必担心查询。

只select你需要的列将数据集保存在内存中,从而保持你的应用程序更快。

另外,很多工具(如存储过程)也caching查询执行计划。 如果以后添加或删除列(如果您select了某个视图,那么这个列特别容易),但是如果没有得到预期结果,该工具通常会出错。

这使得你的代码更加模糊,更难以维护。 因为你正在向域中添加额外的未使用的数据,目前尚不清楚你打算的是哪一个,哪些不是。 (这也表明你可能不知道或者不在乎)

直接回答您的问题:当您的代码更改为基础表更改时,请勿使用“SELECT *”。 只有在对表格进行更改,直接影响到程序的需求时,您的代码才会中断。

您的应用程序应该利用Relational访问提供的抽象层。

我不使用SELECT *只是因为它很高兴看到并知道我正在检索的字段。

通常在视图内部使用'select *'是不好的,因为在表列更改时,您将被迫重新编译视图。 更改一个视图的基础表列,你会得到一个错误的不存在的列,直到你回去重新编译。

当你exists(select * ...)的时候没关系exists(select * ...)因为它永远不会被展开。 否则,只有在使用临时select语句来浏览表时,或者在上面定义了一个CTE时,才需要使用每一列,而不必再次input它们。

只是添加一个没有人提到的东西。 Select *返回所有的列,稍后有人可能会添加一列,你不一定希望用户能够看到,如谁最后更新数据或时间戳,或注意到,只有pipe理者应该看到不是所有的用户,等等。

此外,在添加列时,应该检查对现有代码的影响,并考虑是否需要根据列中存储的信息进行更改。 通过使用select * ,该评论通常会被忽略,因为开发人员会认为没有任何内容会被打破。 事实上,没有任何东西可以明确地显示出来,但现在查询可能开始返回错误的东西。 只是因为没有明确的中断,并不意味着不应该改变查询。

因为“select *”会在你不需要所有字段时浪费内存。但对于sql server来说,它们的性能是一样的。