문제

우리 제품에는 일반적인 검색 엔진이 있으며 검색 성능을 최적화하려고합니다. 쿼리에 사용 된 많은 테이블은 널 값을 허용합니다. 최적화를 위해 널 값을 허용하지 않도록 테이블을 재 설계해야합니까?

우리의 제품은 둘 다에서 실행됩니다 Oracle 그리고 MS SQL Server.

도움이 되었습니까?

해결책

~ 안에 Oracle, NULL 값은 색인화되지 않았으며, 즉이 쿼리는 다음과 같습니다.

SELECT  *
FROM    table
WHERE   column IS NULL

색인이 필요한 값을 커버하지 않기 때문에 항상 전체 테이블 스캔을 사용합니다.

그 이상 으로이 쿼리 :

SELECT  column
FROM    table
ORDER BY
        column

또한 전체 테이블 스캔을 사용하고 같은 이유로 정렬합니다.

값이 본질적으로 허용되지 않는 경우 NULLS, 그 다음 열을 다음과 같이 표시하십시오 NOT NULL.

다른 팁

Quassnoi의 받아 들여진 답변에 대한 David Aldridge의 의견에 대한 추가주의를 기울이는 추가 답변.

성명서 :

이 쿼리 :

열이 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.

보시다시피 인덱스가 사용되고 있습니다.

안부, Rob.

짧은 대답 : 예, 조건부!

널 값과 성능의 주요 문제는 앞으로 조회와 관련이 있습니다.

널 값이있는 테이블에 행을 삽입하면 자연스러운 페이지에 속한 페이지에 배치됩니다. 해당 레코드를 찾는 모든 쿼리는 적절한 장소에서 찾을 수 있습니다. 지금까지 쉬운 ....

...하지만 페이지가 채워 졌다고 가정 해 봅시다. 아직 잘 지내고 ...

... 행이 업데이트 될 때까지 널 값에는 이제 무언가가 포함됩니다. 행 크기는 사용 가능한 공간을 넘어서 증가하므로 DB 엔진은 그것에 대해 무언가를해야합니다.

서버가해야 할 가장 빠른 일은 행을 이동하는 것입니다. 끄다 해당 페이지는 다른 페이지로, 행의 항목을 전방 포인터로 바꿉니다. 불행히도, 쿼리가 수행 될 때 추가 조회가 필요합니다. 하나는 행의 자연 위치를 찾기위한 것과 현재 위치를 찾을 수 있습니다.

따라서 귀하의 질문에 대한 짧은 대답은 그렇습니다. 해당 필드를 무효화 할 수 없게 만드는 것은 성능을 검색하는 데 도움이됩니다. 검색하는 레코드의 NULL 필드가 NULL로 업데이트되는 경우가 종종 발생하는 경우 특히 그렇습니다.

물론, 더 큰 데이터 세트와 관련된 다른 처벌 (특히 I/O, I/O)이 있으며, 개념적으로 요구하는 필드에서 널을 허용하지 않는 응용 프로그램 문제가 있지만 또 다른 문제입니다. :)

열이 널이 포함되어 있지 않으면이 열을 선언하는 것이 가장 좋습니다. NOT NULL, Optimizer는보다 효율적인 경로를 취할 수 있습니다.

그러나 열에 널이있는 경우 선택이 많지 않습니다 (널 비 기본값은 해결하는 것보다 더 많은 문제가 발생할 수 있습니다).

Quassnoi가 언급 한 바와 같이, 널은 Oracle에서 색인화되지 않았거나 더 정확하지 않으므로 모든 색인화 된 열이 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;

테스트가 필요하다고 말하지만 다른 사람들의 경험을 아는 것이 좋습니다. MS SQL Server에 대한 경험에서 Nulls는 대규모 성능 문제 (차이)를 유발할 수 있습니다. 매우 간단한 테스트에서 지금은 테이블의 관련 필드에 NULL이 설정되지 않았을 때 45 초 안에 쿼리 리턴이 있고 설정되지 않은 곳에서 25 분이 넘었습니다 (대기를 포기하고 절정에 이르렀습니다. 예상 쿼리 계획).

테스트 데이터는 I5-3320 일반 HD 및 8GB RAM (2GB를 사용하는 SQL Server) / SQL Server 2012 Enterprise Edition의 8GB RAM (SQL Server)에서 62 개의 임의 소문자 알파 문자로 구성된 백만 행 x 20 열입니다. 임의의 데이터 / 불규칙한 데이터를 사용하여 테스트를 현실적인 "더 나쁜"사례로 만드는 것이 중요합니다. 두 경우 모두 테이블은 이미 적절한 양의 여유 공간이있는 데이터베이스 파일에서 약 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,

성능의 이유로 둘 다 테이블 옵션 data_compression = 페이지 세트가 있었고 다른 모든 것이 기본값을 받았습니다. 색인 없음.

alter table myTable rebuild partition = all with (data_compression = page);

NULLS가없는 것은 메모리에서 구체적으로 사용하지 않는 메모리 최적화 된 테이블의 요구 사항입니다. 그러나 SQL Server는이 특정 경우에 데이터에 널이없고 NULL을 사용하지 않는 것이 큰 것으로 보이는 가장 빠른 일을 분명히 수행 할 것입니다. 테이블 작성.

이 테이블에서 동일한 양식의 후속 쿼리는 2 초 안에 반환되므로 표준 기본 통계가 있다고 가정하고 메모리에 맞는 (1.3GB) 테이블이 잘 작동한다고 가정합니다. 즉

select count(field19) from myTable where field19 
                       not in (select field18 from myTable) 1000000

널이없고 널 케이스를 처리 할 필요가 없으면 쿼리가 훨씬 단순하고 짧고 오류가 적고 오류가 발생하기 쉬우 며 정상적으로 더 빠릅니다. 가능하다면, 최소한 MS SQL Server에서 널을 피하는 것이 가장 좋습니다. 적어도 명시 적으로 요구되고 솔루션에서 합리적으로 작업 할 수 없다면

새 테이블로 시작하여 최대 10m 행 / 13GB 동일한 쿼리까지 크기를 조정하는 데 12 분이 걸리면 하드웨어를 고려할 때 매우 존경 받고 사용중인 인덱스가 없습니다. 정보 쿼리의 경우 IO는 20MB/s에서 60MB/s 사이의 IO 호버링으로 완전히 IO입니다. 동일한 쿼리의 반복은 9 분이 걸렸습니다.

무효 필드는 "쿼리가 아닌"을 수행 할 때 성능에 큰 영향을 줄 수 있습니다. 모든 색인 필드가 NULL로 설정된 행이 B- 트리 색인으로 인덱싱되지 않기 때문에 Oracle은 색인이 존재하는 경우에도 NULL ENTIRES를 확인하기 위해 전체 테이블 스캔을 수행해야합니다.

예를 들어:

create table t1 as select rownum rn from all_objects;

create table t2 as select rownum rn from all_objects;

create unique index t1_idx on t1(rn);

create unique index t2_idx on t2(rn);

delete from t2 where rn = 3;

explain plan for
select *
  from t1
 where rn not in ( select rn
                     from t2 );

---------------------------------------------------------------------------
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      | 50173 |   636K|  3162   (1)| 00:00:38 |
|*  1 |  FILTER            |      |       |       |            |          |
|   2 |   TABLE ACCESS FULL| T1   | 50205 |   637K|    24   (5)| 00:00:01 |
|*  3 |   TABLE ACCESS FULL| T2   | 45404 |   576K|     2   (0)| 00:00:01 |
---------------------------------------------------------------------------

쿼리는 널 값을 확인해야하므로 T1의 각 행에 대해 T2의 전체 테이블 스캔을 수행해야합니다.

이제 필드를 무효가되지 않으면 인덱스를 사용할 수 있습니다.

alter table t1 modify rn not null;

alter table t2 modify rn not null;

explain plan for
select *
  from t1
 where rn not in ( select rn
                     from t2 );

-----------------------------------------------------------------------------
| Id  | Operation          | Name   | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |        |  2412 | 62712 |    24   (9)| 00:00:01 |
|   1 |  NESTED LOOPS ANTI |        |  2412 | 62712 |    24   (9)| 00:00:01 |
|   2 |   INDEX FULL SCAN  | T1_IDX | 50205 |   637K|    21   (0)| 00:00:01 |
|*  3 |   INDEX UNIQUE SCAN| T2_IDX | 45498 |   577K|     1   (0)| 00:00:01 |
-----------------------------------------------------------------------------

성능에 영향을 미치기 때문에 NULL을 사용할지 여부의 문제는 데이터베이스 설계의 균형을 잡는 행위 중 하나입니다. 비즈니스 요구와 성능에 대한 균형을 맞춰야합니다.

널은 필요한 경우 사용해야합니다. 예를 들어, 테이블에서 시작 날짜와 종료 날짜가있을 수 있습니다. 당신은 종종 레코드가 생성 될 때의 종료 날짜를 알지 못할 것입니다. 따라서 데이터가 단순히 입력 할 수 없으므로 성능에 영향을 미치는지 여부를 허용해야합니다. 그러나 데이터가 비즈니스 규칙에 따라 레코드가 생성 된 시점에 있어야하는 경우 허용하지 않아야합니다. 널. 이렇게하면 성능이 향상되고 코딩을 조금 간단하게 만들고 데이터 무결성이 보존되도록합니다.

더 이상 널을 허용하지 않도록 변경하려는 기존 데이터가 있으면 해당 변경의 영향을 고려해야합니다. 먼저, 현재 귀중한 레코드에 어떤 가치를 부여 해야하는지 알고 있습니까? 둘째, 사용중인 많은 코드가 있습니까? isnull 또는 coalesce 업데이트 해야하는 것 (이런 것들이 느리게 성능을 발휘하므로 더 이상 확인할 필요가 없으면 코드를 변경해야합니까)? 기본값이 필요합니까? 당신은 정말로 하나를 할당 할 수 있습니까? 그렇지 않은 경우 필드가 더 이상 널이 될 수 없다는 것을 고려하지 않으면 일부 삽입 또는 업데이트 코드가 중단됩니다. 때때로 사람들은 그들이 널을 제거 할 수 있도록 나쁜 정보를 넣을 것입니다. 따라서 이제 가격 필드에는 소수점 값과 '알 수없는'과 같은 것들이 포함되어 있어야하므로 10 진수 데이터 유형이 될 수 없으므로 계산을 위해 모든 종류의 길이로 가야합니다. 이것은 종종 성능 문제를 생성 된 널보다 나쁘거나 나쁘게 만듭니다. 게다가 모든 코드를 거쳐야하며 신고 된 사람이 NULL 또는 NULL을 사용하지 않는 곳에 대한 참조를 사용한 곳이면 데이터가 허용되지 않기 때문에 누군가가 넣을 수있는 나쁜 값을 제외하거나 포함시키기 위해 다시 작성해야합니다. 널

클라이언트 데이터에서 많은 데이터 가져 오기를 수행하고 NULLS를 허용하는 일부 필드를 얻을 때마다 시스템으로 가져 오기 전에 정리 해야하는 쓰레기 데이터를 얻습니다. 이메일은 이것들 중 하나입니다. 종종 데이터는이 값을 모르고 일반적으로 일부 유형의 문자열 데이터이므로 사용자는 여기에 무엇이든 입력 할 수 있습니다. 우리는 이메일을 수입하고 "모르는 것"을 찾습니다. 실제로 "I Connow"로 이메일을 보내려고 어렵습니다. 시스템이 유효한 이메일 주소를 재평가하고 @ 부호의 존재와 같은 것을 확인하면 'i@dont.know "이와 같은 쓰레기 데이터가 데이터 사용자에게 어떻게 유용합니까?

NULLS의 성능 문제 중 일부는 비 할 수없는 쿼리를 작성한 결과입니다. 때로는 필요한 null을 제거하기보다는 Where 절을 재정렬하면 성능이 향상 될 수 있습니다.

내 경험상 NULL은 유효한 가치이며 일반적으로 "알지 못한다"는 것을 의미합니다. 당신이 모르는 경우, 열에 대한 기본값을 구성하거나 널없는 구속 조건을 시행하려고 시도하는 것은 실제로 무의미합니다. Null은 특정 사례입니다.

Nulls의 진정한 도전은 검색을 조금 복잡하게하는 것입니다. 예를 들어 column_name in (null, 'value1', 'value2')이 어디에 있는지 말할 수 없습니다.

개인적으로 열이 많거나 특정 열에 많은 널이 포함 된 경우 데이터 모델을 다시 방문하고 싶을 수도 있습니다. 아마도 그 널 열이 어린이 테이블에 넣을 수 있습니까? 예를 들어 : 이름, 홈폰, 핸드폰, 팩스, 작업용, 비상 수 등의 전화 번호가있는 테이블 ... 그 중 하나 또는 두 개만 채울 수 있으며 더 잘 정상화 할 수 있습니다.

당신이해야 할 일은 물러서서 데이터에 액세스하는 방법을 확인하는 것입니다. 값이 있어야하는 열입니까? 이것은 어떤 경우에만 값이있는 열입니까? 이것은 많이 쿼리 될 열입니까?

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top