2009-08-17 5 views
3

SQL Server 데이터베이스에 이벤트 로그가 있습니다. 본질적으로 그것은 전화가 걸려 왔을 때와 그 콜이 콜센터에서 (두 개의 다른 레코드로) 끝났을 때뿐만 아니라 몇 가지 다른 세부 사항을 기록합니다. 나는이 데이터로 얼마나 많은 전화선이 주어진 시간에 사용되는지에 대한 아이디어를 얻으려고 노력하고있다. 비록 SQL 쿼리가 나를 위해 이것을 결정할 수있는 좋은 방법이 없다고 생각합니다. (많은 속도를 희생시키지 않으면 이상적입니다.)SQL의 시간별 이벤트 로그 분석

제 생각에는 각 통화에 대한 시작 및 종료 이벤트를 프로그램에 쿼리하여 통화 시간을 결정했습니다. 그런 다음 각 시간 단위를 단계별로 실행하여 주어진 시간에 얼마나 많은 전화가 진행되었는지 집계 할 수 있습니다. SQL에서 선형 메서드를 C#이나 비슷한 것으로 사용하는 대신이 작업을 수행 할 수있는 방법이 있습니까?

편집 : 통화의 고유 ID가 있습니다. 원하면 세션 ID. 또한 시작 및 끝 이벤트는 서로 다른 두 개의 레코드 - 단일 레코드가 아닙니다. 이것은 내가 생각하기에 이것을 복잡하게 만든다. 또한이 테이블에는 1,500 만 개가 넘는 레코드가 있습니다.

Id EvId    CallId       DateTime  
-- ---- ------------------------------------ -------------------- 
1 0 df1cbc93-5cf3-402a-940b-4441f6a7ec5c  7/9/2008 8:12:56 PM 
2 1 df1cbc93-5cf3-402a-940b-4441f6a7ec5c  7/9/2008 8:13:07 PM 
3 0 ec1c2078-1765-4377-9126-6f26fe33e4a9 7/10/2008 4:33:10 PM 
4 10 ec1c2078-1765-4377-9126-6f26fe33e4a9 7/10/2008 4:33:13 PM 
5 1 ec1c2078-1765-4377-9126-6f26fe33e4a9 7/10/2008 4:33:13 PM 
6 0 a3c3b9a0-a23b-4dda-b4e4-e82f0209c94d 7/10/2008 4:33:13 PM 
7 10 a3c3b9a0-a23b-4dda-b4e4-e82f0209c94d 7/10/2008 4:33:15 PM 
8 1 a3c3b9a0-a23b-4dda-b4e4-e82f0209c94d 7/10/2008 4:33:15 PM 
9 0 d23f393d-0272-445a-8670-3f71b016174e 7/10/2008 4:33:15 PM 
10 10 d23f393d-0272-445a-8670-3f71b016174e 7/10/2008 4:33:17 PM 
11 1 d23f393d-0272-445a-8670-3f71b016174e 7/10/2008 4:33:17 PM 


EvId Description 
---- ---------------- 
    0 New Call 
    1 End of Call 
    2 Caller Hangup 
10 CPA Completed 
+0

각 통화마다 고유 한 식별자가 있습니까? –

+0

다음을보십시오 .http : //stackoverflow.com/questions/781895/checking-for-time-range-overlap-the-watchman-problem-sql 이것은 오버랩을 계산하는 방법을 보여줍니다. – pjp

+0

"오버랩" 일부는 동일한 시작/종료 지점을가집니다. 어쨌든, 샘플 코드에 대한 최신 편집을 참조하십시오 ... –

답변

1

, 당신은 전용 데이터베이스 당이 한 번해야하는 "도우미"테이블을 설정해야합니다 단일 열에 1에서 8000까지의 값이 포함되어 있습니다. CTE를 사용하여 동일하게 수행 할 수 있지만 SQL Server 버전을 말하는 것은 아니므로이 기능은 모두 작동하며이 기능을 여러 번 실행하면 더 좋습니다.

이 시도 :

DECLARE @Calls table (rowID int not null primary key identity(1,1) 
         ,EvId int not null 
         ,CallId varchar(36) 
         ,rowDateTime datetime 
        ) 
SET NOCOUNT ON 
INSERT INTO @Calls VALUES (0,'df1cbc93-5cf3-402a-940b-4441f6a7ec5c',' 7/9/2008 8:12:56 PM') 
INSERT INTO @Calls VALUES (1,'df1cbc93-5cf3-402a-940b-4441f6a7ec5c',' 7/9/2008 8:13:07 PM') 
INSERT INTO @Calls VALUES (0,'ec1c2078-1765-4377-9126-6f26fe33e4a9','7/10/2008 4:33:10 PM') 
INSERT INTO @Calls VALUES (10,'ec1c2078-1765-4377-9126-6f26fe33e4a9','7/10/2008 4:33:13 PM') 
INSERT INTO @Calls VALUES (1,'ec1c2078-1765-4377-9126-6f26fe33e4a9','7/10/2008 4:33:13 PM') 
INSERT INTO @Calls VALUES (0,'a3c3b9a0-a23b-4dda-b4e4-e82f0209c94d','7/10/2008 4:33:13 PM') 
INSERT INTO @Calls VALUES (10,'a3c3b9a0-a23b-4dda-b4e4-e82f0209c94d','7/10/2008 4:33:15 PM') 
INSERT INTO @Calls VALUES (1,'a3c3b9a0-a23b-4dda-b4e4-e82f0209c94d','7/10/2008 4:33:15 PM') 
INSERT INTO @Calls VALUES (0,'d23f393d-0272-445a-8670-3f71b016174e','7/10/2008 4:33:15 PM') 
INSERT INTO @Calls VALUES (10,'d23f393d-0272-445a-8670-3f71b016174e','7/10/2008 4:33:17 PM') 
INSERT INTO @Calls VALUES (1,'d23f393d-0272-445a-8670-3f71b016174e','7/10/2008 4:33:17 PM') 
--I added more test data, to hit more cases 
INSERT INTO @Calls VALUES (0,'111111111111111111111111111111111111','7/10/2008 4:10:00 PM') 
INSERT INTO @Calls VALUES (10,'111111111111111111111111111111111111','7/10/2008 4:11:00 PM') 
INSERT INTO @Calls VALUES (1,'111111111111111111111111111111111111','7/10/2008 4:11:00 PM') 
INSERT INTO @Calls VALUES (0,'222222222222222222222222222222222222','7/10/2008 4:15:00 PM') 
INSERT INTO @Calls VALUES (10,'222222222222222222222222222222222222','7/10/2008 4:16:00 PM') 
INSERT INTO @Calls VALUES (1,'222222222222222222222222222222222222','7/10/2008 4:16:00 PM') 
INSERT INTO @Calls VALUES (0,'333333333333333333333333333333333333','7/10/2008 4:09:00 PM') 
INSERT INTO @Calls VALUES (10,'333333333333333333333333333333333333','7/10/2008 4:18:00 PM') 
INSERT INTO @Calls VALUES (1,'333333333333333333333333333333333333','7/10/2008 4:18:00 PM') 
INSERT INTO @Calls VALUES (0,'444444444444444444444444444444444444','7/10/2008 4:13:00 PM') 
INSERT INTO @Calls VALUES (10,'444444444444444444444444444444444444','7/10/2008 4:14:00 PM') 
INSERT INTO @Calls VALUES (1,'444444444444444444444444444444444444','7/10/2008 4:14:00 PM') 
INSERT INTO @Calls VALUES (0,'555555555555555555555555555555555555','7/10/2008 4:13:00 PM') 
SET NOCOUNT OFF 

DECLARE @StartRange datetime 
DECLARE @EndRange datetime 

SET @StartRange='7/10/2008 4:12:00 PM' 
SET @EndRange ='7/10/2008 4:15:00 PM' 

SET @EndRange=DATEADD(mi,1,@EndRange) 

--this lists the match time and each calls details in progress at that time 
SELECT 
    DATEADD(mi,n.Number-1,c.StartTime) AS 'TimeOfMatch' 
     ,c.CallID 
     ,c.StartTime,c.EndTime 
    FROM (SELECT --this derived table joins together the start and end dates into a single row, filtering out rows more than 90 minutes before the start range (if calls are longer than 90 minutes, increase this) and filters out any rows after the end date (will consider call done at end date then) 
       CallID, MIN(rowDateTime) AS StartTime, CASE WHEN MAX(rowDateTime)=MIN(rowDateTime) THEN @EndRange ELSE MAX(rowDateTime) END AS EndTime 
       FROM @Calls 
       WHERE rowDateTime>=DATEADD(mi,-90,@StartRange) --AND rowDateTime<[email protected] 
       GROUP BY CallID 
     ) c 
     INNER JOIN Numbers n ON DATEDIFF(mi,c.StartTime,c.EndTime)+1>=n.Number 
    WHERE DATEADD(mi,n.Number-1,c.StartTime)>[email protected] AND DATEADD(mi,n.Number-1,c.StartTime)<@EndRange 
    ORDER BY 1 

--this lists just the match time and the call count 
SELECT 
    DATEADD(mi,n.Number-1,c.StartTime) AS 'TimeOfMatch' 
     ,c.CallID 
     ,c.StartTime,c.EndTime 
    FROM (SELECT --this derived table joins together the start and end dates into a single row, filtering out rows more than 90 minutes before the start range (if calls are longer than 90 minutes, increase this) and filters out any rows after the end date (will consider call done at end date then) 
       CallID, MIN(rowDateTime) AS StartTime, CASE WHEN MAX(rowDateTime)=MIN(rowDateTime) THEN @EndRange ELSE MAX(rowDateTime) END AS EndTime 
       FROM @Calls 
       WHERE rowDateTime>=DATEADD(mi,-90,@StartRange) --AND rowDateTime<[email protected] 
       GROUP BY CallID 
     ) c 
     INNER JOIN Numbers n ON DATEDIFF(mi,c.StartTime,c.EndTime)+1>=n.Number 
    WHERE DATEADD(mi,n.Number-1,c.StartTime)>[email protected] AND DATEADD(mi,n.Number-1,c.StartTime)<@EndRange 
    ORDER BY 1 

여기 출력입니다 :

TimeOfMatch    CallID        StartTime    EndTime 
----------------------- ------------------------------------ ----------------------- ----------------------- 
2008-07-10 16:12:00.000 333333333333333333333333333333333333 2008-07-10 16:09:00.000 2008-07-10 16:18:00.000 
2008-07-10 16:13:00.000 333333333333333333333333333333333333 2008-07-10 16:09:00.000 2008-07-10 16:18:00.000 
2008-07-10 16:13:00.000 444444444444444444444444444444444444 2008-07-10 16:13:00.000 2008-07-10 16:14:00.000 
2008-07-10 16:13:00.000 555555555555555555555555555555555555 2008-07-10 16:13:00.000 2008-07-10 16:16:00.000 
2008-07-10 16:14:00.000 555555555555555555555555555555555555 2008-07-10 16:13:00.000 2008-07-10 16:16:00.000 
2008-07-10 16:14:00.000 444444444444444444444444444444444444 2008-07-10 16:13:00.000 2008-07-10 16:14:00.000 
2008-07-10 16:14:00.000 333333333333333333333333333333333333 2008-07-10 16:09:00.000 2008-07-10 16:18:00.000 
2008-07-10 16:15:00.000 333333333333333333333333333333333333 2008-07-10 16:09:00.000 2008-07-10 16:18:00.000 
2008-07-10 16:15:00.000 555555555555555555555555555555555555 2008-07-10 16:13:00.000 2008-07-10 16:16:00.000 
2008-07-10 16:15:00.000 222222222222222222222222222222222222 2008-07-10 16:15:00.000 2008-07-10 16:16:00.000 

(10 row(s) affected) 

TimeOfMatch    
----------------------- ----------- 
2008-07-10 16:12:00.000 1 
2008-07-10 16:13:00.000 3 
2008-07-10 16:14:00.000 3 
2008-07-10 16:15:00.000 3 

(4 row(s) affected) 

당신은 rowDateTime + CallId에 복합 인덱스가 필요합니다. 그러나 최상의 성능을 위해 단일 호출의 시작 날짜와 종료 날짜가 모두 포함 된 새 테이블 (startdate + CallId에 클러스터 된 인덱스)을 만든 경우 (시작 날짜가 EvId = 0 인 삽입을 사용하는 경우와 EvId = 1 업데이트 종료 날짜)이 새 테이블을 사용하여 파생 테이블을 제거 할 수 있습니다.

+0

이 필요한 것이거나 다른 것을 추구하고 있습니까? 그래서 알려주는 경우 ... –

+0

유일한 문제는이 카운트 (통화/시간)를 그래프로 표시하려고하는데 매초마다 카운트를 얻으려면 어떤 종류의 반복을해야한다는 것입니다. 그래프로 표시해야하는 시간 범위를 지정하고 싶습니다. (예 : 특정 날짜에 12:00에서 1:00 사이에 이루어지는 모든 호출) –

+0

유일한 문제는이 테이블에 15 백만 레코드가 있으므로 모든 최적화가 가능하다는 것입니다. 개선 –

1

여기에는 이벤트가 발생했을 때의 현재 통화 수와 함께 주어진 기간의 모든 이벤트 로그를 생성하는 쿼리가 있습니다. 몇 가지 CTE를 사용하여 시작 시간 이전에 시작된 통화 선택, 시작 시간 이전에 종료 된 통화 종료, 시작 및 종료 시간 사이의 통화 이벤트 추가 등 일련의 논리적 인 단계로 필요한 데이터를 작성합니다. 그런 다음이 결과 집합을 사용하여 이벤트의 타임 라인을 생성하고 모든 이벤트에서 현재 통화 수를 계산합니다. CTE는 파생 된 테이블보다 읽고 읽고 이해하기가 훨씬 쉽기 때문에 간단하게 사용됩니다.

declare @temp table (
    EvId int not null 
    , CallId uniqueidentifier not null 
    , DateTime Datetime not null); 

declare @starttime datetime 
    , @endtime datetime; 

select @starttime = '7/10/2008 1:33:14 PM'; 
select @endtime = '7/10/2008 1:43:14 PM'; 

-- These are all the calls 
-- that started before the start time 
with started_call as (
select * from call_log 
    where DateTime < @starttime 
    and EvId = 0) 
-- These are all the calls 
-- that ended before the start time 
, ended_call as (
select * from call_log 
    where DateTime < @starttime 
    and EvId = 1) 
-- These are all the call ids 
-- that were ongoing at the start time 
, existing_calls as (
select CallId from started_call 
except 
select CallId from ended_call) 
-- These are all the call events logged 
-- for calls that were were ongoing at the start time 
, existing_details as (
select l.* 
    from call_log l 
    join existing_calls e on e.CallId = l.CallId 
    where l.DateTime < @starttime) 
-- these are events that occured 
-- between start time and endtime 
, new_events as (
    select * from call_log 
    where DateTime between @starttime and @endtime) 
-- and these are all the events that are of interest 
, all_events as (
    select * from existing_details 
    union all 
    select * from new_events) 
-- put all the interesting events into a @temp table 
-- unfortunately QO cannot spool this for us 
-- so we better do it isntead 
insert into @temp (EvId, CallId, DateTime) 
    select EvId, CallId, DateTime from all_events; 

-- Extract events, along with the count 
-- at the time of the event 
select e.*,(
     select sum(case 
      when EvId = 0 then 1 -- Start call 
      when EvId = 1 then -1 -- end call 
      else 0 end) -- Other events 
     from @temp se 
     where se.DateTime < e.DateTime) as cnt 
from @temp e 
where DateTime between @starttime and @endtime 
order by DateTime; 

이 쿼리는 올바른 인덱스가있는 경우 전체 로그 테이블을 검사하지 않는 계획을 생성합니다. 간격 시작 시간에 기존 통화를 고려하여 모든 간격에 대해 올바른 결과를 제공합니다. 1 밀리 로그 기록에 대한 테스트에서 1.5 기가 바이트 RAM 단일 프로세서 랩톱에서 1.1 초 (10 초 간격으로 @temp 테이블을 생성하려면 628ms, 현재 수를 사용하여 타임 라인을 생성하려면 505ms) 이벤트를 지속적으로 생성했습니다. 시작 시간에 기존 호출을 검색 할 때 하단 끝에서 제한 될 수 있기 때문에 모든 호출의 최대 지속 시간에 대한 제한이 도입되면 큰 테이블의 성능을 향상시킬 수 있습니다 (DatTime> = 시작 시간 - 최대 통화 지속 시간) .

중간의 @temp 테이블 변수는 우아하지 않지만 효율적입니다. 여기

EvId CallId         DateTime    cnt 
1 401D9E00-040C-4B0E-8864-C66B72CF47AA 2008-07-10 13:33:16.000 23 
10 401D9E00-040C-4B0E-8864-C66B72CF47AA 2008-07-10 13:33:16.000 23 
1 8BF7AF50-B32C-464A-AF01-FDB653F0517D 2008-07-10 13:33:18.000 22 
10 8BF7AF50-B32C-464A-AF01-FDB653F0517D 2008-07-10 13:33:18.000 22 
0 CB523E24-5CE2-4E36-9D6C-4AE7BCEB1F53 2008-07-10 13:33:19.000 21 
1 4A54EEB6-A899-4167-9D5C-2CE1BC838FFB 2008-07-10 13:33:20.000 22 

제가 생성 및 테스트 데이터를 로딩하는 방법이다 : 여기

샘플 출력이다. 테이블의 클러스터형 인덱스와 비 클러스터형 인덱스는 모두 중요합니다.

CREATE TABLE Numbers 
(Number int NOT NULL, 
    CONSTRAINT PK_Numbers PRIMARY KEY CLUSTERED (Number ASC)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 
) ON [PRIMARY] 
DECLARE @x int 
SET @x=0 
WHILE @x<8000 
BEGIN 
    SET @[email protected]+1 
    INSERT INTO Numbers VALUES (@x) 
END 

이 기본적으로 포함 된 테이블을 생성합니다 : 당신이 내 쿼리 예제를 사용하기 전에

create table call_log (id int identity(1,1) not null 
    , EvId int not null 
    , CallId uniqueidentifier not null 
    , DateTime Datetime not null); 
create clustered index cdx_call_log on call_log(EvId, DateTime); 
create nonclustered index idx_call_log_call_id on call_log(CallId); 
go 

set nocount on; 
declare @i int, @date datetime, @callId uniqueidentifier; 
select @i = 0, @date = '7/10/2008 12:33:14 PM'; 
begin transaction 
while @i < 1000000 
begin 
    declare @duration int, 
     @delay int; 
    select @duration = rand()*180, 
     @delay = rand() * 10; 
    select @date = dateadd(second, @delay, @date) 
     , @callId = newid(); 

    insert into call_log (EvId, CallId, DateTime) 
    values (0, @callId, @date) 
     , (10, @callId, dateadd(second, @duration, @date)) 
     , (1, @callId, dateadd(second, @duration, @date)); 
    select @i = @i + 1; 
    if (0 = @i%100) 
    begin 
     commit; 
     begin tran; 
    end 
end 
commit 
go 
+0

나는 솔루션을 좋아합니다. 이제 내 질문이 좁혀졌습니다 : 단순히 데이터를 가져 와서 C#으로 버리고 선형 분석을 수행하는 더 나은 솔루션입니까? –

+1

보통 예. 데이터에 큰 크기가 있으면 C#으로 '잡아서 던지기'위해 드는 비용이 압도적입니다. –

+0

각 레코드에 시작 및 종료 시간이 없음을 추가해야합니다. 시작 이벤트는 단일 레코드이고 끝 이벤트는 다른 레코드입니다. –

0

이 시도 :

DECLARE @tblCalls TABLE(ActionEffect int, ActionTime datetime) 

INSERT INTO @tblCalls(ActionEffect, ActionTime) 
    SELECT 1, [DateTime] 
    FROM tblCallRecords 
    WHERE EviD = 0 

INSERT INTO @tblCalls(ActionEffect, ActionTime) 
    SELECT -1, [DateTime] 
    FROM tblCallRecords 
    WHERE EvID > 0 

(I 그 EVID의 다른 0이 아닌 통화의 끝을 나타 겠지?)

을 그리고, 어떤 주어진 순간에 호출 수를 얻기 위해, 당신은 do :

SELECT Sum(ActionEffect) 
FROM @tblCalls 
WHERE ActionTime < @GivenMoment 

1500 만 개의 레코드로는별로 좋지 않습니다. 이의 누계를 원한다면

이제, 당신은 아마 이런 식으로 뭔가를 할 필요 해요 : 빨리 큰 얻을

SELECT a.ActionTime, Sum(b.ActionEffect) AS OpenCalls 
FROM @tblCalls AS a 
LEFT JOIN @tblCalls AS b ON a.ActionTime > b.ActionTime 
GROUP BY a.ActionTime 

합니다. 한 번 실행하여 결과를 테이블에 저장하고 전화 녹음 메커니즘 코드를 수정하여 호출이 들어올 때 즉시 업데이트하도록하십시오.

0

이것은 해결책은 아니지만 몇 가지 아이디어를 던지고있는 것입니다. 그곳에. 이것을 테스트하지 않았으므로 쓰레기가 있으면 그들을 격추하십시오.

이 종류의 두 가지

1

) 통화가 일정 시간보다 더 오래 지속되지 않을 것이다 날짜 시간과에서 UniqueID

2)에 인덱스가 가정 (예를 들어 24 시간 48시간) 그렇지 않으면 무시할 수 있습니다.

그렇지 않으면 읽는 것을 멈출 수 있습니다.

예라면, (최대 통화 시간 48 시간 인 경우 이틀)이

Select CallId, 
    Min(DateTime) as StartOfCall , Max(DateTime) as EndofCall   
from Call_log 
where 
    (evid = 0 or evid=1) 
and DateTime between @ExtendedStartPeriod and @ExtendedEndPeriod 
ExtendedStartPeriod 및 ExtendedEndPeriod 전에 하루 실제 기간 이후 하루입니다

같은 쿼리 뭔가를 시작하는 경우

이것은 당신이 더 질의를 할 수 있도록이 (내 생각)해야이

Select UniqueID from (...) table1 
where StartOfCall <= @EndDate or EndOfCall >= @StartDate 

을 제거하는 일을 제외, 당신이 원하지 않는 일부 레코드를 줄 것이다 e 통화는 종료 기간이 지난 후에 시작하거나 시작 날짜 이전에 통화를 종료하는 것입니다.

다음으로 우리는이 당신에게 사건의 시간의 목록을 제공해야하고 다른 외부 쿼리를

Select DateTime, 
    CallChange = Case 
    When Evid = 0 then 1 
    When Evid = 1 then -1 
    else 0 
end 
from call_log 
where 
    unique_id in (...) 
    and (evid = 0 or evid=1) 
and DateTime between @ExtendedStartPeriod and @ExtendedEndPeriod 

을 수행가 증가 또는 호출 수를 감소 여부.

  7/9/2008 8:12:56 PM 1 
     7/9/2008 8:13:07 PM -1 
     7/10/2008 4:33:10 PM 1 
     7/10/2008 4:33:13 PM -1 
     7/10/2008 4:33:13 PM 1 
     7/10/2008 4:33:15 PM -1 
     7/10/2008 4:33:15 PM 1 
     7/10/2008 4:33:17 PM -1 

처럼 예에서 , 뭔가가 데이터를 SQL에서 반환의 크기를 줄일 수 분에 의해 그룹이에 도움이 될 수 있습니다 초당 통화의 매우 높은 볼륨이있는 경우.

그것은 그렇지 않으면 난에 C#을 조금해야 할 것이라고 생각, 심지어 수에 의해 내가 SQL로 갈 수있는만큼 관하여

Select 
    Count(CallChange) , 
    DatePart("yyyy", DateTime) , 
    DatePart("mm", DateTime), 
    DatePart("dd", DateTime), 
    DatePart("hh", DateTime), 
    DatePart("mi", DateTime) 
    DatePart("ss", DateTime) 
From 
    (...) 

    Group By 
    DatePart("yyyy", DateTime) , 
    DatePart("mm", DateTime), 
    DatePart("dd", DateTime), 
    DatePart("hh", DateTime), 
    DatePart("mi", DateTime) 
    DatePart("ss", DateTime) 

는, 어쩌면 사람이 더 걸릴 수 있습니다 추가 쿼리를 수행하는 수도 기간 당 거래의 실행 횟수를 유지합니다.