기본 키에 지정된 정렬 순서이지만 정렬은 SELECT에서 실행됩니다.


15

센서 데이터를 테이블 SensorValues 에 저장하고 있습니다. 테이블 및 기본 키는 다음과 같습니다.

CREATE TABLE [dbo].[SensorValues](
  [DeviceId] [int] NOT NULL,
  [SensorId] [int] NOT NULL,
  [SensorValue] [int] NOT NULL,
  [Date] [int] NOT NULL,
CONSTRAINT [PK_SensorValues] PRIMARY KEY CLUSTERED 
(
  [DeviceId] ASC,
  [SensorId] ASC,
  [Date] DESC
) WITH (
    FILLFACTOR=75,
    DATA_COMPRESSION = PAGE,
    PAD_INDEX = OFF,
    STATISTICS_NORECOMPUTE = OFF,
    SORT_IN_TEMPDB = OFF,
    IGNORE_DUP_KEY = OFF,
    ONLINE = OFF,
    ALLOW_ROW_LOCKS = ON,
    ALLOW_PAGE_LOCKS = ON)
  ON [MyPartitioningScheme]([Date])

그러나 특정 시간 동안 유효한 센서 값을 선택하면 실행 계획에 따라 정렬 중임을 알려줍니다. 왜 그런 겁니까?

Date 열로 정렬 된 값을 저장하므로 정렬이 수행되지 않을 것이라고 생각했을 것입니다. 아니면 인덱스가 날짜 열만 기준으로 정렬되지 않았기 때문에, 즉 결과 집합이 정렬되었다고 가정 할 수 없습니까?

SELECT TOP 1 SensorValue
  FROM SensorValues
  WHERE SensorId = 53
    AND DeviceId = 3819
    AND Date < 1339225010
  ORDER BY Date DESC

실행 계획

편집 : 대신 할 수 있습니까?

테이블이 DeviceId, SensorId, Date 로 정렬되어 있고 하나의 DeviceId 및 하나의 SensorId 만 지정 하여 SELECT 를 수행 하므로 출력 세트는 이미 Date DESC 로 정렬되어야합니다 . 그래서 다음 질문이 모든 경우에 동일한 결과를 낼지 궁금합니다.

SELECT TOP 1 SensorValue
  FROM SensorValues
  WHERE SensorId = 53
    AND DeviceId = 3819
    AND Date < 1339225010

아래 @Catcall에 따르면 정렬 순서는 저장 순서와 다릅니다. 즉, 반환 된 값이 이미 정렬 된 순서라고 가정 할 수 없습니다.

편집 : 나는이 CROSS APPLY 솔루션을 시도했지만 운이 없다.

@Martin Smith는 파티션에 대한 결과를 외부에 적용하려고 시도했습니다. 이 비슷한 문제를 설명 하는 블로그 게시물 ( 파티션 된 테이블의 정렬 된 비 클러스터형 인덱스 )을 발견하고 Smith가 제안한 것과 비슷한 솔루션을 시도했습니다. 그러나 운이 좋으면 실행 시간이 내 원래 솔루션과 동일합니다.

WITH Boundaries(boundary_id)
AS
(
  SELECT boundary_id
  FROM sys.partition_functions pf
  JOIN sys.partition_range_values prf ON pf.function_id = prf.function_id
  WHERE pf.name = 'PF'
  AND prf.value <= 1339225010
  UNION ALL
  SELECT max(boundary_id) + 1
  FROM sys.partition_functions pf
  JOIN sys.partition_range_values prf ON pf.function_id = prf.function_id
  WHERE pf.name = 'PF'
  AND prf.value <= 1339225010
),
Top1(SensorValue)
AS
(
  SELECT TOP 1 d.SensorValue
  FROM Boundaries b
  CROSS APPLY
  (
    SELECT TOP 1 SensorValue
      FROM SensorValues
      WHERE  SensorId = 53
        AND DeviceId = 3819
        AND "Date" < 1339225010
        AND $Partition.PF(Date) = b.boundary_id
        ORDER BY Date DESC
  ) d
  ORDER BY d.Date DESC
)
SELECT SensorValue
FROM Top1

옵션 MAXDOP 1은 도움이되지 않습니다. 아래 @Martin Smith에 의해 지정된 바와 같이, 분할이 원인이되는 것 같습니다 ...
m__

답변:


13

파티션되지 않은 테이블의 경우 다음 계획을 얻습니다.

계획 1

에 하나의 탐색 술어가 Seek Keys[1]: Prefix: DeviceId, SensorId = (3819, 53), Start: Date < 1339225010있습니다.

SQL Server가 평등 처음 두 열을 추구 수행 할 수있는 다음에서 시작 범위를 시크 시작한다는 의미 1339225010및 정렬 FORWARD(인덱스가 정의 될 때 [Date] DESC)

TOP첫 번째 행은 출사 후에 추구에서 작업자가 행을 요청 멈출 것이다.

파티션 구성표와 함수를 만들 때

CREATE PARTITION FUNCTION PF (int)
AS RANGE LEFT FOR VALUES (1000, 1339225009 ,1339225010 , 1339225011);
GO
CREATE PARTITION SCHEME [MyPartitioningScheme]
AS PARTITION PF
ALL TO ([PRIMARY] );

그리고 다음 데이터로 테이블을 채 웁니다.

INSERT INTO [dbo].[SensorValues]    
/*500 rows matching date and SensorId, DeviceId predicate*/
SELECT TOP (500) 3819,53,1, ROW_NUMBER() OVER (ORDER BY (SELECT 0))           
FROM master..spt_values
UNION ALL
/*700 rows matching date but not SensorId, DeviceId predicate*/
SELECT TOP (700) 3819,52,1, ROW_NUMBER() OVER (ORDER BY (SELECT 0))           
FROM master..spt_values
UNION ALL 
/*1100 rows matching SensorId, DeviceId predicate but not date */
SELECT TOP (1100) 3819,53,1, ROW_NUMBER() OVER (ORDER BY (SELECT 0)) + 1339225011      
FROM master..spt_values

SQL Server 2008 계획은 다음과 같습니다.

계획 2

seek에서 생성 된 실제 행 수는 500입니다. 계획은 탐색 술어를 보여줍니다.

Seek Keys[1]: Start: PtnId1000 <= 2, End: PtnId1000 >= 1, 
Seek Keys[2]: Prefix: DeviceId, SensorId = (3819, 53), Start: Date < 1339225010

여기에 설명 된 스킵 스캔 방법을 사용하고 있음을 표시

하나의 조건을 가진 탐색 또는 스캔 조작이 PartitionID (논리 선행 컬럼으로) 및 가능하면 다른 인덱스 키 컬럼에서 수행 될 수 있고 다른 조건을 가진 2 차 탐색이 수행 될 수 있도록 쿼리 최적화 프로그램이 확장됩니다. 첫 번째 레벨 탐색 조작에 대한 규정을 충족시키는 각각의 고유 한 값에 대해 하나 이상의 추가 열에서.

이 계획은 직렬 계획이므로 특정 쿼리에 대해 SQL Server에서 파티션을 date원래 계획 의 내림차순으로 TOP처리 한 경우 여전히 작동하며 일치하는 첫 번째 행 이후에 처리를 중지 할 수 있습니다. 나머지 499 개의 경기를 계속 진행하는 대신 발견했습니다.

실제로 2005 년 계획은 그러한 접근 방식을 취하는 것처럼 보입니다.

2005 년 계획

2008 년에 동일한 계획을 세우는 것이 옳은지 확실하지 않거나 시뮬레이션을 위해 OUTER APPLY온 이 필요할 수도 sys.partition_range_values있습니다.



9

많은 사람들이 클러스터형 인덱스 가 출력 에서 정렬 순서를 보장한다고 생각합니다 . 그러나 그것이하는 일은 아닙니다. 디스크 의 스토리지 순서를 보장합니다 .

참조, 예를 들어, 이 블로그 게시물 , 그리고 이 더 이상 토론 .


1
음, 이전에 OP는 "날짜 열에 의해 정렬 된 값을 저장하기 때문에 정렬이 이루어지지 않을 것이라고 생각했을 것입니다." 따라서 문제의 적어도 일부는 클러스터형 인덱스의 기능에 대한 오해입니다. 어쨌든 그것을 똑 바르게하는 것이 좋다고 생각합니다.
Mike Sherrill 'Cat

어쩌면 나는 고집이 났을 것입니다 (그래서 저를 용서하십시오 ;-)). 어쨌든, Hugo Kornelis의 블로그 게시물을 읽었으며 매우 간단합니다. 그러나 그의 예에서 그는 하나의 클러스터형 인덱스와 하나의 비 클러스터형을 사용하고 있으며 비 클러스터형 인덱스는 크기가 더 작아서 실행 계획에 사용되고 있습니다. 내 경우에는 클러스터 된 인덱스가 하나만 있는데 SQL 서버는 여전히 잘못된 순서로 값을 반환 할 수 있습니까 (사용할 인덱스가 작고 전체 테이블 스캔이 너무 느립니다)?
m__

나는 이것을 새로운 질문 으로 옮겼습니다 (주제
m__

5

병렬 계획으로 인해 SORT가 필요하다고 추측합니다. 나는 이것을 희미하고 먼 블로그 기사를 기반으로하지만, 이것을 MSDN 에서 찾 거나 이것을 정당화하지 않을 수도 있습니다.

MAXDOP 1을 사용 해보고 무슨 일이 일어나는지보십시오 ...

또한 "Exchange Operator"아래의 Simple Talk대한 @sql kiwi의 블로그 게시물 에서 힌트를 얻었습니다 . 그리고 "DOP ​​의존성"


date이전에 파티션 기능을 설정하지 않았지만 . 이제 분할을하고있는 것 같습니다.이 특정 쿼리에서 2005가 더 잘 동작 할 수있는 원인이되었습니다.
Martin Smith

1

기본적으로 기본 키는 "DeviceId, SensorId, Date"순서이므로 키의 데이터는 날짜별로 정렬되지 않으므로 사용할 수 없습니다. 키가 다른 순서에있는 경우 "날짜,의 DeviceID, SensorId은"다음 키의 데이터는 때문에 사용할 수, 날짜별로 정렬 할 수 ...


이미 언급 한 방식으로 키를 변경하려고했기 때문에 죄송합니다. 어쨌든 3 열 모두에 대해 클러스터되지 않은 인덱스를 작성하여 그 결과를 확인하십시오. (누락 된 색인에 대한 탐색은 계속됩니다 ... ;-))
m__
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.