代码之家  ›  专栏  ›  技术社区  ›  Neel Basu

选择*与选择列

  •  109
  • Neel Basu  · 技术社区  · 16 年前

    如果我只需要2/3列并查询 SELECT * 与在select查询中提供这些列不同,是否存在更多/更少I/O或内存方面的性能下降?

    如果我不需要选择*的话,可能会出现网络开销。

    但是在select操作中,数据库引擎总是从磁盘中提取原子元组,还是只提取select操作中请求的那些列?

    如果它总是拉一个元组,那么I/O开销是相同的。

    同时,如果从元组中取出请求的列,则可能会消耗内存。

    因此,如果是这种情况,select somecolumn的内存开销将比select的内存开销更多。*

    12 回复  |  直到 8 年前
        1
  •  22
  •   Community Mohan Dere    9 年前

    它总是拉一个元组(除了表被垂直分割成列段的情况),所以,为了回答您提出的问题,从性能的角度来看并不重要。但是,由于许多其他原因,(下面)您应该始终按名称具体选择所需的列。

    它总是拉一个元组,因为(在我熟悉的每个供应商RDBMS中,所有内容(包括表数据)的底层磁盘上存储结构都是基于定义的 输入输出页面 (例如,在SQL Server中,每页为8千字节。每次读写I/O都是按页进行的。即,每次写入或读取都是完整的数据页。

    由于这种底层的结构约束,结果是数据库中的每一行数据必须始终位于一个且只有一个页面上。它不能跨越多个数据页(除了像blobs这样的特殊情况,实际的blob数据存储在单独的页面块中,而实际的表行列则只获取一个指针…)。但这些例外只是例外,通常不适用,除非在特殊情况下(对于特殊类型的数据,或针对特殊情况的某些优化)。
    即使在这些特殊情况下,通常是数据本身的实际表行(其中包含指向blob实际数据的指针或其他内容),它也必须存储在单个IO页上…

    例外。唯一的地方 Select * 是确定的,在子查询中 Exists Not Exists 谓词子句,如:

       Select colA, colB
       From table1 t1
       Where Exists (Select * From Table2
                     Where column = t1.colA)
    

    编辑:要处理@mike sherer comment,是的,这是真的,从技术上讲,对您的特殊情况有一点定义,从美学上来说。首先,即使请求的列集是存储在某个索引中的列的子集,查询处理器也必须获取 每一个 存储在该索引中的列,而不仅仅是请求的列,原因相同-所有I/O都必须在页中完成,索引数据与表数据一样存储在IO页中。因此,如果将索引页的“tuple”定义为存储在索引中的一组列,那么该语句仍然为true。
    这句话在美学上是正确的,因为它获取的数据是基于存储在I/O页中的内容,而不是基于您所要求的内容,无论您是访问基表I/O页还是索引I/O页,这都是正确的。

    出于其他原因不使用 选择* Why is SELECT * considered harmful? :

        2
  •  102
  •   marc_s MisterSmith    16 年前

    你永远(永远)不应该使用的原因有很多 SELECT * 在生产代码中:

    • 由于您没有向数据库提供任何有关所需内容的提示,因此它首先需要检查表的定义,以便确定该表上的列。这个查找将花费一段时间-在一个查询中不多-但随着时间的推移,它会累积起来。

    • 如果只需要其中的2/3列,则会选择1/3太多的数据,这些数据需要从磁盘检索并通过网络发送。

    • 如果您开始依赖数据的某些方面,例如返回的列的顺序,那么一旦重新组织表并添加新列(或删除现有列),您可能会感到非常意外。

    • 在SQL Server中(不确定其他数据库),如果需要列的子集,则非聚集索引总是有可能覆盖该请求(包含所需的所有列)。用一个 选择* 你从一开始就放弃了这种可能性。在这种特定的情况下,数据将从索引页(如果索引页包含所有必需的列)中检索,因此磁盘I/O 与执行 SELECT *.... 查询。

    是的,最初需要更多的输入(工具如 SQL Prompt 因为SQL Server甚至可以帮助您实现这一点),但实际上有一个规则没有任何例外:不要在生产代码中使用select*。 曾经。

        3
  •  19
  •   Donnie    16 年前

    你应该 总是 只有 select 您实际需要的列。选择少而不是多,效率永远不会降低,而且您也会遇到一些意想不到的副作用-例如,逐个索引访问客户端上的结果列,然后将新列添加到表中,使这些索引变得不正确。

    [编辑]:表示访问。愚蠢的大脑还在苏醒。

        4
  •  7
  •   gxti    16 年前

    除非您存储的是大型Blob,否则性能不是问题。不使用select*的主要原因是,如果您将返回的行用作元组,那么列将按模式指定的顺序返回,如果这样做了更改,则必须修复所有代码。

    另一方面,如果您使用字典样式的访问,那么列返回的顺序无关紧要,因为您总是按名称访问它们。

        5
  •  6
  •   Richard JP Le Guen    16 年前

    这立刻让我想到一个我正在使用的表,其中包含一个类型为的列 blob ;它通常包含一个jpeg图像,一些 Mb S的大小。

    不用说我没有 SELECT 除非我 真正地 需要它。让这些数据四处飘浮——尤其是当我选择多行时——只是一个麻烦。

    但是,我承认我通常查询表中的所有列。

        6
  •  6
  •   Will Hartung    16 年前

    在SQL select期间,无论是select*还是select a、b、c,db都将引用表的元数据。为什么?因为这就是系统上表的结构和布局信息所在的位置。

    它必须阅读此信息有两个原因。第一,简单地编译语句。它需要确保至少指定一个现有的表。此外,自上次执行语句以来,数据库结构可能已更改。

    现在,显然,DB元数据被缓存在系统中,但它仍然需要进行处理。

    接下来,元数据用于生成查询计划。每次编译语句时也会发生这种情况。同样,这是针对缓存的元数据运行的,但始终是这样。

    只有在数据库使用预编译查询或缓存了前一个查询时,才会不执行此处理。这是使用绑定参数而不是文字SQL的参数。”select*from table where key=1”与“select*from table where key=?”是不同的查询。“1”在呼叫时绑定。

    DBS主要依靠页面缓存来完成这项工作。许多现代数据库都很小,可以完全存储在内存中(或者,也许我应该说,现代内存足够大,可以容纳许多数据库)。然后,后端的主要I/O成本是日志记录和页面刷新。

    但是,如果您仍在为数据库访问磁盘,那么许多系统所做的主要优化就是依赖索引中的数据,而不是表本身。

    如果你有:

    CREATE TABLE customer (
        id INTEGER NOT NULL PRIMARY KEY,
        name VARCHAR(150) NOT NULL,
        city VARCHAR(30),
        state VARCHAR(30),
        zip VARCHAR(10));
    
    CREATE INDEX k1_customer ON customer(id, name);
    

    然后,如果您执行“select id,name from customer where id=1”,那么数据库很可能会从索引中而不是从表中提取这些数据。

    为什么?它很可能会使用索引来满足查询(与表扫描相比),即使在WHERE子句中没有使用'name',该索引仍然是查询的最佳选项。

    现在数据库拥有了满足查询所需的所有数据,因此没有理由自己访问表页。使用索引可以减少磁盘流量,因为与表相比,索引中的行密度更高。

    这是一些数据库所使用的特定优化技术的手工波浪式解释。许多人有几种优化和调优技术。

    最后,select*对于您必须手工输入的动态查询很有用,我从不将其用于“真正的代码”。单个列的标识为数据库提供了更多信息,可用于优化查询,并使您更好地控制代码中的模式更改等。

        7
  •  4
  •   M.Torres    16 年前

    我认为没有确切的答案来回答你的问题,因为你在考虑性能和维护你的应用程序的便利性。 Select column 更具表现力的 select * 但是,如果您正在开发一个面向对象系统,那么您将喜欢使用 object.properties 你可以在应用程序的任何部分使用属性,如果你不使用的话,你需要写更多的方法来在特殊情况下获取属性。 选择* 并填充所有属性。你的应用需要有一个良好的性能使用 选择* 在某些情况下,您需要使用Select列来提高性能。然后,您将拥有两个世界中更好的一个,即编写和维护应用程序的功能,以及在需要性能时的性能。

        8
  •  3
  •   Community Mohan Dere    9 年前

    这里接受的答案是错误的。我遇到这个的时候 another question 作为这个问题的副本关闭(当我还在写我的答案-grr-因此下面的SQL引用了另一个问题)。

    您应该始终使用select attribute、attribute…。不选择*

    主要针对性能问题。

    从name='john'的用户中选择name;

    不是一个很有用的例子。请考虑:

    SELECT telephone FROM users WHERE name='John';
    

    如果(姓名、电话)上有索引,则无需从表中查找相关值即可解决查询-有一个 覆盖 索引。

    此外,假设该表有一个包含用户图片的blob、一个上载的cv和一个电子表格… 使用select*将把所有这些信息拉回到DBMS缓冲区(从缓存中强制输出其他有用的信息)。然后,它将全部发送给客户机,用掉网络上的空闲时间和客户机上的内存来获取冗余的数据。

    如果客户机以枚举数组的形式检索数据(如php的mysql-fetch-au数组($x,mysql-num)),也可能导致功能问题。也许当代码被写下时,“电话”是select*返回的第三列,但随后有人过来决定在“电话”前面的表格中添加一个电子邮件地址。所需字段现在移到第4列。

        9
  •  2
  •   Chris Travers    13 年前

    有理由这样做。我在postgresql上使用select*是因为在postgresql中,select*有很多事情是不能用显式的列列表来做的,特别是在存储过程中。同样,在Informix中,在继承的表树上选择*可以为您提供交错行,而显式列列表则不能,因为子表中的其他列也会返回。

    我在PostgreSQL中这样做的主要原因是它确保我得到一个特定于表的格式良好的类型。这允许我获取结果并将它们作为PostgreSQL中的表类型。这也允许在查询中使用比刚性列列表更多的选项。

    另一方面,刚性列列表提供了一个应用程序级别的检查,以确保DB模式没有以某些方式发生更改,这可能会有所帮助。(我在另一个层次上进行这种检查。)

    至于性能,我倾向于使用视图和返回类型的存储过程(然后是存储过程中的列列表)。这使我能够控制返回的类型。

    但请记住,我使用select*通常是针对抽象层,而不是针对基表。

        10
  •  2
  •   Anvesh    10 年前

    Reference taken from this article:

    无选择*: 当您使用_157;select*_157;时,您正在从数据库中选择更多的列,其中一些列可能不会被您的应用程序使用。 这将在数据库系统上产生额外的成本和负载,并在网络上传输更多的数据。

    用选择*: 如果您有特殊的需求,并且在添加或删除列时创建了动态环境,则由应用程序代码自动处理。在这种特殊情况下,您不需要更改应用程序和数据库代码,这将自动影响生产环境。在这种情况下,可以使用__select*__。

        11
  •  0
  •   Carnot Antonio Romero    10 年前

    只是在这里我没有看到的讨论中添加一个细微的差别:在I/O方面,如果您使用的是 column-oriented storage 如果只查询某些列,则可以减少很多I/O操作。当我们转向SSD时,与面向行的存储相比,好处可能要小一些,但有a)只读取包含您关心的列的块b)压缩,这通常会大大减小磁盘上数据的大小,从而减少从磁盘读取的数据量。

    如果您不熟悉面向列的存储,Postgres的一个实现来自citus数据,另一个实现来自greenplum,另一个paraccel,另一个(不严格地说)是AmazonRedshift。对于mysql来说,有infobright,现在已经接近灭绝的infinidb。其他商业产品包括HP的Vertica、Sybase IQ、Teradata…

        12
  •  -1
  •   dpfauwadel    9 年前
    select * from table1 INTERSECT  select * from table2
    

    平等的

    select distinct t1 from table1 where Exists (select t2 from table2 where table1.t1 = t2 )