SQL Server에서 숫자 범위 (간격) 검색 최적화


18

이 질문은 IP 범위 검색 최적화 와 비슷 합니까? 그러나 그 중 하나는 SQL Server 2000으로 제한됩니다.

다음과 같이 구조화되고 채워진 테이블에 천만 개의 범위가 임시로 저장되어 있다고 가정하십시오.

CREATE TABLE MyTable
(
Id        INT IDENTITY PRIMARY KEY,
RangeFrom INT NOT NULL,
RangeTo   INT NOT NULL,
CHECK (RangeTo > RangeFrom),
INDEX IX1 (RangeFrom,RangeTo),
INDEX IX2 (RangeTo,RangeFrom)
);

WITH RandomNumbers
     AS (SELECT TOP 10000000 ABS(CRYPT_GEN_RANDOM(4)%100000000) AS Num
         FROM   sys.all_objects o1,
                sys.all_objects o2,
                sys.all_objects o3,
                sys.all_objects o4)
INSERT INTO MyTable
            (RangeFrom,
             RangeTo)
SELECT Num,
       Num + 1 + CRYPT_GEN_RANDOM(1)
FROM   RandomNumbers 

value를 포함하는 모든 범위를 알아야합니다 50,000,000. 나는 다음과 같은 쿼리를 시도

SELECT *
FROM MyTable
WHERE 50000000 BETWEEN RangeFrom AND RangeTo

SQL Server는 일치하는 12 개의 것을 반환하기 위해 10,951 개의 논리적 읽기가 있었고 거의 5 백만 개의 행이 읽 혔음을 보여줍니다.

여기에 이미지 설명을 입력하십시오

이 성능을 향상시킬 수 있습니까? 테이블 또는 추가 인덱스의 재구성은 괜찮습니다.


표 설정을 올바르게 이해하고 있다면 각 범위의 "크기"에 대한 제약없이 범위를 형성하기 위해 임의의 숫자를 균일하게 선택합니다. 그리고 귀하의 프로브는 전체 범위 1..100M의 중간에 있습니다. 이 경우 균일 한 임의성으로 인해 명백한 군집이 발생하지 않습니다. 왜 하한 또는 상한의 인덱스가 도움이 될지 모르겠습니다. 설명 할 수 있습니까?
davidbak

@ davidbak이 표의 기존 색인은 범위의 절반을 스캔해야하기 때문에 최악의 경우에는별로 도움이되지 않으므로 잠재적 인 개선이 필요합니다. "과립"의 도입으로 SQL Server 2000에 대한 관련 질문에서 크게 개선되었습니다. 공간 인덱스가 contains쿼리 를 지원 하고 여기에서 도움이 될 것으로 기대하면서 데이터 읽기 양을 줄이는 데 도움이되기를 바랍니다. 이에 대응하는 오버 헤드.
Martin Smith

나는 그것을 시도 할 기능이 없지만-두 개의 인덱스-하나는 하한에, 하나는 상한에-다음에는 내부 조인이 쿼리 최적화 프로그램이 무언가를 해결할 수 있는지 궁금합니다.
davidbak

답변:


11

Columnstore는 테이블의 절반을 스캔하는 비 클러스터형 인덱스와 비교할 때 매우 많습니다. 비 클러스터형 열 저장소 인덱스는 대부분의 이점을 제공하지만 정렬 된 데이터를 클러스터 된 열 저장소 인덱스에 삽입하는 것이 훨씬 좋습니다.

DROP TABLE IF EXISTS dbo.MyTableCCI;

CREATE TABLE dbo.MyTableCCI
(
Id        INT PRIMARY KEY,
RangeFrom INT NOT NULL,
RangeTo   INT NOT NULL,
CHECK (RangeTo > RangeFrom),
INDEX CCI CLUSTERED COLUMNSTORE
);

INSERT INTO dbo.MyTableCCI
SELECT TOP (987654321) *
FROM dbo.MyTable
ORDER BY RangeFrom ASC
OPTION (MAXDOP 1);

설계 상 RangeFrom열 에서 행 그룹 제거를 수행하여 행 그룹의 절반을 제거 할 수 있습니다 . 그러나 데이터의 특성으로 인해 RangeTo열 에서 행 그룹 제거 도 수행됩니다.

Table 'MyTableCCI'. Segment reads 1, segment skipped 9.

가변 데이터가 더 많은 대형 테이블의 경우 두 열에서 최상의 행 그룹 제거를 보장하기 위해 데이터를로드하는 다른 방법이 있습니다. 특히 데이터의 경우 쿼리에 1ms가 걸립니다.


그러나 2000 년 제한없이 고려해야 할 다른 접근 방법을 확실히 찾고 있습니다. 그런 소리가 나지 않습니다.
Martin Smith

9

Paul White는 Itzik Ben Gan의 흥미로운 기사에 대한 링크가 포함 된 유사한 질문에 대한 답변을 지적했습니다 . 이 작업을 효율적으로 수행 할 수있는 "정적 관계형 간격 트리"모델에 대해 설명합니다.

요약하면이 방법은 행의 간격 값을 기반으로 계산 된 ( "포크 노드") 값을 저장하는 것과 관련이 있습니다. 다른 범위와 교차하는 범위를 검색 할 때 일치하는 행에 있어야하는 가능한 forknode 값을 미리 계산하고이를 사용하여 최대 31 개의 탐색 조작으로 결과를 찾을 수 있습니다 (아래는 0에서 최대 부호있는 32 범위의 정수를 지원합니다). 비트 int)

이를 바탕으로 다음과 같이 테이블을 재구성했습니다.

CREATE TABLE dbo.MyTable3
(
  Id        INT IDENTITY PRIMARY KEY,
  RangeFrom INT NOT NULL,
  RangeTo   INT NOT NULL,   
  node  AS RangeTo - RangeTo % POWER(2, FLOOR(LOG((RangeFrom - 1) ^ RangeTo, 2))) PERSISTED NOT NULL,
  CHECK (RangeTo > RangeFrom)
);

CREATE INDEX ix1 ON dbo.MyTable3 (node, RangeFrom) INCLUDE (RangeTo);
CREATE INDEX ix2 ON dbo.MyTable3 (node, RangeTo) INCLUDE (RangeFrom);

SET IDENTITY_INSERT MyTable3 ON

INSERT INTO MyTable3
            (Id,
             RangeFrom,
             RangeTo)
SELECT Id,
       RangeFrom,
       RangeTo
FROM   MyTable

SET IDENTITY_INSERT MyTable3 OFF 

그런 다음 다음 쿼리를 사용했습니다 (이 기사는 교차 간격을 찾고 있으므로 점을 포함하는 간격을 찾는 것이 이질적인 경우입니다)

DECLARE @value INT = 50000000;

;WITH N AS
(
SELECT 30 AS Level, 
       CASE WHEN @value > POWER(2,30) THEN POWER(2,30) END AS selected_left_node, 
       CASE WHEN @value < POWER(2,30) THEN POWER(2,30) END AS selected_right_node, 
       (SIGN(@value - POWER(2,30)) * POWER(2,29)) + POWER(2,30)  AS node
UNION ALL
SELECT N.Level-1,   
       CASE WHEN @value > node THEN node END AS selected_left_node,  
       CASE WHEN @value < node THEN node END AS selected_right_node,
       (SIGN(@value - node) * POWER(2,N.Level-2)) + node  AS node
FROM N 
WHERE N.Level > 0
)
SELECT I.id, I.RangeFrom, I.RangeTo
FROM dbo.MyTable3 AS I
  JOIN N AS L
    ON I.node = L.selected_left_node
    AND I.RangeTo >= @value
    AND L.selected_left_node IS NOT NULL
UNION ALL
SELECT I.id, I.RangeFrom, I.RangeTo
FROM dbo.MyTable3 AS I
  JOIN N AS R
    ON I.node = R.selected_right_node
    AND I.RangeFrom <= @value
    AND R.selected_right_node IS NOT NULL
UNION ALL
SELECT I.id, I.RangeFrom, I.RangeTo
FROM dbo.MyTable3 AS I
WHERE node = @value;

이것은 일반적으로 1ms모든 페이지가 캐시에 있고 IO 통계와 함께 내 컴퓨터 에서 실행됩니다 .

Table 'MyTable3'. Scan count 24, logical reads 72, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Worktable'. Scan count 4, logical reads 374, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

그리고 계획

여기에 이미지 설명을 입력하십시오

NB : 소스는 재귀 적 CTE가 아닌 멀티 스테이트먼트 TVF를 사용하여 노드를 연결하지만 내 답변을 자체적으로 포함시키기 위해 후자를 선택했습니다. 프로덕션 용도로는 TVF를 사용했을 것입니다.


9

N / CCI 접근 방식과 경쟁하는 행 모드 접근 방식을 찾을 수 있었지만 데이터에 대해 알아야합니다. 당신의 차이를 포함 열 있다고 가정 RangeFromRangeTo당신이와 함께 그것을 색인 RangeFrom:

ALTER TABLE dbo.MyTableWithDiff ADD DiffOfColumns AS RangeTo-RangeFrom;

CREATE INDEX IXDIFF ON dbo.MyTableWithDiff (DiffOfColumns,RangeFrom) INCLUDE (RangeTo);

고유 한 값을 모두 알고 있으면 범위 필터 를 켜고 모든 관련 데이터를 얻기 DiffOfColumns위해 모든 값을 검색 할 수 있습니다. 예를 들어, = 2 인 경우 허용되는 값 은 49999998, 49999999 및 50000000뿐입니다. 재귀를 사용하여 고유 한 값을 모두 얻을 수 있으며 256 개만 있기 때문에 데이터 세트에 적합합니다. 아래 쿼리는 내 컴퓨터에서 약 6ms가 걸립니다.DiffOfColumnsRangeToDiffOfColumnsRangeFromDiffOfColumns

WITH RecursiveCTE
AS
(
    -- Anchor
    SELECT TOP (1)
        DiffOfColumns
    FROM dbo.MyTableWithDiff AS T
    ORDER BY
        T.DiffOfColumns

    UNION ALL

    -- Recursive
    SELECT R.DiffOfColumns
    FROM
    (
        -- Number the rows
        SELECT 
            T.DiffOfColumns,
            rn = ROW_NUMBER() OVER (
                ORDER BY T.DiffOfColumns)
        FROM dbo.MyTableWithDiff AS T
        JOIN RecursiveCTE AS R
            ON R.DiffOfColumns < T.DiffOfColumns
    ) AS R
    WHERE
        -- Only the row that sorts lowest
        R.rn = 1
)
SELECT ca.*
FROM RecursiveCTE rcte
CROSS APPLY (
    SELECT mt.Id, mt.RangeFrom, mt.RangeTo
    FROM dbo.MyTableWithDiff mt
    WHERE mt.DiffOfColumns = rcte.DiffOfColumns
    AND mt.RangeFrom >= 50000000 - rcte.DiffOfColumns AND mt.RangeFrom <= 50000000
) ca
OPTION (MAXRECURSION 0);

모든 고유 값에 대한 인덱스 탐색과 함께 일반적인 재귀 부분을 볼 수 있습니다.

쿼리 계획 1

이 방법의 단점은에 대한 고유 값이 너무 많으면 속도가 느려지기 시작한다는 것입니다 DiffOfColumns. 동일한 테스트를 수행하지만 CRYPT_GEN_RANDOM(2)대신 사용하십시오 CRYPT_GEN_RANDOM(1).

DROP TABLE IF EXISTS dbo.MyTableBigDiff;

CREATE TABLE dbo.MyTableBigDiff
(
Id        INT IDENTITY PRIMARY KEY,
RangeFrom INT NOT NULL,
RangeTo   INT NOT NULL,
CHECK (RangeTo > RangeFrom)
);

WITH RandomNumbers
     AS (SELECT TOP 10000000 ABS(CRYPT_GEN_RANDOM(4)%100000000) AS Num
         FROM   sys.all_objects o1,
                sys.all_objects o2,
                sys.all_objects o3,
                sys.all_objects o4)
INSERT INTO dbo.MyTableBigDiff
            (RangeFrom,
             RangeTo)
SELECT Num,
       Num + 1 + CRYPT_GEN_RANDOM(2) -- note the 2
FROM   RandomNumbers;


ALTER TABLE dbo.MyTableBigDiff ADD DiffOfColumns AS RangeTo-RangeFrom;

CREATE INDEX IXDIFF ON dbo.MyTableBigDiff (DiffOfColumns,RangeFrom) INCLUDE (RangeTo);

동일한 쿼리는 이제 재귀 부분에서 65536 행을 찾고 내 컴퓨터에서 823ms의 CPU를 사용합니다. PAGELATCH_SH 대기 및 기타 나쁜 일이 진행 중입니다. diff 값을 버킷하여 고유 값의 수를 제어하고에서 버킷 팅을 조정하여 성능을 향상시킬 수 있습니다 CROSS APPLY. 이 데이터 세트의 경우 256 버킷을 시도합니다.

ALTER TABLE dbo.MyTableBigDiff ADD DiffOfColumns_bucket256 AS CAST(CEILING((RangeTo-RangeFrom) / 256.) AS INT);

CREATE INDEX [IXDIFF😎] ON dbo.MyTableBigDiff (DiffOfColumns_bucket256, RangeFrom) INCLUDE (RangeTo);

여분의 행을 얻는 것을 피하는 한 가지 방법 (이제 실제 값 대신 둥근 값과 비교하고 있음)은 다음을 필터링하는 것입니다 RangeTo.

CROSS APPLY (
    SELECT mt.Id, mt.RangeFrom, mt.RangeTo
    FROM dbo.MyTableBigDiff mt
    WHERE mt.DiffOfColumns_bucket256 = rcte.DiffOfColumns_bucket256
    AND mt.RangeFrom >= 50000000 - (256 * rcte.DiffOfColumns_bucket256)
    AND mt.RangeFrom <= 50000000
    AND mt.RangeTo >= 50000000
) ca

전체 쿼리는 이제 내 컴퓨터에서 6ms가 걸립니다.


8

범위를 나타내는 또 다른 방법은 선의 점입니다.

아래는 모든 데이터를 범위가 geometry데이터 유형으로 표시된 새 테이블로 마이그레이션합니다 .

CREATE TABLE MyTable2
(
Id INT IDENTITY PRIMARY KEY,
Range GEOMETRY NOT NULL,
RangeFrom AS Range.STPointN(1).STX,
RangeTo   AS Range.STPointN(2).STX,
CHECK (Range.STNumPoints() = 2 AND Range.STPointN(1).STY = 0 AND Range.STPointN(2).STY = 0)
);

SET IDENTITY_INSERT MyTable2 ON

INSERT INTO MyTable2
            (Id,
             Range)
SELECT ID,
       geometry::STLineFromText(CONCAT('LINESTRING(', RangeFrom, ' 0, ', RangeTo, ' 0)'), 0)
FROM   MyTable

SET IDENTITY_INSERT MyTable2 OFF 


CREATE SPATIAL INDEX index_name   
ON MyTable2 ( Range )  
USING GEOMETRY_GRID  
WITH (  
BOUNDING_BOX = ( xmin=0, ymin=0, xmax=110000000, ymax=1 ),  
GRIDS = (HIGH, HIGH, HIGH, HIGH),  
CELLS_PER_OBJECT = 16); 

50,000,000이 포함 된 범위를 찾는 해당 쿼리 는 다음과 같습니다.

SELECT Id,
       RangeFrom,
       RangeTo
FROM   MyTable2
WHERE  Range.STContains(geometry::STPointFromText ('POINT (50000000 0)', 0)) = 1 

이에 대한 읽기 10,951는 원래 쿼리에서 개선 된 점을 보여줍니다 .

Table 'MyTable2'. Scan count 0, logical reads 505, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Workfile'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'extended_index_1797581442_384000'. Scan count 4, logical reads 17, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

그러나 경과 시간면에서 원본에 비해 크게 개선되지 않았습니다 . 일반적인 실행 결과는 250ms 대 252ms입니다.

실행 계획은 아래와 같이 더 복잡합니다

여기에 이미지 설명을 입력하십시오

다시 쓰기가 안정적으로 더 잘 수행되는 유일한 경우는 콜드 캐시입니다.

따라서이 경우 실망 스럽고이 다시 작성을 권장하기는 어렵지만 부정적인 결과를 게시하는 것도 유용 할 수 있습니다.


5

우리의 새로운 로봇 군주들에게 경의를 표하면서, 새로운 R 및 Python 기능이 우리를 도울 수 있는지 결정했습니다. 적어도 내가 일하고 올바른 결과를 얻을 수있는 스크립트에 대한 대답은 아닙니다. 더 나은 지식을 가진 사람이 있다면, 저를 때리십시오. 내 요금은 합리적입니다.

이를 위해 코어가 4 개이고 RAM이 16GB 인 VM을 설정하여 ~ 200MB 데이터 세트를 처리하기에 충분하다고 생각했습니다.

보스턴에 존재하지 않는 언어로 시작합시다!

아르 자형

EXEC sp_execute_external_script 
@language = N'R', 
@script = N'
tweener = 50000000
MO = data.frame(MartinIn)
MartinOut <- subset(MO, RangeFrom <= tweener & RangeTo >= tweener, select = c("Id","RangeFrom","RangeTo"))
', 
@input_data_1_name = N'MartinIn',
@input_data_1 = N'SELECT Id, RangeFrom, RangeTo FROM dbo.MyTable',
@output_data_1_name = N'MartinOut',
@parallel = 1
WITH RESULT SETS ((ID INT, RangeFrom INT, RangeTo INT));

이것은 나쁜 시간이었다.

Table 'MyTable'. Scan count 1, logical reads 22400

 SQL Server Execution Times:
   CPU time = 3219 ms,  elapsed time = 5349 ms.

실행 계획은 꽤 중간 운전자가 우리에게 이름을 호출하는 이유를 모르겠어요하지만, 재미있다.

견과류

다음은 크레용으로 코딩!

파이썬

EXEC sp_execute_external_script 
@language = N'Python', 
@script = N'
import pandas as pd
MO = pd.DataFrame(MartinIn)
tweener = 50000000
MartinOut = MO[(MO.RangeFrom <= tweener) & (MO.RangeTo >= tweener)]
', 
@input_data_1_name = N'MartinIn',
@input_data_1 = N'SELECT Id, RangeFrom, RangeTo FROM dbo.MyTable',
@output_data_1_name = N'MartinOut',
@parallel = 1
WITH RESULT SETS ((ID INT, RangeFrom INT, RangeTo INT));

R보다 나빠질 수 없다고 생각했을 때 :

Table 'MyTable'. Scan count 1, logical reads 22400

 SQL Server Execution Times:
   CPU time = 3797 ms,  elapsed time = 10146 ms.

또 다른 파울 링 된 실행 계획 :

견과류

흠과 흠

지금까지 나는 감동하지 않았다. 이 VM을 삭제할 때까지 기다릴 수 없습니다.


1
예를 들어 매개 변수도 전달할 수 DECLARE @input INT = 50000001; EXEC dbo.sp_execute_external_script @language = N'R', @script = N'OutputDataSet <- InputDataSet[which(x >= InputDataSet$RangeFrom & x <= InputDataSet$RangeTo) , ]', @parallel = 1, @input_data_1 = N'SELECT Id, RangeFrom, RangeTo FROM dbo.MyTable;', @params = N'@x INT', @x = 50000001 WITH RESULT SETS ( ( Id INT NOT NULL, RangeFrom INT NOT NULL, RangeTo INT NOT NULL ));있지만 성능은 좋지 않습니다. SQL에서 할 수없는 것들에 R을 사용합니다. 예를 들어 무언가를 예측하고 싶다면 말하십시오.
wBob

4

계산 열을 사용하여 꽤 좋은 해결책을 찾았지만 단일 값에만 적합합니다. 즉, 마법의 가치가 있다면 충분할 것입니다.

주어진 샘플로 시작한 다음 테이블을 수정하십시오.

ALTER TABLE dbo.MyTable
    ADD curtis_jackson 
        AS CONVERT(BIT, CASE 
                            WHEN RangeTo >= 50000000
                            AND RangeFrom < 50000000
                            THEN 1 
                            ELSE 0 
                        END);

CREATE INDEX IX1_redo 
    ON dbo.MyTable (curtis_jackson) 
        INCLUDE (RangeFrom, RangeTo);

쿼리는 단순히 다음과 같이됩니다.

SELECT *
FROM MyTable
WHERE curtis_jackson = 1;

시작 쿼리와 동일한 결과를 반환합니다. 실행 계획이 해제 된 상태에서 통계는 다음과 같습니다 (간단하게 자름).

Table 'MyTable'. Scan count 1, logical reads 3...

SQL Server Execution Times:
  CPU time = 0 ms,  elapsed time = 0 ms.

그리고 여기의 쿼리 계획 :

견과류


에 인덱스가있는 계산 열 / 필터링 된 인덱스의 모방을 극복 할 수 없습니까 WHERE (50000000 BETWEEN RangeFrom AND RangeTo) INCLUDE (..)?
ypercubeᵀᴹ

3
@ yper-crazyhat-cubeᵀᴹ-그렇습니다. CREATE INDEX IX1_redo ON dbo.MyTable (curtis_jackson) INCLUDE (RangeFrom, RangeTo) WHERE RangeTo >= 50000000 AND RangeFrom <= 50000000작동 할 것이다. 그리고 쿼리 SELECT * FROM MyTable WHERE RangeTo >= 50000000 AND RangeFrom <= 50000000;는 그것을 사용하므로 가난한 커티스가 많이 필요하지 않습니다
Martin Smith

3

내 솔루션은 간격에 알려진 최대 너비 W 가 있다는 관찰을 기반으로합니다 . 샘플 데이터의 경우 1 바이트 또는 256 개의 정수입니다. 따라서 주어진 검색 매개 변수 값 P 에 대해 결과 집합에있을 수있는 가장 작은 RangeFrom이 P-W 임을 알 수 있습니다 . 이것을 술어에 추가하면

declare @P int = 50000000;
declare @W int = 256;

select
    *
from MyTable
where @P between RangeFrom and RangeTo
and RangeFrom >= (@P - @W);

원래 설정 및 쿼리 내 컴퓨터 (64 비트 Windows 10, 4 코어 하이퍼 스레드 i7, 2.8GHz, 16GB RAM)가 13 행을 반환합니다. 이 쿼리는 (RangeFrom, RangeTo) 인덱스의 병렬 인덱스 검색을 사용합니다. 수정 된 쿼리는 또한 동일한 인덱스에서 병렬 인덱스 검색을 수행합니다.

원래 쿼리와 수정 된 쿼리에 대한 측정 값은 다음과 같습니다.

                          Original  Revised
                          --------  -------
Stats IO Scan count              9        6
Stats IO logical reads       11547        6

Estimated number of rows   1643170  1216080
Number of rows read        5109666       29
QueryTimeStats CPU             344        2
QueryTimeStats Elapsed          53        0

원래 쿼리의 경우 읽은 행 수는 @P보다 작거나 같은 행 수와 같습니다. 쿼리 최적화 프로그램 (QO)은 대안이 없지만 이들 행이 술어를 만족시킬 것인지를 사전에 판별 할 수 없으므로 모든 것을 읽습니다. (RangeFrom, RangeTo)의 다중 열 인덱스는 적용 할 수있는 첫 번째 인덱스 키와 두 번째 인덱스 키 사이에 상관 관계가 없으므로 RangeTo와 일치하지 않는 행을 제거하는 데 유용하지 않습니다. 예를 들어, 첫 번째 행은 작은 간격을 가지고 제거 될 수 있지만 두 번째 행은 큰 간격을 가지고 리턴되며 그 반대도 마찬가지입니다.

한 번의 실패한 시도에서 확인 제약 조건을 통해 확실성을 제공하려고했습니다.

alter table MyTable with check
add constraint CK_MyTable_Interval
check
(
    RangeTo <= RangeFrom + 256
);

아무런 차이가 없었습니다.

데이터 분배에 대한 외부 지식을 술어에 통합함으로써 QO가 낮은 값의 RangeFrom 행을 건너 뛸 수 있습니다. 결과 행에는 절대로 포함되지 않으며 인덱스의 맨 앞 열을 허용 가능한 행으로 이동합니다. 이는 각 쿼리마다 다른 탐색 술어에 표시됩니다.

미러 인수에서 RangeTo의 상한은 P + W 입니다. 그러나 다중 열 인덱스의 후행 열이 행을 제거 할 수있게하는 RangeFrom과 RangeTo 사이에는 상관 관계가 없으므로 유용하지 않습니다. 따라서이 절을 쿼리에 추가하면 이점이 없습니다.

이 방법은 작은 간격 크기에서 대부분의 이점을 얻습니다. 가능한 간격 크기가 증가함에 따라 건너 뛴 낮은 값의 행 수가 감소하지만 일부는 여전히 건너 뜁니다. 제한적인 경우에, 데이터 범위만큼 큰 간격으로,이 접근법은 원래 질의보다 나쁘지 않습니다.

이 답변에 존재할 수있는 모든 오류에 대해 사과드립니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.