Oracle <> , != , ^= 연산자
저는 그 운영자들의 차이점, 주로 그들의 성능 차이를 알고 싶습니다.
SQL에서 <>와 !=의 차이를 살펴보았는데 성능 관련 정보가 없습니다.
그리고 dba-oracle.com 에서 이를 발견했습니다. 10.2 이상에서는 성능이 상당히 달라질 수 있습니다.
그럴까요?왜 그럴까요?!=
항상 더 나은 성능을 발휘합니다.<>
?
" " "에서 되었습니다."로 됩니다.<>
!=
쿼리가 반환되는 시간에 큰 영향을 미칩니다.저는 그들이 같은지 아닌지가 아니라 왜 이런 일이 일어나는지 묻기 위해 여기에 왔습니다.저는 의미론적으로 그들이 그렇다는 것을 알지만, 실제로는 다릅니다.
오라클에서 동일하지 않은 연산자에 대해 다른 구문의 성능을 테스트했습니다.저는 시험에 대한 외부의 영향을 모두 없애기 위해 노력해 왔습니다.
11.2.0.3 데이터베이스를 사용하고 있습니다.다른 세션이 연결되지 않았으며 테스트를 시작하기 전에 데이터베이스가 다시 시작되었습니다.
단일 테이블과 기본 키에 대한 시퀀스로 스키마가 생성되었습니다.
CREATE TABLE loadtest.load_test (
id NUMBER NOT NULL,
a VARCHAR2(1) NOT NULL,
n NUMBER(2) NOT NULL,
t TIMESTAMP NOT NULL
);
CREATE SEQUENCE loadtest.load_test_seq
START WITH 0
MINVALUE 0;
테이블이 쿼리 성능을 향상시키기 위해 인덱싱되었습니다.
ALTER TABLE loadtest.load_test
ADD CONSTRAINT pk_load_test
PRIMARY KEY (id)
USING INDEX;
CREATE INDEX loadtest.load_test_i1
ON loadtest.load_test (a, n);
SYSDATE
타임스탬프 및 랜덤 데이터의 경우 DBMS_RANDOM(A-Z)을 사용하고 나머지 두 필드의 경우 (0-99)를 사용합니다.
SELECT COUNT(*) FROM load_test;
COUNT(*)
----------
10000000
1 row selected.
스키마는 좋은 통계를 제공하기 위해 분석되었습니다.
EXEC DBMS_STATS.GATHER_SCHEMA_STATS(ownname => 'LOADTEST', estimate_percent => NULL, cascade => TRUE);
세 가지 간단한 쿼리는 다음과 같습니다.
SELECT a, COUNT(*) FROM load_test WHERE n <> 5 GROUP BY a ORDER BY a;
SELECT a, COUNT(*) FROM load_test WHERE n != 5 GROUP BY a ORDER BY a;
SELECT a, COUNT(*) FROM load_test WHERE n ^= 5 GROUP BY a ORDER BY a;
이것들은 동일하지 않은 연산자에 대한 구문을 제외하면 완전히 동일합니다(<> 및 != 뿐만 아니라 ^= ).
먼저 각 쿼리는 캐싱의 효과를 제거하기 위해 결과를 수집하지 않고 실행됩니다.
쿼리의 실제 실행 시간과 실행 계획을 모두 수집하기 위해 다음 타이밍과 자동 추적이 켜졌습니다.
SET TIMING ON
SET AUTOTRACE TRACE
이제 쿼리가 차례로 실행됩니다.먼저 <>입니다.
> SELECT a, COUNT(*) FROM load_test WHERE n <> 5 GROUP BY a ORDER BY a;
26 rows selected.
Elapsed: 00:00:02.12
Execution Plan
----------------------------------------------------------
Plan hash value: 2978325580
--------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 26 | 130 | 6626 (9)| 00:01:20 |
| 1 | SORT GROUP BY | | 26 | 130 | 6626 (9)| 00:01:20 |
|* 2 | INDEX FAST FULL SCAN| LOAD_TEST_I1 | 9898K| 47M| 6132 (2)| 00:01:14 |
--------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("N"<>5)
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
22376 consistent gets
22353 physical reads
0 redo size
751 bytes sent via SQL*Net to client
459 bytes received via SQL*Net from client
3 SQL*Net roundtrips to/from client
1 sorts (memory)
0 sorts (disk)
26 rows processed
다음!=
> SELECT a, COUNT(*) FROM load_test WHERE n != 5 GROUP BY a ORDER BY a;
26 rows selected.
Elapsed: 00:00:02.13
Execution Plan
----------------------------------------------------------
Plan hash value: 2978325580
--------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 26 | 130 | 6626 (9)| 00:01:20 |
| 1 | SORT GROUP BY | | 26 | 130 | 6626 (9)| 00:01:20 |
|* 2 | INDEX FAST FULL SCAN| LOAD_TEST_I1 | 9898K| 47M| 6132 (2)| 00:01:14 |
--------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("N"<>5)
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
22376 consistent gets
22353 physical reads
0 redo size
751 bytes sent via SQL*Net to client
459 bytes received via SQL*Net from client
3 SQL*Net roundtrips to/from client
1 sorts (memory)
0 sorts (disk)
26 rows processed
마지막으로 ^=
> SELECT a, COUNT(*) FROM load_test WHERE n ^= 5 GROUP BY a ORDER BY a;
26 rows selected.
Elapsed: 00:00:02.10
Execution Plan
----------------------------------------------------------
Plan hash value: 2978325580
--------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 26 | 130 | 6626 (9)| 00:01:20 |
| 1 | SORT GROUP BY | | 26 | 130 | 6626 (9)| 00:01:20 |
|* 2 | INDEX FAST FULL SCAN| LOAD_TEST_I1 | 9898K| 47M| 6132 (2)| 00:01:14 |
--------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("N"<>5)
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
22376 consistent gets
22353 physical reads
0 redo size
751 bytes sent via SQL*Net to client
459 bytes received via SQL*Net from client
3 SQL*Net roundtrips to/from client
1 sorts (memory)
0 sorts (disk)
26 rows processed
세 가지 쿼리에 대한 실행 계획은 동일하며 타이밍은 2.12, 2.13 및 2.10초입니다.
쿼리에서 사용되는 구문이 무엇이든 실행 계획은 항상 <>를 표시합니다.
검정은 각 측정 시스템 구문에 대해 10회 반복되었습니다.시간은 다음과 같습니다.
<>
2.09
2.13
2.12
2.10
2.07
2.09
2.10
2.13
2.13
2.10
!=
2.09
2.10
2.12
2.10
2.15
2.10
2.12
2.10
2.10
2.12
^=
2.09
2.16
2.10
2.09
2.07
2.16
2.12
2.12
2.09
2.07
몇 백 분의 일 초의 차이는 있지만 중요하지 않습니다.세 가지 구문 선택 항목에 대한 결과는 동일합니다.
구문 선택은 구문 분석 및 최적화되고 동시에 동일한 노력으로 반환됩니다.따라서 이 테스트에서 다른 테스트보다 사용함으로써 얻을 수 있는 이점은 없습니다.
당신은 "아 BC"라고 말합니다. "내 테스트에서 나는 진정한 차이가 있다고 믿으며 그렇지 않으면 그것을 증명할 수 없습니다."
네, 저는 그것이 완벽하게 사실이라고 말합니다.테스트, 쿼리, 데이터 또는 결과를 표시하지 않았습니다.그래서 저는 당신의 결과에 대해 할 말이 없습니다.다른 모든 것이 동일하기 때문에 어떤 구문을 사용하는지는 중요하지 않다는 것을 보여주었습니다.
"그럼 왜 시험에서 한 쪽이 더 낫다는 것을 알 수 있을까요?"
좋은 질문입니다.몇 가지 가능성이 있습니다.
- 테스트에 결함이 있습니다(외부 요인 - 기타 워크로드, 캐싱 등). 정보에 입각한 결정을 내릴 수 있는 정보를 제공하지 않았습니다.
- 당신의 질문은 특별한 경우입니다(질문을 보여주시면 토론할 수 있습니다).
- 데이터는 특별한 경우입니다(아마도 - 어떻게 - 우리는 그것도 보지 못합니다).
- 다른 외부의 영향이 있습니다.
문서화되고 반복 가능한 프로세스를 통해 한 구문을 다른 구문보다 사용하는 것이 아무런 이점이 없다는 것을 보여주었습니다.저는 <>와 ^=이 동의어라고 믿습니다.
만약 당신이 다른 것이 괜찮다고 믿는다면, 그래서.
내가 직접 시도할 수 있는 문서화된 예를 보여줍니다.
그리고.
당신이 가장 좋다고 생각하는 구문을 사용하세요.제 말이 맞으며 차이가 없다면 문제가 되지 않을 것입니다.만약 당신이 맞다면, 당신은 아주 적은 일에도 개선이 될 것입니다.
"하지만 벌레슨은 그것이 더 낫다고 말했고 나는 당신, 파울트, 루이스, 키테 및 다른 모든 부랑자들보다 그를 더 신뢰합니다."
그가 그게 더 낫다고 말했나요?난 그렇게 생각 안 해.그는 확실한 예, 테스트 또는 결과를 제공하지 않았지만!=가 더 낫다는 누군가의 말과 연결되어 그들의 게시물 중 일부를 인용했습니다.
쇼는 말하지 마.
당신은 Burleson 사이트의 기사를 참조합니다.오라클-L 아카이브에 대한 링크를 추적했습니까?그리고 벌슨이 인용한 이메일에 답장하는 다른 이메일들을 읽으셨습니까?
저는 당신이 그렇게 생각하지 않는다고 생각합니다, 그렇지 않았다면 당신은 이 질문을 하지 않았을 것입니다.사이에는 근본적인 차이가 없기 때문입니다.!=
그리고.<>
원래의 관찰은 거의 확실히 데이터베이스의 환경 조건에 의해 발생한 우연이었습니다.조나단 루이스와 스테판 파울트의 답변을 읽고 더 많은 것을 이해하세요.
존중은 프로그래머가 가질 필요가 있는 것이 아니라, 그것은 인간이 가져야 할 기본적인 태도입니다."
지점을 넘어갔다.우리가 길에서 낯선 사람을 만났을 때, 물론 우리는 예의를 갖추고 그들을 존중해야 합니다.
하지만 그 낯선 사람이 "성능 향상"을 위해 데이터베이스 애플리케이션을 특정한 방식으로 설계하기를 원한다면 설득력 있는 설명과 이를 뒷받침할 방탄 테스트 사례가 있어야 합니다.어떤 무작위적인 개인의 고립된 일화로는 충분하지 않습니다.
그 기사의 작가는, 비록 책 작가이자 몇몇 유용한 정보의 제공자이지만, 정확성에 대한 좋은 평판을 가지고 있지 않습니다.이 경우 이 기사는 유명한 오라클 메일링 목록에 있는 한 사람의 관찰에 대한 언급에 불과했습니다.답변을 전체적으로 읽어보면 게시물에 대한 가정은 문제가 있지만 정확성에 대한 가정은 없습니다.다음은 몇 가지 발췌문입니다.
설명 계획(또는 자동 추적)을 통해 쿼리를 실행하고 다음과 같은 내용을 확인해 보십시오.이에 따르면 "!="는 "<>"와 동일한 것으로 간주됩니다.조너선 루이스
조나단 루이스는 오라클 커뮤니티에서 존경받는 전문가입니다.
그냥 호기심 때문에...쿼리 옵티마이저는 두 쿼리에 대해 다른 실행 계획을 생성합니까?안녕하세요, 크리스
.
동작 중인 바인딩 변수 훔쳐보기일 수 있습니까?<> 대신에 !=를 쓰는 것의 확실한 효과는 재실행을 강제하는 것입니다.처음 실행할 때 :id의 값이 달랐다면 clooks_doc_id에 히스토그램이 있으면 원인일 수 있습니다.그리고 clooks_doc_id가 기본 키라고 하면, 특히 EXISTS 절의 쿼리가 외부 쿼리와 상관이 없고 :id가 무엇이든 동일한 결과를 반환할 때 카운트의 목적이 무엇인지 묻겠습니다.폴링 쿼리처럼 보입니다.그것을 둘러싼 코드는 흥미로울 것입니다.
스테판 파울트
.
저는 어휘적 구문 분석이 !=를 <>로 변환하거나 <>를 !=로 변환한다고 확신하지만, 그것이 SQL 텍스트가 저장된 개요와 일치하는지 여부에 영향을 미치는지는 잘 모르겠습니다.
.
설명 계획이 똑같아 보이나요?같은 비용?
다음 답변은 원래 포스터에서 온 것입니다.
조나단, 답변 감사합니다.우리는 두 가지 버전의 성명서에 대해 설명 계획을 세웠고 그것들은 동일했는데, 이것이 이것에 대해 매우 곤혹스러운 것입니다.설명서에 따르면, 동일하지 않은 두 가지 형식은 (^=과 입력할 수 없는 다른 형식과 함께) 동일한데, 성능에 차이가 있는 이유가 무엇인지 이해할 수 없습니다.
스콧 가나안
.
모든 것을 포함하는 작은 테스트는 아니지만 적어도 10.1.0.2에서는 다음 중 하나에 대해 "<>"로 구분됩니다(각 계획의 필터 라인에 주의).
.
저장된 아웃라인이 있습니까? 저장된 아웃라인은 정확하게 일치하므로 예를 들어, "!="가 있는 SQL에 대해 저장된 아웃라인이 있고 "<>"가 있는 SQL에 대해 없는 경우(또는 그 반대의 경우) 저장된 아웃라인이 힌트를 사용하고 있을 수 있습니다. (생각해보니, 저장된 아웃라인을 실행할 때 설명 계획이 힌트를 표시했어야 합니까?)
.
단순한 설명 및 자동 추적을 넘어 10046 레벨 12 전체 추적을 실행하여 느린 버전이 어디에 시간을 보내고 있는지 확인해 보셨습니까?이를 통해 주제에 대한 정보를 얻을 수 있으며, 설명 계획이 EXPLE= 옵션으로 생성된 것이 아니라 10046 추적 파일과 v$sql 계획에서 정확히 동일한지 확인해야 합니다.자동 추적의 "기능"과 설명이 정확한 설명 계획을 제공하지 못하게 할 수 있습니다.
안녕하세요, 브랜든
.
그 현상은 완전히 재현 가능합니까?
계획의 filter_predicates 및 access_predicates를 확인하셨습니까, 아니면 구조만 확인하셨습니까?차이는 없을 것으로 예상되지만, 운이 나쁘면 술어 순서가 바뀌면 CPU 사용량에 큰 변화가 생길 수 있습니다.
차이가 없으면 행 소스 통계를 사용하도록 설정하고(vmdk 세션 집합 "_rowsource_vmdk_statistics"=true) 쿼리를 실행한 다음 V$sql_plan에서 실행 계획을 가져와 v$sql_plan_statistics에 가입하여 last_vmdk_buffer_gets, last_disk_gets,last_valid_time은 시간이 어디로 갔는지에 대한 단서를 제공합니다.
10gR2에 있는 경우 "변경 세션" 대신 사용할 수 있는 /*+ gather_plan_statistics */ 힌트가 있습니다.
조나단 루이스에 대하여
이 시점에서 스레드는 사라지고 원본 포스터에서 더 이상 게시물을 볼 수 없습니다. OP가 그들이 만든 가정이 사실이 아니라는 것을 발견했거나 추가 조사를 하지 않았다고 믿게 됩니다.
추적을 또한 설 계 명 또 자 수 비 항 면 다 는 표 다 수 있 알 다 니 습 것 을 된 시 같 이 과 음 교 가 상 획 하 행 추 는 적 을 동 ▁as ▁i ▁that ▁always ▁or , ▁that ▁displayed ▁is ▁out ▁if ▁aut ▁point ▁the ▁see ot ▁compar ▁will ▁an ▁you ▁plan 다 ▁you 있 또 ▁explain 니 <>
.
여기 테스트 코드가 있습니다.원하는 경우 루프 반복 횟수를 늘립니다.서버 활동의 다른 활동에 따라 한 쪽 또는 다른 쪽에서 더 많은 수를 얻을 수 있지만, 한 운영자가 다른 운영자보다 일관되게 더 나은 결과를 얻을 수는 없습니다.
DROP TABLE t1;
DROP TABLE t2;
CREATE TABLE t1 AS (SELECT level c1 FROM dual CONNECT BY level <=144000);
CREATE TABLE t2 AS (SELECT level c1 FROM dual CONNECT BY level <=144000);
SET SERVEROUTPUT ON FORMAT WRAPPED
DECLARE
vStart Date;
vTotalA Number(10) := 0;
vTotalB Number(10) := 0;
vResult Number(10);
BEGIN
For vLoop In 1..10 Loop
vStart := sysdate;
For vLoop2 In 1..2000 Loop
SELECT count(*) INTO vResult FROM t1 WHERE t1.c1 = 777 AND EXISTS
(SELECT 1 FROM t2 WHERE t2.c1 <> 0);
End Loop;
vTotalA := vTotalA + ((sysdate - vStart)*24*60*60);
vStart := sysdate;
For vLoop2 In 1..2000 Loop
SELECT count(*) INTO vResult FROM t1 WHERE t1.c1 = 777 AND EXISTS
(SELECT 1 FROM t2 WHERE t2.c1 != 0);
End Loop;
vTotalB := vTotalB + ((sysdate - vStart)*24*60*60);
DBMS_Output.Put_Line('Total <>: ' || RPAD(vTotalA,8) || '!=: ' || vTotalB);
vTotalA := 0;
vTotalB := 0;
End Loop;
END;
프로그래머는 다음을 사용합니다.!=
는 DBA를 합니다.<>
실행 계획이 다를 경우 각 표기법에 대한 쿼리 캐시 또는 통계에 차이가 있을 수 있습니다.하지만 저는 그렇게 생각하지 않습니다.
편집:
위에서 말한 것.복잡한 데이터베이스에는 몇 가지 이상한 부작용이 있을 수 있습니다.Oracle을 잘 모르지만 SQL Server 2008 R2와 같은 Query Compilation Cache가 있는 것 같습니다.쿼리가 새 쿼리로 컴파일된 경우 데이터베이스 최적화 도구는 현재 통계에 따라 새 실행 계획을 계산합니다.통계가 변경되면 다른 결과를 초래할 수 있습니다. 더 나쁜 계획일 수도 있습니다.
언급URL : https://stackoverflow.com/questions/12003127/oracle-operators
'bestsource' 카테고리의 다른 글
한 데이터베이스/테이블에서 다른 데이터베이스/테이블로 데이터를 복사하는 방법 (0) | 2023.07.23 |
---|---|
SpringBoot를 통해 SpringBoot 응용 프로그램을 디버깅하는 방법시험 (0) | 2023.07.23 |
PHP 출력을 변수로 캡처하려면 어떻게 해야 합니까? (0) | 2023.07.23 |
연결 시 Java 애플리케이션이 Oracle에 대해 식별되도록 하려면 어떻게 해야 합니까? (0) | 2023.07.23 |
플라이웨이 Java 기반 마이그레이션에는 스프링 콩이 주입되지 않습니다. (0) | 2023.07.23 |