1

약 30 초 간격으로 데이터를 수집하는 두 개의 장치가 있습니다. 장치는 두 개의 간격이 큰 사이트에 있습니다. 각 사이트의 각 컬렉션의 절대 시간은 +/- 30 초입니다. 사이트가 여러 가지 이유로 오프라인 상태로 전환되는 경우가 있습니다. 각 기기의 데이터는 다른 종류의 측정 값을 나타냅니다. 온도는 장치 1에서, 습도는 장치 2에서 측정합니다. 프로세스는 device1과 device2의 데이터를 각 장치와 별도의 서버에서 실행되는 SQL Server 2012 Express 데이터베이스의 개별 테이블에 기록합니다.이 SQL 쿼리의 속도를 향상시키는 방법은 무엇입니까?

특정 날짜/시간에 대해 site1에 대한 값이있는 열과 사용할 수있는 경우 site2에 대한 데이터가 결합 된 레코드에 상관 된 두 장치의 데이터를 표시하는 것이 좋습니다. 그런 다음 사용자 프로그램은 지정된 날짜/시간 범위의 레코드 세트를 요청합니다. 이를 위해, 나는 다음과 같은 SP를 구성 :

ALTER PROCEDURE [db_datareader].[DataJoinDateRange] 
@DateFrom DateTime = '2014-05-15 15:10:24.000', 
@DateTo DateTime = '2014-06-15 15:10:24.000' 
AS 
BEGIN 
SET NOCOUNT ON; 
WITH site1(id, date_time, dataval) 
AS 
(
    SELECT * 
    FROM site1_data 
    WHERE site1_data.date_time BETWEEN @DateFrom AND @DateTo 
), 
site2(id, date_time, datavaql) 
AS 
(
    SELECT * 
    FROM site2_data 
    WHERE site2_data.date_time BETWEEN @DateFrom AND @DateTo 
) 
SELECT * from site1 site1_res 
INNER JOIN (select id, date_time, data_val) site2_res 
on ABS(DATEDIFF("SECOND", site1_res.date_time, site_2_res.date_time)) < 30 
END 

의도 먼저 원하는 날짜/시간 범위의 레코드를 선택한 다음 내에있는 사이트 2 사람들에게 사이트 개발에서 레코드에 가입하는 것입니다 +/- 30 초. 범위. 결과 레코드 세트에는 두 장치의 데이터가 포함되며 해당 레코드가없는 경우에는 null이 포함됩니다.

이것은 작동하는 것 같습니다. 원하는 양식의 레코드가 출력되고 각 테이블의 올바른 레코드에 해당합니다. 그러나 처형은 매우 느립니다. 날짜 범위에 대한 몇 주간의 쿼리에는 약 1 분 30 초가 걸립니다. Site1에는이 날짜 범위에 약 5000 개의 레코드가 들어 있지만 Site2에는 레코드가 하나만 들어 있습니다. 각 테이블에 대해서만 날짜 범위에 대한 SELECT 쿼리가 1 초 이내에 실행됩니다.

필자는 이전에 SQL에 대해 깊이 파고 들지 않았지만이 작은 그룹에는이 시점에서 아무도이 작업을 수행하지 않았습니다. 누구든지이 일을하기위한 적절한 방법이나 적어도이 SP를 가속화하는 방법에 대한 아이디어를 나에게 줄 수 있습니까?

+0

당신이 테이블에 DATE_TIME의 -fields를 편집 할 수 있습니다 그것이 작동하는 방법이 몇 번

실행 확인합니다 필드를이 목적 (datetime 또는 int) 대신 거기에 datediff를 계산하는 대신에? –

+1

쿼리의 속도를 높이려면 항상 인덱스로 전환하여 현재 작업중인 인덱스 만 만들 수 있는지 확인하십시오. 테이블을 교차하는 인덱스를 만들 수도 있습니다. –

+2

하위 쿼리에 쿼리에 테이블이 없습니다. 그런 다음 함수에 컬럼이 있으 G로 nonSARGable 술어가 있습니다. 마지막으로 삼각형 결합이 있습니다. http://www.sqlservercentral.com/articles/T-SQL/61539/ –

답변

0

다른 곳에서 this article을 찾았습니다. 매우 도움이되었습니다. 결과적으로 SP를 다음과 같이 변경했습니다.

ALTER PROCEDURE [dbo].[SPJoinDateRange] 
@DateFrom DateTime = '2014-05-01 15:10:24.000', 
@DateTo DateTime = '2014-07-31 15:10:00.000' 
AS 
BEGIN 
SET NOCOUNT ON; 
WITH site1(id, date_time, data_val1) 
AS 
(
    SELECT * 
    FROM site1_data 
    WHERE site1_data.date_time BETWEEN @DateFrom AND @DateTo 
) 
SELECT * FROM site1 
CROSS APPLY 
(
    SELECT id as id1, date_time as date_time1, data_val2 
    FROM site2_data AS site2 
    WHERE site2.date_time BETWEEN @DateFrom AND @DateTo 
    AND 
    ABS(DATEDIFF("SECOND", site1.date_time, site2.date_time)) < 30 
) 
AS result 
END 

이 쿼리의 결과 시간은 6 초입니다. (이전 버전의 경우 90 초와 비교). 이는 여전히 가능한 것보다 훨씬 느릴 수 있습니다. 내 다음 과제는이 접근법이 왜 더 빠른지 이해하는 것이 이상적입니다. Sean Lange의 간결한 답변 (및 링크)은 의심 할 여지없이 단서를 제공합니다. 물론 우리는 그것을 지연하고 우리의 초기 구현에서 다음 작업을 수행해야 할 것입니다.

제 질문에 너무나 빠르게 반응 한 모든 분들께 감사드립니다.

+1

성능과 같은 사운드가 크게 개선되었습니다. 축하해! 나는 그것이 6 초보다 빠르게 될 수 있다고 의심하지만 테이블 구조와 인덱스를 모른 채 최선의 것이 무엇인지 말하기는 어렵습니다. 한 가지 제안은 site1을 사용하려는 경우 *를 사용하는 대신 select에서 열의 이름을 명시 적으로 지정해야한다는 것입니다. 테이블 구조가 변경되면 쿼리가 손상됩니다. –

1

date_time 열의 색인을보다 잘 활용하여 솔루션을 개선 할 수 있습니다.

ABS(S1 - S2) < 30 

은 상처를 안하지만 당신은 정말, 첫 번째 CTE가 필요하지 않습니다

ABS(S2 - S1) < 30 

<=> 

-30 < S2 - S1 < 30 

<=> 

S2 - S1 < 30 
AND 
S2 - S1 > -30 

<=> 

S2 < S1 + 30 
AND 
S2 > S1 - 30 

에 해당합니다. 그러나 CROSS APPLY 안에있는 WHERE 절은 이와 같이 작성하는 것이 좋습니다. 또한 site2의 해당 데이터가없는 site1의 데이터를 보려면 CROSS APPLY 대신 OUTER APPLY을 사용해야합니다. 이제 site2.date_time은 함수 호출 내부에 없으며 옵티마이 저는이 열에서 인덱스를 사용할 수 있습니다. 당신이 가장 가까운 30 초 반올림하여 타임 스탬프를 포함 할 여분의 열을 추가 할 수있는 경우

ALTER PROCEDURE [dbo].[SPJoinDateRange] 
    @DateFrom DateTime = '2014-05-01 15:10:24.000', 
    @DateTo DateTime = '2014-07-31 15:10:00.000' 
AS 
BEGIN 
    SET NOCOUNT ON; 

    SELECT 
     site1_data.id AS id1 
     ,site1_data.date_time AS date_time1 
     ,site1_data.data_val1 
     ,CA_site2.id2 
     ,CA_site2.date_time2 
     ,CA_site2.data_val2 
    FROM 
     site1_data 
     OUTER APPLY 
     (
      SELECT 
       site2_data.id as id2 
       ,site2_data.date_time as date_time2 
       ,site2_data.data_val2 
      FROM 
       site2_data 
      WHERE 
       site2.date_time BETWEEN @DateFrom AND @DateTo 
       AND site2.date_time < DATEADD(second, +30, site1_data.date_time) 
       AND site2.date_time > DATEADD(second, -30, site1_data.date_time) 
     ) AS CA_site2 
    WHERE 
     site1_data.date_time BETWEEN @DateFrom AND @DateTo 
    ; 

END 

이 더 빠르게 작동합니다.정확한 시간 소인이 필요하지 않으면 기존 값을 그대로 두십시오. 우리가 인덱스를 생성, 30 초 반올림 원래의 타임 스탬프를 포함 date_time_rounded라는 열을 추가하는 경우

, 다음 쿼리는 다음과 같이 표시됩니다

ALTER PROCEDURE [dbo].[SPJoinDateRange] 
    @DateFrom DateTime = '2014-05-01 15:10:24.000', 
    @DateTo DateTime = '2014-07-31 15:10:00.000' 
AS 
BEGIN 
    SET NOCOUNT ON; 

    SELECT 
     site1_data.id AS id1 
     ,site1_data.date_time AS date_time1 
     ,site1_data.data_val1 
     ,site2_data.id AS id2 
     ,site2_data.date_time AS date_time2 
     ,site2_data.data_val2 
    FROM 
     site1_data 
     LEFT JOIN site2_data ON site2_data.date_time_rounded = site1_data.date_time_rounded 
    WHERE 
     site1_data.date_time BETWEEN @DateFrom AND @DateTo 
    ; 

END 

date_time에 가장 가까운 30초 당신을 반올림하기 이 같은 것을 사용할 수 있습니다

DATEADD(second, 30 * ROUND(DATEDIFF(second, '20010101', date_time)/30.0, 0), '20010101') 

그것은는 date_time 주어진에 2001-01-01에서 초 수를 계산 (30)을 분할은, 정수로 결과를 반올림 (30)에 의해 결과를 곱을,이 번호를 추가합니다 second to 2001-01-01. 00 또는 : 30 또는 새를 추가 그래서 항상 것이라고에서 데이터를로드 할 때

SELECT 
GETDATE() as original, 
DATEADD(second, 30 * ROUND(DATEDIFF(second, '20010101', GETDATE())/30.0, 0), '20010101') AS rounded 
관련 문제