2012-11-30 20 views
5

T-SQL, एसक्यूएल सर्वर 2008 और ऊपर15 में समूह सारणी मिनिट के अंतराल

की

StatusSetDateTime एक नमूना तालिका को देखते हुए | उपयोगकर्ता आईडी | स्थिति | StatusEndDateTime | StatusDuration (सेकंड में) =========================================== ================================= 2012-01-01 12:00:00 | आईआईडी | उपलब्ध | 2012-01-01 13:00:00 | 3600

मुझे लगता है कि नीचे तोड़ने के लिए एक दृश्य उदाहरण के लिए 15 मिनट के अंतराल का उपयोग करता है में की जरूरत है:

IntervalStart | उपयोगकर्ता आईडी | स्थिति | अवधि

===========================================

2012-01-01 12:00:00 | आईआईडी | उपलब्ध | 900

2012-01-01 12:15:00 | आईआईडी | उपलब्ध | 900

2012-01-01 12:30:00 | आईआईडी | उपलब्ध | 900

2012-01-01 12:45:00 | आईआईडी | उपलब्ध | 900

2012-01-01 13:00:00 | आईआईडी | उपलब्ध | 0

आदि ....

अब मैं चारों ओर खोज और आप कुछ पूछना चाहते है कि नीचे टूट जाएगा मैं MySql Here के लिए कुछ इसी तरह पाया ढूँढने में सक्षम किया गया है: टी के लिए कुछ

और -एसक्यूएल Here

लेकिन दूसरे उदाहरण पर वे परिणाम एकत्र कर रहे हैं जबकि मुझे स्थिति द्वारा उपयोगकर्ता द्वारा अंतराल समय (900 सेकंड) द्वारा कुल अवधि को विभाजित करने की आवश्यकता है।

मैं सभी लिंक को अंतराल में विभाजित करने के लिए दूसरे लिंक में उदाहरणों को अनुकूलित करने में सक्षम था लेकिन कुल अवधि का समय वापस कर दिया गया है और मैं समझ नहीं पा रहा हूं कि अंतराल अवधि को विभाजित करने के लिए कैसे प्राप्त करें (और अभी भी कुल मूल तक अवधि)।

किसी भी जानकारी के लिए अग्रिम धन्यवाद!

संपादित करें: सबसे पहले प्रयास

;with cte as 
    (select MIN(StatusDateTime) as MinDate 
      , MAX(StatusDateTime) as MaxDate 
      , convert(varchar(14),StatusDateTime, 120) as StartDate 
      , DATEPART(minute, StatusDateTime) /15 as GroupID 
      , UserID 
      , StatusKey 
      , avg(StateDuration) as AvgAmount 
    from AgentActivityLog 
    group by convert(varchar(14),StatusDateTime, 120) 
     , DATEPART(minute, StatusDateTime) /15 
     , Userid,StatusKey) 

    select dateadd(minute, 15*GroupID, CONVERT(datetime,StartDate+'00')) 
     as [Start Date] 
     , UserID, StatusKey, AvgAmount as [Average Amount] 
    from cte 

संपादित करें: दूसरा प्रयास

;With cte As 
    (Select DateAdd(minute 
        , 15 * (DateDiff(minute, '20000101', StatusDateTime)/15) 
        , '20000101') As StatusDateTime 
     , userid, statuskey, StateDuration 
    From AgentActivityLog) 

Select StatusDateTime, userid,statuskey,Avg(StateDuration) 
From cte 
Group By StatusDateTime,userid,statuskey; 
+0

आपके उदाहरण में, आपने 5 पंक्तियों और 5 * 900 में विभाजित किया है! = 3600 या क्या मुझे कुछ याद आ रही है? –

+1

क्या आपका पहला अंतराल हमेशा स्टेटससेटडेट टाइम के समय से शुरू होता है, या यह हमेशा 00 00: 15, 30, 45 पर आधारित है? अपने उदाहरण का प्रयोग करते हुए, यदि StatusSetDateTime 12:06:30 के रूप में आया, तो क्या आपका पहला अंतराल 12; 00: 00 या 12:06:30 पर शुरू होगा? साथ ही, क्या आप एसक्यूएल को विभाजित करने के लिए अब तक पोस्ट कर सकते हैं? –

+0

@ फ़ारुकसाइन क्षमा करें, मुझे अंतिम अंतराल में 0 को पकड़ने के लिए धन्यवाद देना था। अंतराल हमेशा 00, 15, 30, 45 – Wjdavis5

उत्तर

3
;with cte_max as 
(
    select dateadd(mi, -15, max(StatusEndDateTime)) as EndTime, min(StatusSetDateTime) as StartTime 
    from AgentActivityLog 
), times as 
(
    select StartTime as Time from cte_max 
    union all 
    select dateadd(mi, 15, c.Time) 
    from times as c 
     cross join cte_max as cm 
    where c.Time <= cm.EndTime 
) 
select 
    t.Time, A.UserID, A.Status, 
    case 
     when t.Time = A.StatusEndDateTime then 0 
     else A.StatusDuration/(count(*) over (partition by A.StatusSetDateTime, A.UserID, A.Status) - 1) 
    end as Duration 
from AgentActivityLog as A 
    left outer join times as t on t.Time >= A.StatusSetDateTime and t.Time <= A.StatusEndDateTime 

sql fiddle demo

+0

सहायता के लिए धन्यवाद - एसएसएमएस में इसे चलाने पर मुझे अधिकतम प्रतिक्रिया प्राप्त होती है, ऐसा लगता है कि SQL Fiddle में ठीक काम करता है। केवल समस्या यह है कि myID2 के लिए - अनुपलब्ध, आपके पास 320 की अवधि है, भले ही वे पूरे 15 मिनट के लिए उस राज्य में हों, जो 900 – Wjdavis5

+0

होनी चाहिए, ठीक है, ऐसा इसलिए है क्योंकि मैंने एक राउंड अवधि जोड़ा है - 1600 यह 4500 होना चाहिए। ऐसा लगता है कि अवधि क्षेत्र बिल्कुल अनावश्यक है, आप 'एस्टैटस अवधि/(गिनती (*) के बजाय 900 लिख सकते हैं (एस्टैटससेटडेट टाइम, एयूसरिड, एस्टैटस द्वारा विभाजन) - 1) 'और अपनी तालिका से अवधि हटाएं :) http://sqlfiddle.com/#!3/8c921/1 –

+0

@ Wjdavis5 मेरे पास भी आपके लिए सुपर सरल समाधान है यदि प्रारंभ और समाप्ति तिथि के बीच अंतर हमेशा 1 घंटा –

3

मैं कभी नहीं की तारीख गणित का उपयोग कर विभाजन में चीजों को विभाजित करने के साथ सहज गया है। ऐसा लगता है कि गिरने के लिए सभी तरह के नुकसान हैं।

मुझे एक टेबल (पूर्व-परिभाषित, तालिका-मूल्यवान फ़ंक्शन, तालिका चर) बनाने के लिए क्या करना पसंद है, जहां प्रत्येक दिनांक विभाजन सीमा के लिए एक पंक्ति है।तालिका-मूल्यवान फ़ंक्शन दृष्टिकोण विशेष रूप से उपयोगी होता है क्योंकि आप इसे आवश्यक तरीके से मनमानी श्रेणियों और विभाजन आकारों के लिए बना सकते हैं। फिर, चीजों को विभाजित करने के लिए आप इस तालिका में शामिल हो सकते हैं।

paritionid starttime  endtime 
---------- ------------- ------------- 
1   8/1/2012 5:00 8/1/2012 5:15 
2   8/1/2012 5:15 8/1/2012 5:30 
... 

मैं इस विधि के प्रदर्शन से बात नहीं कर सकता, लेकिन मुझे लगता है कि प्रश्न अधिक सहज हैं।

+0

पीएस आपको "गिरने में परेशानी" कहने की ज़रूरत नहीं है। "pitfalls" है अपने आप से पर्याप्त! एक और गड़बड़ी के साथ क्या करता है, लेकिन, इसमें गिरावट आती है? :) मेरी दादी "क्या आप जा रहे हैं?" के बजाय "क्या आप जा रहे हैं?" कहती थी, जो एक समान बात है। – ErikE

+0

जब भी कोई मेरा कार्य ड्राफ्ट संपादित करता है, तो उन्होंने शब्दों का 5% से 10% काट दिया। अगर मैं पिछले जीवन में विश्वास करता था es, मुझे विश्वास होगा कि पिछले जन्म में, मुझे शब्द द्वारा भुगतान किया गया था। सत्य। – quillbreaker

+0

मुझे उम्मीद है कि आप समझ गए हैं कि मैं भाषा की चीज़ का आनंद ले रहा था, मूल रूप से आलोचना करने की कोशिश नहीं कर रहा था। :) – ErikE

0

आप recursive Common Table Expression का उपयोग कर सकते हैं, जहां आप अपनी अवधि जोड़ते रहते हैं जबकि StatusEndDateTime इंटरवलस्टार्ट से अधिक है उदा।

;with cte as (
    select StatusSetDateTime as IntervalStart 
     ,UserID 
     ,Status 
     ,StatusDuration/(datediff(mi, StatusSetDateTime, StatusEndDateTime)/15) as Duration 
     , StatusEndDateTime 
    From AgentActivityLog 
    Union all 
    Select DATEADD(ss, Duration, IntervalStart) as IntervalStart 
     , UserID 
     , Status 
     , case when DATEADD(ss, Duration, IntervalStart) = StatusEndDateTime then 0 else Duration end as Duration 
     , StatusEndDateTime 
    From cte 
    Where IntervalStart < StatusEndDateTime 
) 

select IntervalStart, UserID, Status, Duration from cte 
1

यह अपेक्षाकृत सरल है, तो आप हर 15 मिनट की टाइमस्टैम्प है, जो आप के बीच के माध्यम से अपने आधार तालिका में शामिल हों के साथ एक सहायक तालिका है। आप फ्लाई पर सहायक टेबल बना सकते हैं या इसे अपने डेटाबेस में स्थायी रूप से रख सकते हैं। आपकी कंपनी के अगले व्यक्ति के लिए भी यह पता लगाने के लिए सरल:

// declare a table and a timestamp variable 
declare @timetbl table(t datetime) 
declare @t datetime 

// set the first timestamp 
set @t = '2012-01-01 00:00:00' 

// set the last timestamp, can easily be extended to cover many years 
while @t <= '2013-01-01' 
begin 
    // populate the table with a new row, every 15 minutes 
    insert into @timetbl values (@t) 
    set @t = dateadd(mi, 15, @t) 
end 


// now the Select query: 
select 
    tt.t, aal.UserID, aal.Status, 
    case when aal.StatusEndDateTime <= tt.t then 0 else 900 end as Duration 
    // using a shortcut for Duration, based on your comment that Start/End are always on the quarter-hour, and thus always 900 seconds or zero 

from 
    @timetbl tt 
     INNER JOIN AgentActivityLog aal 
     on tt.t between aal.StatusSetDateTime and aal.StatusEndDateTime 

order by 
    aal.UserID, tt.t 
0

यहां एक प्रश्न है जो आपके लिए सहायक टेबल की आवश्यकता के बिना काम करेगा। (मेरे पास सहायक टेबल के खिलाफ कुछ भी नहीं है, वे उपयोगी हैं और मैं उनका उपयोग करता हूं। कभी-कभी उनका उपयोग नहीं करना भी संभव है।) यह क्वेरी गतिविधियों को किसी भी समय शुरू करने और समाप्त करने की अनुमति देती है, भले ही पूरे मिनटों में समाप्त न हो: 00, : 15, 30, 45। यदि मिलीसेकंद भाग होंगे तो आपको कुछ प्रयोग करना होगा क्योंकि, आपके मॉडल के बाद, मैं केवल दूसरे संकल्प में गया था।

यदि आपके पास हार्ड हार्ड अवधि ज्ञात है, तो @MaxDuration को हटा दें और मिनटों में उस मान के साथ इसे प्रतिस्थापित करें। अच्छी तरह से प्रदर्शन करने के लिए N <= @MaxDuration महत्वपूर्ण है। 15: 00,:

CREATE TABLE #AgentActivityLog (
    StatusSetDateTime datetime, 
    StatusEndDateTime datetime, 
    StatusDuration AS (DateDiff(second, 0, StatusEndDateTime - StatusSetDateTime)) 
); 

INSERT #AgentActivityLog -- weird end times 
SELECT '20120101 12:00:00', '20120101 13:00:00' 
UNION ALL SELECT '20120101 13:00:00', '20120101 13:27:56' 
UNION ALL SELECT '20120101 13:27:56', '20120101 13:28:52' 
UNION ALL SELECT '20120101 13:28:52', '20120120 11:00:00' 

INSERT #AgentActivityLog -- 15-minute quantized end times 
SELECT '20120101 12:00:00', '20120101 13:00:00' 
UNION ALL SELECT '20120101 13:00:00', '20120101 13:30:00' 
UNION ALL SELECT '20120101 13:30:00', '20120101 14:00:00' 
UNION ALL SELECT '20120101 14:00:00', '20120120 11:00:00' 

इसके अलावा, यहाँ एक संस्करण है कि केवल उम्मीद के दिनों में समाप्त होने वाले पूरे मिनट है कि है:

DECLARE @MaxDuration int; 
SET @MaxDuration = (SELECT Max(StatusDuration)/60 FROM #AgentActivityLog); 

WITH 
L0 AS(SELECT 1 c UNION ALL SELECT 1), 
L1 AS(SELECT 1 c FROM L0, L0 B), 
L2 AS(SELECT 1 c FROM L1, L1 B), 
L3 AS(SELECT 1 c FROM L2, L2 B), 
L4 AS(SELECT 1 c FROM L3, L3 B), 
L5 AS(SELECT 1 c FROM L4, L4 B), 
Nums AS(SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 0)) n FROM L5) 
SELECT 
    S.IntervalStart, 
    Duration = DateDiff(second, S.IntervalStart, E.IntervalEnd) 
FROM 
    #AgentActivityLog L 
    CROSS APPLY (
     SELECT N, Offset = (N.N - 1) * 900 
     FROM Nums N 
     WHERE N <= @MaxDuration 
    ) N 
    CROSS APPLY (
     SELECT Edge = 
     DateAdd(second, N.Offset, DateAdd(minute, 
      DateDiff(minute, '20000101', L.StatusSetDateTime) 
      /15 * 15, '20000101') 
     ) 
    ) G 
    CROSS APPLY (
     SELECT IntervalStart = Max(T.BeginTime) 
     FROM (
     SELECT L.StatusSetDateTime 
     UNION ALL SELECT G.Edge 
    ) T (BeginTime) 
    ) S 
    CROSS APPLY (
     SELECT IntervalEnd = Min(T.EndTime) 
     FROM (
     SELECT L.StatusEndDateTime 
     UNION ALL SELECT G.Edge + '00:15:00' 
    ) T (EndTime) 
    ) E 
WHERE 
    N.Offset <= L.StatusDuration 
ORDER BY 
    L.StatusSetDateTime, 
    S.IntervalStart; 

यहाँ यदि आप इसे की कोशिश करना चाहते सेटअप स्क्रिप्ट है 30, या : 45।

DECLARE @MaxDuration int; 
SET @MaxDuration = (SELECT Max(StatusDuration)/60 FROM #AgentActivityLog); 

WITH 
L0 AS(SELECT 1 c UNION ALL SELECT 1), 
L1 AS(SELECT 1 c FROM L0, L0 B), 
L2 AS(SELECT 1 c FROM L1, L1 B), 
L3 AS(SELECT 1 c FROM L2, L2 B), 
L4 AS(SELECT 1 c FROM L3, L3 B), 
L5 AS(SELECT 1 c FROM L4, L4 B), 
Nums AS(SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 0)) n FROM L5) 
SELECT 
    S.IntervalStart, 
    Duration = CASE WHEN Offset = StatusDuration THEN 0 ELSE 900 END 
FROM 
    #AgentActivityLog L 
    CROSS APPLY (
     SELECT N, Offset = (N.N - 1) * 900 
     FROM Nums N 
     WHERE N <= @MaxDuration 
    ) N 
    CROSS APPLY (
     SELECT IntervalStart = DateAdd(second, N.Offset, L.StatusSetDateTime) 
    ) S 
WHERE 
    N.Offset <= L.StatusDuration 
ORDER BY 
    L.StatusSetDateTime, 
    S.IntervalStart; 

यह वास्तव में अंतिम 0 अवधि पंक्ति होने की तरह लगता है क्योंकि तब आप कर सकते हैं बस नहीं क्रम IntervalStart द्वारा के रूप में वहाँ डुप्लिकेट IntervalStart मान होते हैं, सही नहीं है। कुल पंक्तियों को जोड़ने वाली पंक्तियों का लाभ क्या है?