여러 시간대의 데이터에 대해보고하기위한 데이터웨어 하우스 디자인


10

여러 시간대의 데이터에 대한보고를 지원할 데이터웨어 하우스 디자인을 최적화하려고합니다. 예를 들어, 한 시간 분량의 활동을 표시해야하는 한 달 분량의 활동 (수백만 행)에 대한 보고서가있을 수 있습니다. 물론 하루 중 해당 시간은 지정된 시간대의 "현지"시간이어야합니다.

우리는 UTC와 현지 시간을 지원했을 때 잘 작동했습니다. UTC 및 현지 시간에 대한 날짜 및 시간 차원의 표준 설계 ID는 팩트 테이블에 있습니다. 그러나 100 개 이상의 시간대에 대한보고를 지원해야하는 경우 이러한 접근 방식이 확장되지 않는 것 같습니다.

우리의 사실 테이블은 매우 넓어 질 것입니다. 또한 특정 보고서 실행시 그룹화에 사용할 날짜 및 시간 ID를 지정하는 SQL의 구문 문제를 해결해야합니다. 아마도 매우 큰 CASE 선언문입니까?

나는 당신이 다루고있는 UTC 시간 범위로 모든 데이터를 얻는 제안을 보았고 프리젠 테이션 레이어로 돌려 로컬로 변환하고 집계하지만 SSRS를 사용한 제한된 테스트는 그것이 매우 느릴 것이라고 제안합니다.

나는 주제에 관한 책을 참고했는데 모두 UTC 만 있고 디스플레이로 변환하거나 UTC와 로컬이 하나 있다고 말합니다. 어떤 생각이나 제안을 부탁드립니다.

참고 :이 질문은 다음과 유사합니다 : 데이터 마트 / 창고의 시간대 처리 .하지만 그 질문에 대해서는 언급 할 수 없으므로 이것이 그 자체의 질문이라고 생각합니다.

업데이트 : Aaron이 중요한 업데이트를하고 샘플 코드와 다이어그램을 게시 한 후 Aaron의 대답을 선택했습니다. 그의 답변에 대한 나의 이전 의견은 원래 답변의 편집을 언급했을 때 더 이상 의미가 없습니다. 필요한 경우 다시 와서 다시 업데이트하려고합니다.


내 답변 (그리고 나중에 업데이트 할 내용)과 관련하여 데이터가 얼마나 멀리 이동합니까? 월간 보고서에 24 시간 단위 28-31 세트가 표시됩니까? 항상 "달력"입니까 아니면 실제로 어떤 범위 일 수 있습니까? 선택한 시간대의 날짜 중 하나가 DST 스프링 포워드 / 롤백 날짜 인 경우 무엇을 표시해야합니까? 또한 보고서의 입력 내용은 정확히 무엇입니까? 현재 로케일을 기준으로 사용자의 현지 시간을 UTC로 자동 변환하거나, 환경 설정이 있거나, 수동으로 선택하거나, 다른 방법으로 추론하거나, 쿼리에서 알아 내기를 원하십니까?
Aaron Bertrand

질문에 대답하기 위해 : 데이터는 최대 2 년으로 거슬러 올라갑니다. 보고서 시간 범위에서 하루에 24 시간 청크가 한 세트 만 표시되는 일부 보고서와 하루 24 시간 청크가있는 기타 보고서가 있습니다. 날짜 범위는 실제로 사용자가 원하는 모든 것이 될 수 있습니다. 사용자는 시작 및 종료 날짜 (및 시간)를 선택한 다음 드롭 다운에서 원하는 시간대를 선택합니다
Peter M

답변:


18

매우 간단한 달력 테이블을 사용 하여이 문제를 해결했습니다. 매년 표준 시간대 와 표준 날짜 오프셋과 DST의 시작 날짜 시간 / 종료 날짜 시간 및 오프셋 (해당 시간대가 지원하는 경우)과 함께 지원되는 시간대 당 하나의 행이 있습니다. 그런 다음 소스 시간 (UTC 기준)을 가져오고 오프셋을 더하거나 빼는 인라인 스키마 바인딩 테이블 반환 함수입니다.

많은 양의 데이터에 대해보고하는 경우 성능이 크게 향상되지 않습니다. 파티셔닝은 도움이 될 수 있지만 특정 시간대로 변환 할 때 1 년의 마지막 몇 시간 또는 다음 해의 첫 몇 시간이 실제로 다른 연도에 속하는 경우가 있으므로 실제 파티션을 얻을 수 없습니다. 보고 범위에 12 월 31 일 또는 1 월 1 일이 포함되지 않은 경우를 제외하고

고려해야 할 몇 가지 이상한 경우가 있습니다.

  • 예를 들어 2014-11-02 05:30 UTC와 2014-11-02 06:30 UTC는 동부 표준시에서 오전 1시 30 분으로 변환됩니다 (예 : 처음으로 01:30은 로컬로 충돌 한 후 하나). 시계가 오전 2:00에서 오전 1:00로 롤백되고 두 번째 반 시간이 경과 한 두 번째 시간). 따라서 해당 시간의보고 처리 방법을 결정해야합니다. UTC에 따르면 DST를 관찰하는 시간대에서 두 시간이 단일 시간에 매핑되면 측정하는 트래픽의 양이나 트래픽이 두 배가됩니다. 다른 이벤트가 나타날 때 논리적으로 일어난 일이 있기 때문에 이벤트 순서 지정으로 재미있는 게임을 할 수도 있습니다.타이밍이 2 시간 대신 1 시간으로 조정되면 그 전에 발생합니다. 극단적 인 예는 05:59 UTC에 발생한 페이지 조회수와 06:00 UTC에 발생한 클릭 수입니다. UTC 시간은 1 분 간격으로 발생했지만 동부 시간으로 변환하면 오전 1시 59 분에 조회가 발생했으며 1 시간 전에 클릭이 발생했습니다.

  • 2014-03-09 02:30 미국에서는 절대 발생하지 않습니다. 오전 2시에 시계를 오전 3 시로 롤 포워드하기 때문입니다. 따라서 사용자가 그러한 시간을 입력하고 UTC로 변환하도록 요청하거나 사용자가 그러한 시간을 선택할 수 없도록 양식을 설계하도록 요청하면 오류가 발생하기를 원할 것입니다.

이러한 경우를 염두에두고도 여전히 올바른 접근 방법이 있다고 생각합니다. UTC로 데이터를 저장하십시오. 특히 다른 시간대가 다른 날짜에 DST를 시작 / 종료하고 동일한 시간대조차도 다른 연도에 다른 규칙을 사용하여 전환 할 수있는 경우, 일부 시간대에서 다른 시간대로 UTC에서 다른 시간대로 데이터를 매핑하는 것이 훨씬 쉽습니다 ( 예를 들어 미국은 6 년 전 규칙을 변경했습니다).

이 아니라 어떤 거대한의 모든 달력 테이블을 사용하는 것이 좋습니다 CASE 표현 (안 ). 방금 MSSQLTips.com 에 대한 3 부로 구성된 시리즈를 작성 했습니다. 세 번째 부분이 가장 유용하다고 생각합니다.

http://www.mssqltips.com/sqlservertip/3173/handle-conversion-between-time-zones-in-sql-server--part-1/

http://www.mssqltips.com/sqlservertip/3174/handle-conversion-between-time-zones-in-sql-server--part-2/

http://www.mssqltips.com/sqlservertip/3175/handle-conversion-between-time-zones-in-sql-server--part-3/


그 동안 실제 예

매우 간단한 팩트 테이블이 있다고 가정 해 봅시다. 이 경우 내가 염려하는 것은 이벤트 시간이지만, 테이블을 충분히 넓게 만들기 위해 의미없는 GUID를 추가 할 것입니다. 다시 말하지만 사실 테이블은 UTC 시간과 UTC 시간으로 만 이벤트를 저장합니다. 심지어 열에 접미사를 _UTC붙여 혼동이 없습니다.

CREATE TABLE dbo.Fact
(
  EventTime_UTC DATETIME NOT NULL,
  Filler UNIQUEIDENTIFIER NOT NULL DEFAULT NEWSEQUENTIALID()
);
GO

CREATE CLUSTERED INDEX x ON dbo.Fact(EventTime_UTC);
GO

이제 2013-12-30부터 자정 UTC의 2013-12-30부터 2014-12-12의 UTC 오전 5 시까 지 3 초마다 (1 시간에 1,200 행) 10,000,000 개의 행으로 팩트 테이블을로드 해 보겠습니다. 이를 통해 데이터가 연도 경계를 넘어 여러 표준 시간대의 DST 앞뒤로 이동합니다. 이것은 정말로 무서워 보이지만 내 시스템에서 ~ 9 초가 걸렸습니다. 테이블의 크기는 약 325MB입니다.

;WITH x(c) AS 
(
  SELECT TOP (10000000) DATEADD(SECOND, 
    3*(ROW_NUMBER() OVER (ORDER BY s1.[object_id])-1),
    '20131230')
  FROM sys.all_columns AS s1
  CROSS JOIN sys.all_columns AS s2
  ORDER BY s1.[object_id]
)
INSERT dbo.Fact WITH (TABLOCKX) (EventTime_UTC) 
  SELECT c FROM x;

그리고이 쿼리를 실행하면이 10MM 행 테이블에 대해 일반적인 검색 쿼리가 어떻게 표시되는지 보여주기 위해

SELECT DATEADD(HOUR, DATEDIFF(HOUR, 0, EventTime_UTC), 0),
  COUNT(*)
FROM dbo.Fact 
WHERE EventTime_UTC >= '20140308'
AND EventTime_UTC < '20140311'
GROUP BY DATEADD(HOUR, DATEDIFF(HOUR, 0, EventTime_UTC), 0);

이 계획을 세우고 358 회 읽기를 수행하여 25 밀리 초 *에 반환되어 총 72 시간을 반환합니다.

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

* 무료 SQL Sentry Plan Explorer 에서 측정 한 결과는 버려 지므로 결과를 버리므로 데이터의 네트워크 전송 시간, 렌더링 등은 포함되지 않습니다. 추가 면책 조항으로 SQL Sentry에서 일합니다.

범위를 너무 크게하면 한 달의 데이터에 258ms, 2 개월에 500ms 이상이 걸리는 등 시간이 조금 더 걸립니다. 병렬 처리가 시작될 수 있습니다.

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

여기서는보고 쿼리를 만족시키기위한 다른 더 나은 솔루션에 대해 생각하기 시작하며 출력이 표시되는 시간대와는 아무런 관련이 없습니다. 나는 그것에 동의하지 않을 것입니다. 저는 시간대 변환이 실제로보고 쿼리를 훨씬 더 많이 빨아 들일 것이 아니며 적절한 지원되지 않는 넓은 범위를 얻는다면 이미 빨려들 수 있음을 보여주고 싶습니다. 색인. 논리가 올바르다는 것을 보여주기 위해 작은 날짜 범위를 고수하고, 시간대 변환 유무에 관계없이 범위 기반보고 쿼리가 제대로 수행되는지 걱정할 수 있습니다.

이제 시간대를 저장하기위한 테이블이 필요합니다 (오프셋은 분 단위, 모든 사람이 UTC를 벗어난 시간이 아니기 때문에). 그리고 지원되는 각 연도의 DST 변경 날짜가 필요합니다. 간단히하기 위해 위의 데이터와 일치시키기 위해 몇 개의 시간대와 1 년만 입력합니다.

CREATE TABLE dbo.TimeZones
(
  TimeZoneID TINYINT    NOT NULL PRIMARY KEY,
  Name       VARCHAR(9) NOT NULL,
  Offset     SMALLINT   NOT NULL, -- minutes
  DSTName    VARCHAR(9) NOT NULL,
  DSTOffset  SMALLINT   NOT NULL  -- minutes
);

다양한 표준 시간대가 포함되어 있으며 일부는 30 분 오프셋이 있으며 일부는 DST를 준수하지 않습니다. 자신의 시계를 갈 수 있도록 호주는 남반구에, 우리의 겨울 동안 DST를 준수합니다 다시 4 월과 앞으로 10월있다. (위의 표는 이름을 바꾸지 만 남반구 시간대에 대해 덜 혼란스럽게 만드는 방법을 모르겠습니다.)

INSERT dbo.TimeZones VALUES
(1, 'UTC',     0, 'UTC',     0),
(2, 'GMT',     0, 'BST',    60), 
     -- London = UTC in winter, +1 in summer
(3, 'EST',  -300, 'EDT',  -240), 
     -- East coast US (-5 h in winter, -4 in summer)
(4, 'ACDT',  630, 'ACST',  570), 
     -- Adelaide (Australia) +10.5 h Oct - Apr, +9.5 Apr - Oct
(5, 'ACST',  570, 'ACST',  570); 
     -- Darwin (Australia) +9.5 h year round

이제 TZ가 언제 변경되는지 알 수있는 달력 테이블. 관심있는 행만 삽입합니다 (위의 각 시간대 및 2014 년의 DST 만 변경). 계산을 쉽게하기 위해 시간대가 변경되는 UTC와 현지 시간의 동일한 순간을 모두 저장합니다. DST를 준수하지 않는 시간대의 경우 표준은 1 년 내내 표준이며 DST는 1 월 1 일에 "시작"됩니다.

CREATE TABLE dbo.Calendar
(
  TimeZoneID    TINYINT NOT NULL FOREIGN KEY
                REFERENCES dbo.TimeZones(TimeZoneID),
  [Year]        SMALLDATETIME NOT NULL,
  UTCDSTStart   SMALLDATETIME NOT NULL,
  UTCDSTEnd     SMALLDATETIME NOT NULL,
  LocalDSTStart SMALLDATETIME NOT NULL,
  LocalDSTEnd   SMALLDATETIME NOT NULL,
  PRIMARY KEY (TimeZoneID, [Year])
);

루프로 수동으로 채우는 대신 알고리즘으로 알고리즘을 채울 수 있습니다. 이 답변을 위해 5 개의 시간대에 대해 1 년을 수동으로 채우기로 결정했으며 멋진 트릭을 귀찮게하지 않을 것입니다.

INSERT dbo.Calendar VALUES
(1, '20140101', '20140101 00:00','20150101 00:00','20140101 00:00','20150101 00:00'),
(2, '20140101', '20140330 01:00','20141026 00:00','20140330 02:00','20141026 01:00'),
(3, '20140101', '20140309 07:00','20141102 06:00','20140309 03:00','20141102 01:00'),
(4, '20140101', '20140405 16:30','20141004 16:30','20140406 03:00','20141005 02:00'),
(5, '20140101', '20140101 00:00','20150101 00:00','20140101 00:00','20150101 00:00');

자, 사실 데이터와 "치수"테이블 (내가 말할 때 울부 짖음)이 있습니다. 그렇다면 논리는 무엇입니까? 글쎄, 사용자가 시간대를 선택하고 쿼리의 날짜 범위를 입력하게 할 것이라고 가정합니다. 또한 날짜 범위는 자체 시간대로 하루 종일로 가정합니다. 부분적인 일이 없으며 부분적인 시간을 신경 쓰지 마십시오. 따라서 시작 날짜, 종료 날짜 및 TimeZoneID가 전달됩니다. 여기에서 스칼라 함수를 사용하여 시작 / 종료 날짜를 해당 시간대에서 UTC로 변환하여 UTC 범위를 기준으로 데이터를 필터링 할 수 있습니다. 이 작업을 수행하고 집계를 수행 한 후 사용자에게 표시하기 전에 그룹화 된 시간의 변환을 소스 시간대에 다시 적용 할 수 있습니다.

스칼라 UDF :

CREATE FUNCTION dbo.ConvertToUTC
(
  @Source   SMALLDATETIME,
  @SourceTZ TINYINT
)
RETURNS SMALLDATETIME
WITH SCHEMABINDING
AS
BEGIN
  RETURN 
  (
    SELECT DATEADD(MINUTE, -CASE 
        WHEN @Source >= src.LocalDSTStart 
         AND @Source < src.LocalDSTEnd THEN t.DSTOffset 
        WHEN @Source >= DATEADD(HOUR,-1,src.LocalDSTStart) 
         AND @Source < src.LocalDSTStart THEN NULL
        ELSE t.Offset END, @Source)
    FROM dbo.Calendar AS src
    INNER JOIN dbo.TimeZones AS t 
    ON src.TimeZoneID = t.TimeZoneID
    WHERE src.TimeZoneID = @SourceTZ 
      AND t.TimeZoneID = @SourceTZ
      AND DATEADD(MINUTE,t.Offset,@Source) >= src.[Year]
      AND DATEADD(MINUTE,t.Offset,@Source) < DATEADD(YEAR, 1, src.[Year])
  );
END
GO

그리고 테이블 반환 함수 :

CREATE FUNCTION dbo.ConvertFromUTC
(
  @Source   SMALLDATETIME,
  @SourceTZ TINYINT
)
RETURNS TABLE
WITH SCHEMABINDING
AS
 RETURN 
 (
  SELECT 
     [Target] = DATEADD(MINUTE, CASE 
       WHEN @Source >= trg.UTCDSTStart 
        AND @Source < trg.UTCDSTEnd THEN tz.DSTOffset 
       ELSE tz.Offset END, @Source)
  FROM dbo.Calendar AS trg
  INNER JOIN dbo.TimeZones AS tz
  ON trg.TimeZoneID = tz.TimeZoneID
  WHERE trg.TimeZoneID = @SourceTZ 
  AND tz.TimeZoneID = @SourceTZ
  AND @Source >= trg.[Year] 
  AND @Source < DATEADD(YEAR, 1, trg.[Year])
);

그리고 그것을 사용하는 절차 ( 편집 : 30 분 오프셋 그룹화를 처리하도록 업데이트 됨) :

CREATE PROCEDURE dbo.ReportOnDateRange
  @Start      SMALLDATETIME, -- whole dates only please! 
  @End        SMALLDATETIME, -- whole dates only please!
  @TimeZoneID TINYINT
AS 
BEGIN
  SET NOCOUNT ON;

  SELECT @Start = dbo.ConvertToUTC(@Start, @TimeZoneID),
         @End   = dbo.ConvertToUTC(@End,   @TimeZoneID);

  ;WITH x(t,c) AS
  (
    SELECT DATEDIFF(MINUTE, @Start, EventTime_UTC)/60, 
      COUNT(*) 
    FROM dbo.Fact 
    WHERE EventTime_UTC >= @Start
      AND EventTime_UTC <  DATEADD(DAY, 1, @End)
    GROUP BY DATEDIFF(MINUTE, @Start, EventTime_UTC)/60
  )
  SELECT 
    UTC = DATEADD(MINUTE, x.t*60, @Start), 
    [Local] = y.[Target], 
    [RowCount] = x.c 
  FROM x OUTER APPLY 
    dbo.ConvertFromUTC(DATEADD(MINUTE, x.t*60, @Start), @TimeZoneID) AS y
  ORDER BY UTC;
END
GO

(사용자가 UTC로보고하려는 경우 단락을 중단하거나 별도의 저장 프로 시저를 원할 수 있습니다. 분명히 UTC로 /에서 번역하는 것은 낭비스러운 작업입니다.)

샘플 통화 :

EXEC dbo.ReportOnDateRange 
  @Start      = '20140308', 
  @End        = '20140311', 
  @TimeZoneID = 3;

41ms *로 반환하고이 계획을 생성합니다.

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

* 다시, 폐기 된 결과.

2 개월 동안 507ms 후에 반환되며 계획은 행 개수와 다릅니다.

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

약간 더 복잡하고 런타임이 약간 증가하지만이 유형의 접근 방식이 브리지 테이블 접근 방식보다 훨씬 더 잘 작동 할 것이라고 확신합니다. 그리고 이것은 dba.se 답변의 커프스 예입니다. 저보다 훨씬 똑똑한 사람들이 내 논리와 효율성을 향상시킬 수 있다고 확신합니다.

시계를 롤 포워드하는 시간에 대한 행의 행이없고 롤백 한 시간에 대한 두 행의 행이 없습니다 (그리고 그 시간이 두 번 발생했습니다). 나쁜 가치를 가지고 놀 수도 있습니다. 예를 들어 20140309 02:30 동부 시간을 지나면 제대로 작동하지 않습니다.

보고의 작동 방식에 대한 모든 가정이 없을 수도 있으므로 일부 조정을 수행해야 할 수도 있습니다. 그러나 나는 이것이 기본 사항을 다루고 있다고 생각합니다.


0

프리젠 테이션 레이어 대신 저장된 프로 시저 또는 매개 변수화 된보기에서 변환을 수행 할 수 있습니까? 또 다른 옵션은 큐브를 작성하고 큐브로 계산하는 것입니다.

의견에서 설명 :

OP는 프레젠테이션 계층에서 계산을 수행하여 제한적인 테스트로 인해 성능 문제가 발생했습니다. 내 제안은 데이터베이스로 옮기는 것입니다. SQL에서는 테이블 반환 함수를 사용하여 매개 변수화 된 뷰를 수행 할 수 있습니다. 이 함수에 전달 된 시간대를 기반으로 UTC 테이블에서 데이터를 계산하고 리턴 할 수 있습니다. 이것이 내 원래의 대답을 분명히하기를 바랍니다.


따라서 각 행의 소스 시간이 UTC로 100 개 이상인 열이 100 개 이상인 뷰는 100 개 이상의 시간대로 변환됩니까? 나는 그런 견해가 어떻게 쓰여질 지 추측조차 할 수 없습니다. 또한 SQL Server에는 "매개 변수보기"가 없습니다.
Aaron Bertrand

흠 .. 그래서 당신이 생각하는 것입니다. 그리고 그것은 내가 의미 한 것이 아닙니다.
KNI

1
다른 생각을하게 해주세요. 그건 그렇고, 난 당신의 대답에서 더 나은 명확성을 장려하려고 노력하는 투표하지 않았습니다.
Aaron Bertrand

op는 프레젠테이션 계층에서 계산을 수행하여 제한적인 테스트로 성능 문제를 겪었습니다. 내 제안은 데이터베이스로 옮기는 것입니다. SQL에서는 테이블 반환 함수를 사용하여 매개 변수화 된 뷰를 수행 할 수 있습니다. 이 함수에 전달 된 시간대를 기반으로 utc 테이블에서 데이터를 계산하고 리턴 할 수 있습니다. 이것이 내 원래의 대답을 분명히하기를 바랍니다.
KNI

데이터가 집계되면 어떻게 작동합니까? 시간대가 30 분 오프셋이면 데이터가 다른 그룹에 속합니다. 프리젠 테이션 레이어에 표시되는 레이블 만 변경할 수는 없습니다.
Colin 't Hart
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.