NULL值如何影响数据库搜索的性能?

Jak*_*sen 30 sql database oracle database-performance query-performance

在我们的产品中,我们有一个通用的搜索引擎,并试图优化搜索性能.查询中使用的许多表都允许空值.我们应该重新设计我们的表以禁止空值进行优化吗?

我们的产品上都运行OracleMS SQL Server.

Qua*_*noi 28

Oracle,NULL值没有索引,即此查询:

SELECT  *
FROM    table
WHERE   column IS NULL
Run Code Online (Sandbox Code Playgroud)

将始终使用全表扫描,因为索引不会涵盖您需要的值.

不仅如此,这个查询:

SELECT  column
FROM    table
ORDER BY
        column
Run Code Online (Sandbox Code Playgroud)

也将使用全表扫描并出于同样的原因进行排序.

如果您的值本身不允许NULL,则将列标记为NOT NULL.

  • SQL Server会对NULL进行索引 (6认同)
  • 您可以使用基于函数的索引来解决此限制,其中包含一个文字值,例如CREATE INDEX MY_INDEX ON MY_TABLE(MY_NULLABLE_COLUMN,0) (3认同)

Rob*_*ijk 13

另外一个答案是对David Aldridge对Quassnoi接受的答案的评论给予一些额外的关注.

该声明:

这个查询:

SELECT*FROM表WHERE列为IS NULL

将始终使用全表扫描

不是真的.以下是使用带有文字值的索引的计数器示例:

SQL> create table mytable (mycolumn)
  2  as
  3   select nullif(level,10000)
  4     from dual
  5  connect by level <= 10000
  6  /

Table created.

SQL> create index i1 on mytable(mycolumn,1)
  2  /

Index created.

SQL> exec dbms_stats.gather_table_stats(user,'mytable',cascade=>true)

PL/SQL procedure successfully completed.

SQL> set serveroutput off
SQL> select /*+ gather_plan_statistics */ *
  2    from mytable
  3   where mycolumn is null
  4  /

  MYCOLUMN
----------


1 row selected.

SQL> select * from table(dbms_xplan.display_cursor(null,null,'allstats last'))
  2  /

PLAN_TABLE_OUTPUT
-----------------------------------------------------------------------------------------
SQL_ID  daxdqjwaww1gr, child number 0
-------------------------------------
select /*+ gather_plan_statistics */ *   from mytable  where mycolumn
is null

Plan hash value: 1816312439

-----------------------------------------------------------------------------------
| Id  | Operation        | Name | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
-----------------------------------------------------------------------------------
|   0 | SELECT STATEMENT |      |      1 |        |      1 |00:00:00.01 |       2 |
|*  1 |  INDEX RANGE SCAN| I1   |      1 |      1 |      1 |00:00:00.01 |       2 |
-----------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - access("MYCOLUMN" IS NULL)


19 rows selected.
Run Code Online (Sandbox Code Playgroud)

如您所见,正在使用索引.

问候,Rob.


Jer*_*yth 8

简短回答:是的,有条件的!

null值和性能的主要问题与正向查找有关.

如果将一行插入到具有空值的表中,则将其放在它所属的自然页面中.查找该记录的任何查询都会在适当的位置找到它.容易到目前为止....

...但是让我们说页面填满了,现在那行被包含在其他行中.还顺利......

...直到更新行,并且null值现在包含某些内容.行的大小已超出可用空间,因此数据库引擎必须对其进行一些操作.

服务器要做的最快的事情就是将该行该页移到另一页,并用前向指针替换该行的条目.不幸的是,这需要在执行查询时进行额外查找:一个用于查找行的自然位置,另一个用于查找其当前位置.

因此,对您的问题的简短回答是肯定的,使这些字段不可为空将有助于搜索性能.如果经常发生您搜索的记录中的空字段更新为非null,则尤其如此.

当然,还有其他处罚(特别是I/O,虽然在很小程度上索引深度)与较大的数据集相关联,然后你有应用程序问题禁止在概念上需要它们的字段中的空值,但是,嘿,这是另一个问题:)

  • 将这些列设置为NOT NULL将无法解决"行迁移"问题:如果在插入时未知信息,则会输入另一个默认值(如"."),并且在实际数据时仍会迁移行将替换默认值.在Oracle中,您可以适当地设置PCTFREE以防止行迁移. (3认同)

HLG*_*GEM 6

是否使用 Null 的问题是因为它们会影响性能,这是数据库设计的平衡行为之一。您必须平衡业务需求和性能。

如果需要,应该使用空值。例如,表中可能有开始日期和结束日期。您通常不知道创建记录时的结束日期。因此,您必须允许空值,无论它们是否影响性能,因为数据根本不存在。但是,如果根据业务规则,数据必须在创建记录时存在,那么您不应该允许空值空值。这将提高性能,使编码更简单并确保保留数据完整性。

如果您希望将现有数据更改为不再允许空值,那么您必须考虑该更改的影响。首先,您知道需要将什么值放入当前为空的记录中吗?其次,您是否有很多正在使用isnullcoalesce需要更新的代码(这些东西会降低性能,因此如果您不再需要检查它们,则应该更改代码)?您需要默认值吗?你真的可以分配一个吗?否则,如果不考虑该字段不能再为空,某些插入或更新代码将会中断。有时人们会输入错误的信息以允许他们摆脱空值。因此,现在价格字段需要包含小数值和“未知”之类的内容,因此不能正确地成为小数数据类型,然后您必须使用各种长度才能进行计算。这通常会产生与创建的 null 一样糟糕或更严重的性能问题。另外,您需要检查所有代码,并且无论您在何处使用对空或非空文件的引用,您都需要根据某人可能输入的错误值进行重写以排除或包含,因为数据不允许为空。

我从客户端数据导入大量数据,每次我们获取一个文件,其中某些字段不允许为空时,我们都会得到垃圾数据,在导入到系统之前需要清理这些数据。电子邮件就是其中之一。通常,输入的数据不知道该值,并且通常是某种类型的字符串数据,因此用户可以在此处键入任何内容。我们导入电子邮件并查找“我不知道”的内容。很难尝试实际发送电子邮件给“我不知道”。如果系统需要有效的电子邮件地址并检查 @ 符号是否存在,我们会得到“I@dont.know” 这样的垃圾数据对数据用户有何用处?

空值的一些性能问题是由于编写不可优化查询而导致的。有时,只需重新排列 where 子句而不是消除必要的 null 即可提高性能。


Vin*_*rat 5

如果您的列不包含NULL,则最好声明此列NOT NULL,优化器可能能够采用更高效的路径.

但是,如果列中有NULL,则没有太多选择(非空默认值可能会产生比解决的问题更多的问题).

正如Quassnoi所提到的,在Oracle中没有对NULL进行索引,或者更确切地说,如果所有索引列都为NULL,则不会对行编制索引,这意味着:

  • NULL可能会加速您的研究,因为索引将有更少的行
  • 如果向索引添加另一个NOT NULL列,甚至是常量,仍然可以索引NULL行.

以下脚本演示了一种索引NULL值的方法:

CREATE TABLE TEST AS 
SELECT CASE
          WHEN MOD(ROWNUM, 100) != 0 THEN
           object_id
          ELSE
           NULL
       END object_id
  FROM all_objects;

CREATE INDEX idx_null ON test(object_id, 1);

SET AUTOTRACE ON EXPLAIN

SELECT COUNT(*) FROM TEST WHERE object_id IS NULL;
Run Code Online (Sandbox Code Playgroud)


小智 5

我会说需要进行测试,但是很高兴了解其他人的经验。根据我在ms sql服务器上的经验,null会而且确实会导致大量的性能问题(差异)。现在在一个非常简单的测试中,我看到在表create语句的相关字段上未设置null的情况下,查询在45秒内返回,在超过25分钟的时间内未返回查询(我放弃了等待,只是在估算的查询计划)。

测试数据为100万行x 20列,它们由i5-3320普通HD和8GB RAM(SQL Server使用2GB)/ Windows 8.1上的SQL Server 2012 Enterprise Edition上的62个随机小写字母字符组成。使用随机数据/不规则数据来使测试成为现实的“更糟糕”的情况很重要。在这两种情况下,都重新创建了表,并用随机数据重新加载了表,这些数据对已经具有适当可用空间量的数据库文件花费了大约30秒。

select count(field0) from myTable where field0 
                     not in (select field1 from myTable) 1000000

CREATE TABLE [dbo].[myTable]([Field0] [nvarchar](64) , ...

 vs

CREATE TABLE [dbo].[myTable]([Field0] [nvarchar](64) not null,
Run Code Online (Sandbox Code Playgroud)

出于性能方面的原因,都设置了表选项data_compression =页面,并且其他所有内容均为默认设置。没有索引。

alter table myTable rebuild partition = all with (data_compression = page);
Run Code Online (Sandbox Code Playgroud)

对于没有专门针对内存优化的表,我没有专门使用空值,但是sql server显然会做最快的事情,在这种特定情况下,这似乎在很大程度上支持不使用空值并且不使用空值。表创建。

此表上任何后续的相同形式的查询都将在两秒钟内返回,因此我将假设使用标准默认统计信息,并且可能已将(1.3GB)表装入内存。即

select count(field19) from myTable where field19 
                       not in (select field18 from myTable) 1000000
Run Code Online (Sandbox Code Playgroud)

除了没有空值而且不必处理空值情况之外,查询也变得更简单,更短,更不容易出错并且通常更快。如果有可能,最好至少在ms sql服务器上避免空值,除非明确要求它们并且不能从解决方案中合理地排除它们。

从一个新表开始,然后将其调整为10m行/ 13GB的大小,相同的查询将花费12分钟,考虑到硬件和未使用索引,这是非常可观的。对于信息查询,它完全与IO绑定,并且IO徘徊在20MB / s至60MB / s之间。重复执行相同的查询需要9分钟。