큰 데이터 집합을 기준으로 시간별 그룹화


12

MS SQL 2008을 사용하여 250 만 레코드에서 평균 필드를 선택하고 있습니다. 각 레코드는 1 초를 나타냅니다. MyField는 해당 1 초 레코드의 시간당 평균입니다. 물론 서버 CPU가 100 %에 도달하고 선택 시간이 너무 오래 걸립니다. SQL이 각 요청에서 해당 레코드를 모두 선택할 필요가 없도록 평균 값을 저장해야합니다. 무엇을 할 수 있습니까?

  SELECT DISTINCT
         CONVERT(VARCHAR, [timestamp], 1)+' '+ CAST(DATEPART(Hh,[timestamp]) as VARCHAR) AS TimeStampHour,
         MIN([timestamp]) as TimeStamp,
         AVG(MyField) As AvgField
    FROM MyData
   WHERE TimeStamp > '4/10/2011'
GROUP BY CONVERT(VARCHAR, [timestamp], 1)+' '+ CAST(DATEPART(Hh,[timestamp]) as VARCHAR)
ORDER BY TimeStamp

6
TimeStamp는 클러스터형 인덱스의 일부입니까? 그것은 ...해야한다

@antisanity-왜? 그는 CPU하지 디스크 IO를 긁고있다
잭 topanswers.xyz 시도라고

답변:


5

쿼리의 일부는 오랫동안 CPU를 최대로 늘리는 것입니다. GROUP BY 절의 함수이며 그룹화는 항상이 인스턴스에서 인덱싱되지 않은 정렬이 필요하다는 사실입니다. 타임 스탬프 필드의 인덱스는 초기 필터에 도움이되지만 필터와 일치하는 모든 행에서이 작업을 수행해야합니다. 이 속도를 높이는 것은 Alex가 제안한 것과 동일한 작업을 수행하기 위해보다 효율적인 경로를 사용하지만 쿼리 플래너를 사용하는 기능 조합이 나오지 않기 때문에 여전히 비효율적입니다. 어떤 인덱스가 도움이 될 것이므로 그룹화 값을 계산하기 위해 먼저 함수를 실행하는 모든 행을 실행해야합니다. 그러면 데이터를 정렬하고 결과 그룹화에 대한 집계를 계산할 수 있습니다.

따라서 해결책은 프로세스 그룹이 색인을 사용할 수있는 방법으로 프로세스 그룹을 만들거나 일치하는 모든 행을 한 번에 고려해야 할 필요성을 제거하는 것입니다.

시간을 반올림 한 시간을 포함하는 각 행에 대해 여분의 열을 유지 관리하고 이러한 쿼리에 사용하기 위해이 열을 색인화 할 수 있습니다. 이것은 데이터를 비정규 화하고 있으므로 "더러운"느낌이 들지만 나중에 사용할 수 있도록 모든 집계를 캐싱하는 것 (및 기본 데이터가 변경 될 때 해당 캐시를 업데이트하는 것)보다 더 깨끗하고 효과적입니다. 여분의 열은 데이터를 삽입하거나 타임 스탬프 열 또는 기존 행을 업데이트 할 수있는 현재 및 미래의 모든 장소가 새로운 데이터에서 일관된 데이터를 생성 할 수 있기 때문에 다른 곳에서는 논리에 의해 유지되는 것이 아니라 트리거에 의해 유지되거나 지속적으로 계산 된 열이어야합니다. 기둥. 여전히 MIN (타임 스탬프)을 가져올 수 있습니다. 쿼리가 이런 식으로 결과를 얻는 것은 여전히 ​​모든 행을 걸어 내려가는 것입니다 (물론 피할 수는 없지만) 인덱스 순서를 수행 할 수 있습니다. 그룹화 / 집계를 수행하기 전에 인덱싱되지 않은 정렬 작업을 위해 전체 행 집합을 기억할 필요없이 인덱스의 다음 값에 도달 할 때 각 그룹화에 대한 행을 출력합니다. 현재보고있는 값이나 나머지 부분을 처리하기 위해 이전 그룹화 값의 행을 기억할 필요가 없으므로 메모리도 훨씬 적게 사용합니다.

이 방법을 사용하면 전체 결과 집합에 대해 메모리에서 어딘가를 찾을 필요가 없으며 그룹 작업에 대해 색인화되지 않은 정렬을 수행하고 큰 쿼리에서 그룹 값 계산을 제거합니다 (작업을 해당 작업을 생성하는 개별 INSERT / UPDATE로 이동). 데이터)) 집계 된 결과의 별도 저장소를 유지할 필요없이 이러한 쿼리를 수용 가능하게 실행할 수 있어야합니다.

그렇지 않은 방법데이터를 비정규 화하지만 여전히 추가 구조가 필요한 경우에는 "시간표"(이 경우 시간당 한 행씩 포함 된 행)를 사용하는 것이 좋습니다. 이 테이블은 DB 또는 상당한 크기의 공간을 많이 소비하지 않습니다. 두 날짜의 한 행 (시간의 시작과 끝, 예 : '2011-01-01 @ 00 : 00 : 00.0000 ','2011-01-01 @ 00 : 00 : 59.9997 ', "9997"은 DATETIME 필드가 다음 초로 반올림되지 않는 최소 밀리 초입니다. 클러스터 된 기본 키는 ~ 14Mbyte의 공간을 차지합니다 (행당 8 + 8 바이트 * 24 시간 / 일 * 365.25 일 / 년 * 100). .

SELECT CONVERT(VARCHAR, [timestamp], 1)+' '+ CAST(DATEPART(Hh,[timestamp]) as VARCHAR) AS TimeStampHour
     , MIN([timestamp]) as TimeStamp
     , AVG(MyField) As AvgField
FROM TimeRangeByHours tt
INNER JOIN MyData md ON md.TimeStamp BETWEEN tt.StartTime AND tt.EndTime
WHERE tt.StartTime > '4/10/2011'
GROUP BY tt.StartTime
ORDER BY tt.StartTime

이는 쿼리 플래너가 MyData.TimeStamp의 인덱스를 사용하도록 정렬 할 수 있음을 의미합니다. 쿼리 플래너는 MyData.TimeStamp의 인덱스를 사용하여 단계적으로 길들이기 테이블을 걸을 수 있도록 그룹화 할 수있을 정도로 밝아 야합니다. 그룹 화당 하나의 행을 다시 출력하고 다음 그룹화 값에 도달하면 각 세트를 삭제합니다. RAM의 어딘가에 모든 중간 행을 저장하지 않고 색인화되지 않은 정렬을 수행하지 않습니다. 물론이 방법을 사용하려면 시간표를 작성하여 시간이 앞뒤로 충분히 넓어야하지만 "추가 열"옵션과 같이 다른 쿼리의 여러 날짜 필드에 대한 쿼리에 시간표를 사용할 수 있습니다. 이러한 방식으로 필터링 / 그룹화해야하는 각 날짜 필드에 대한 추가 계산 열과 테이블의 작은 크기 (10 개 범위에 걸쳐 필요하지 않은 경우)

시간표 방법은 현재 상황 및 계산 된 열 솔루션과 비교할 때 추가 이점 (매우 유리할 수 있음)이 있습니다. 위의 예제 쿼리에서 INNER JOIN을 변경하여 데이터가없는 기간 동안 행을 리턴 할 수 있습니다. 왼쪽 외부가됩니다.

어떤 사람들은 물리적 시간표가 없지만 항상 테이블 반환 함수에서 반환하는 것이 좋습니다. 이것은 시간표의 내용이 디스크에 저장되거나 읽혀질 필요가 없다는 것을 의미하며 함수가 제대로 작성되면 시간표가 시간과 시간에 얼마나 오래 걸리는지 걱정할 필요가 없습니다. 의심의 여지없이 모든 행에 대해 메모리 내 테이블을 생성하는 CPU 비용이 실제 시간 테이블을 생성 (및 초기 버전의 한계를 초과하여 확장해야 할 경우 유지 관리해야하는 번거 로움)를 줄일 가치가 있습니다.

참고 사항 : 원래 쿼리에 DISTINCT 절이 필요하지 않습니다. 그룹화는 이러한 쿼리가 고려중인 기간 당 하나의 행만 리턴하도록 보장하므로 DISTINCT는 CPU를 조금 더 회전시키는 것 외에는 아무 것도 수행하지 않습니다 (쿼리 플래너가 구별이 아무 문제가 없음을 통지하지 않는 한) 무시하고 추가 CPU 시간을 사용하지 마십시오).


3

이 질문 ( 날짜 날짜 )을 참조하십시오. 또한 모든 것을 문자열로 변환하는 데 귀찮은 이유는 나중에 수행 할 수 있습니다 (필요한 경우).

  SELECT DISTINCT
         dateadd(hour,datediff(hour,0,[timestamp]),0) AS TimeStampHour,
         MIN([timestamp]) as TimeStamp,
         AVG(MyField) As AvgField
    FROM MyData
   WHERE TimeStamp > '4/10/2011'
GROUP BY dateadd(hour,datediff(hour,0,[timestamp],0);
ORDER BY TimeStamp

1

쿼리 속도를 높이고 싶거나 데이터 스냅 샷을 만들어 저장하는 방법을 묻고 있습니까?

더 빠르게하려면 TimeStamp 필드에 대한 인덱스가 필요합니다. 또한 이것을 사용하여 시간으로 변환하는 것이 좋습니다.

select convert(varchar(13), getdate(), 121)

스냅 샷 insert into을 작성 하고 재사용해야하는 경우 나중에 쿼리 결과가 포함 된 새 테이블을 작성하는 데 사용 합니다. 인덱스 테이블에 따라 사용하십시오. 내가 이해 한 것부터 TimeStampHour에 대한 색인이 필요합니다.

또한 새 집계표에서 매일 데이터를 집계하는 작업을 설정할 수 있습니다.


-1

group by 절을 이와 같은 문자열로 변환하면 기본적으로 데이터베이스의 모든 단일 행에 대해 색인화되지 않은 히트가됩니다. 이것이 성능을 저하시키는 원인입니다. 중간 정도의 서버는 인덱스를 올바르게 사용하면 백만 개의 레코드에서와 같은 간단한 집계를 처리 할 수 ​​있습니다. 쿼리를 수정하고 타임 스탬프에 클러스터 된 인덱스를 넣습니다. 성능 문제를 해결하는 반면 매시간 데이터를 계산하면 문제가 해결됩니다.


1
-1 - 아니 당신은 "데이터베이스의 모든 단일 행에에게 색인화되지 않은 타격을"하지 않습니다 -에 어떤 인덱스를 TimeStamp여전히 사용되는이 행을 필터링
잭은 말한다 topanswers.xyz 시도

-3

관계형 데이터베이스 모델을 사용하여 이러한 종류의 계산을 구현한다는 아이디어를 포기하는 것을 고려할 것입니다. 특히 매 초마다 값을 수집하는 많은 데이터 포인트가있는 경우.

돈이 있다면 다음과 같은 전용 프로세스 데이터 역사가를 구입할 것을 고려할 수 있습니다.

  1. 하니웰 균일 성 PHD
  2. 오시 소프트 PI
  3. 아스 펜텍 IP21
  4. 기타

이 제품들은 대량의 엄밀하게 조밀 한 시계열 데이터를 독점 형식으로 저장할 수 있으며 동시에 데이터 추출 쿼리를 신속하게 처리 할 수 ​​있습니다. 쿼리는 많은 데이터 포인트 (태그라고도 함), 긴 시간 간격 (월 / 년)을 지정할 수 있으며 추가로 광범위한 요약 데이터 계산 (평균 포함)을 수행 할 수 있습니다.

.. 및 일반적인 참고 사항 : DISTINCTSQL을 작성할 때 항상 키워드를 사용하지 마십시오 . 좋은 아이디어는 아닙니다. 귀하의 경우에 당신은 드롭 할 수 있어야 DISTINCT하고 추가하여 동일한 결과를 얻을 MIN([timestamp])당신에게 GROUP BY절.


1
이것은 실제로 정확하지 않습니다. 관계형 데이터베이스는 250 만 레코드에 완벽하게 적합합니다. 그리고 그는 많은 테이블에서 조인조차하지 않습니다. 데이터를 비정규 화하거나 관계형이 아닌 시스템으로 이동해야한다는 첫 번째 징후는 여러 테이블에서 크고 복잡한 조인을 수행 할 때입니다. 포스터의 데이터 세트는 실제로 관계형 데이터베이스 시스템을 완벽하게 수용하는 것처럼 들립니다.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.