क्वेरी ऑप्टिमाइज़ेशन: समय अंतराल


10

मुख्य में, मुझे दो प्रकार के समय अंतराल मिले हैं:

presence time तथा absence time

absence time विभिन्न प्रकार के हो सकते हैं (जैसे विराम, अनुपस्थिति, विशेष दिन वगैरह) और समय अंतराल ओवरलैप और / या इंटरसेक्ट हो सकते हैं।

यह सुनिश्चित करने के लिए नहीं है, कि कच्चे डेटा में अंतराल के केवल प्रशंसनीय संयोजन मौजूद हैं, जैसे। अतिव्यापी उपस्थिति-अंतराल का कोई मतलब नहीं है, लेकिन मौजूद हो सकता है। मैंने अब कई तरीकों से परिणामी उपस्थिति-समय अंतराल की पहचान करने की कोशिश की है - मेरे लिए, सबसे आरामदायक एक follwing लगता है।

;with "timestamps"
as
(
    select
        "id" = row_number() over ( order by "empId", "timestamp", "opening", "type" )
        , "empId"
        , "timestamp"
        , "type"
        , "opening"
    from
    (
        select "empId", "timestamp", "type", case when "types" = 'starttime' then 1 else -1 end as "opening" from
        ( select "empId", "starttime", "endtime", 1 as "type" from "worktime" ) as data
        unpivot ( "timestamp" for "types" in ( "starttime", "endtime" ) ) as pvt
        union all
        select "empId", "timestamp", "type", case when "types" = 'starttime' then 1 else -1 end as "opening" from
        ( select "empId", "starttime", "endtime", 2 as "type" from "break" ) as data
        unpivot ( "timestamp" for "types" in ( "starttime", "endtime" ) ) as pvt
        union all
        select "empId", "timestamp", "type", case when "types" = 'starttime' then 1 else -1 end as "opening" from
        ( select "empId", "starttime", "endtime", 3 as "type" from "absence" ) as data
        unpivot ( "timestamp" for "types" in ( "starttime", "endtime" ) ) as pvt
    ) as data
)
select 
      T1."empId"
    , "starttime"   = T1."timestamp"
    , "endtime"     = T2."timestamp"
from 
    "timestamps" as T1
    left join "timestamps" as T2
        on T2."empId" = T1."empId"
        and T2."id" = T1."id" + 1
    left join "timestamps" as RS
        on RS."empId" = T2."empId"
        and RS."id" <= T1."id"      
group by
    T1."empId", T1."timestamp", T2."timestamp"
having
    (sum( power( 2, RS."type" ) * RS."opening" ) = 2)
order by 
    T1."empId", T1."timestamp";

कुछ डेमो डेटा के लिए SQL-Fiddle देखें ।

कच्चे डेटा के रूप में अलग-अलग तालिकाओं में मौजूद "starttime" - "endtime"या "starttime" - "duration"

यह विचार था कि उपस्थिति समय का अनुमान लगाने के लिए प्रत्येक टाइमस्टैम्प की खुली सूची में एक "बिटमास्क" रोलिंग अंतराल के साथ एक आदेशित सूची प्राप्त की जाए।

फ़िडल काम करता है और अनुमानित परिणाम देता है, भले ही विभिन्न अंतरालों की शुरुआत समान हो। इस उदाहरण में किसी भी सूचकांक का उपयोग नहीं किया गया है।

क्या यह सही कार्य को प्राप्त करने का सही तरीका है या इसके लिए और अधिक सुरुचिपूर्ण तरीका है?

यदि उत्तर देने के लिए प्रासंगिक है: डेटा की मात्रा प्रति टेबल प्रति कर्मचारी दस-हज़ार डेटासेट तक होगी। एग्रीगेट में पूर्ववर्ती इनलाइन की रोलिंग राशि की गणना करने के लिए sql-2012 उपलब्ध नहीं है।


संपादित करें:

बस टेस्टाटाटा (1000, 10.000, 100.000, 1 मिलियन) की बड़ी राशि के खिलाफ क्वेरी को निष्पादित किया और देख सकते हैं कि रनटाइम तेजी से बढ़ता है। जाहिर है एक चेतावनी झंडा, सही?

मैंने क्वेरी को बदल दिया और क्वर्की अपडेट द्वारा रोलिंग योग के एकत्रीकरण को हटा दिया।

मैंने एक सहायक तालिका जोड़ी है:

create table timestamps
(
  "id" int
  , "empId" int
  , "timestamp" datetime
  , "type" int
  , "opening" int
  , "rolSum" int
)

create nonclustered index "idx" on "timestamps" ( "rolSum" ) include ( "id", "empId", "timestamp" )

और मैंने इस स्थान पर रोलिंग योग की गणना की:

declare @rolSum int = 0
update "timestamps" set @rolSum = "rolSum" = @rolSum + power( 2, "type" ) * "opening" from "timestamps"

यहां SQL-Fiddle देखें

रनटाइम "वर्कटाइम" -योग्य में 1 मिलियन प्रविष्टियों के बारे में 3 सेकंड तक कम हो गया।

प्रश्न समान है : इसे हल करने का सबसे प्रभावी तरीका क्या है?


मुझे यकीन है कि इस पर विवाद होगा, लेकिन आप इसे सीटीई में नहीं करने की कोशिश कर सकते हैं । इसके बजाय अस्थायी तालिकाओं का उपयोग करें, और देखें कि क्या यह तेज है।
रोटेनजेक

बस एक स्टाइल सवाल: मैंने कभी किसी को अपने सभी कॉलम के नाम और टेबल के नाम डबल कोट्स में डालते नहीं देखा। क्या यह आपकी पूरी कंपनी का अभ्यास है? मुझे यह निश्चित रूप से असहज लगता है। मेरे विचार में यह आवश्यक नहीं है, और इस तरह संकेत पर शोर बढ़ता है ...
एरिक

@ एरिक एबव विधि एक विशाल ऐडऑन का हिस्सा है। कुछ वस्तुओं को गतिशील रूप से बनाया जाता है और एंड-यूज़र-इनपुट-पसंद पर निर्भर करता है। इसलिए उदाहरण के लिए तालिका में रिक्त स्थान आ सकते हैं- या दृश्य-नाम। उन चारों ओर दोहरे उद्धरण क्वेरी दुर्घटना नहीं होने देंगे ...!
निको

@ नीको मेरी दुनिया में आमतौर पर वर्ग कोष्ठक के साथ किया जाता है [this]। मुझे लगता है कि डबल कोट्स की तुलना में बेहतर है, मुझे लगता है।
एरिक

@ एरिक वर्ग कोष्ठक tsql है। मानक दोहरे उद्धरण है! वैसे भी, मैंने इसे इस तरह सीखा है और इसलिए किसी तरह इसका उपयोग किया जाता है!
निको

जवाबों:


3

मैं आपके प्रश्न का उत्तर सबसे अच्छे तरीके से नहीं दे सकता। लेकिन मैं समस्या को हल करने का एक अलग तरीका पेश कर सकता हूं , जो बेहतर हो सकता है या नहीं। इसकी यथोचित सपाट निष्पादन योजना है, और मुझे लगता है कि यह अच्छा प्रदर्शन करेगा। (मैं यह जानने के लिए उत्सुक हूं कि परिणाम साझा करें!)

मैं आपके बजाय अपनी खुद की वाक्य रचना शैली का उपयोग करने के लिए माफी माँगता हूँ - यह क्वेरी मैजड्री को मेरे पास आने में मदद करता है जब सब कुछ अपने सामान्य स्थान पर लाइन करता है।

क्वेरी SqlFiddle में उपलब्ध है । मैं एम्पिड 1 के लिए एक ओवरलैप में फेंक दिया सिर्फ यह सुनिश्चित करने के लिए कि मुझे कवर किया गया था। यदि आप अंततः पाते हैं कि उपस्थिति डेटा में ओवरलैप नहीं हो सकता है, तो आप अंतिम क्वेरी और Dense_Rankगणना को हटा सकते हैं ।

WITH Points AS (
  SELECT DISTINCT
    T.EmpID,
    P.TimePoint
  FROM
    (
      SELECT * FROM dbo.WorkTime
      UNION SELECT * FROM dbo.BreakTime
      UNION SELECT * FROM dbo.Absence
    ) T
    CROSS APPLY (VALUES (StartTime), (EndTime)) P (TimePoint)
), Groups AS (
  SELECT
    P.EmpID,
    P.TimePoint,
    Grp =
      Row_Number()
      OVER (PARTITION BY P.EmpID ORDER BY P.TimePoint, X.Which) / 2
  FROM
    Points P
    CROSS JOIN (VALUES (1), (2)) X (Which)
), Ranges AS (
  SELECT
    G.EmpID,
    StartTime = Min(G.TimePoint),
    EndTime = Max(G.TimePoint)
  FROM Groups G
  GROUP BY
    G.EmpID,
    G.Grp
  HAVING Count(*) = 2
), Presences AS (
  SELECT
    R.*,
    P.Present,
    Grp =
       Dense_Rank() OVER (PARTITION BY R.EmpID ORDER BY R.StartTime)
       - Dense_Rank() OVER (PARTITION BY R.EmpID, P.Present ORDER BY R.StartTime)
  FROM
    Ranges R
    CROSS APPLY (
      SELECT
        CASE WHEN EXISTS (
          SELECT *
          FROM dbo.WorkTime W
          WHERE
            R.EmpID = W.EmpID
            AND R.StartTime < W.EndTime
            AND W.StartTime < R.EndTime
        ) AND NOT EXISTS (
          SELECT *
          FROM dbo.BreakTime B
          WHERE
            R.EmpID = B.EmpID
            AND R.StartTime < B.EndTime
            AND B.StartTime < R.EndTime
        ) AND NOT EXISTS (
          SELECT *
          FROM dbo.Absence A
          WHERE
            R.EmpID = A.EmpID
            AND R.StartTime < A.EndTime
            AND A.StartTime < R.EndTime
        ) THEN 1 ELSE 0 END
    ) P (Present)
)
SELECT
  EmpID,
  StartTime = Min(StartTime),
  EndTime = Max(EndTime)
FROM Presences
WHERE Present = 1
GROUP BY
  EmpID,
  Grp
ORDER BY
  EmpID,
  StartTime;

नोट: इस क्वेरी के प्रदर्शन में सुधार किया जाएगा कि आपने तीन तालिकाओं को संयोजित किया है और यह इंगित करने के लिए एक कॉलम जोड़ा है कि यह किस प्रकार का था: कार्य, ब्रेक या अनुपस्थिति।

और सभी सीटीई, आप क्यों पूछते हैं? क्योंकि हर एक को डेटा के लिए मुझे क्या करने की जरूरत है। एक समुच्चय है, या मुझे एक विंडिंग फ़ंक्शन पर WHERE की स्थिति डालने या एक क्लॉज़ में उपयोग करने की आवश्यकता है जहां विंडोिंग फ़ंक्शन की अनुमति नहीं है।

अब मैं जा रहा हूँ और देख रहा हूँ कि क्या मैं इसे पूरा करने के लिए एक और रणनीति नहीं सोच सकता। :)

मनोरंजन के लिए मैं यहाँ एक "चित्र" शामिल करता हूँ जिसे मैंने समस्या को हल करने में मदद करने के लिए किया है:

------------
   -----------------
                ---------------
                           -----------

    ---    ------   ------       ------------

----   ----      ---      -------

डैश के तीन सेट (रिक्त स्थान से अलग), क्रम में दर्शाते हैं: उपस्थिति डेटा, अनुपस्थिति डेटा और वांछित परिणाम।


इस दृष्टिकोण के लिए धन्यवाद। मैं वापस कार्यालय में इसकी जाँच करूँगा और आपको बड़े डेटा-बेस के साथ रनटाइम-परिणाम दूंगा।
निको

रनटाइम निश्चित रूप से 1 दृष्टिकोण से बहुत अधिक है। मेरे पास यह जाँचने का कोई समय नहीं था कि क्या आगे के सूचकांक अभी और घट सकते हैं। जल्द से जल्द जांच करेंगे!
निको

मेरे पास एक और विचार है कि मेरे पास काम करने का समय नहीं है। इसके लायक क्या है, आपकी क्वेरी सभी तालिकाओं में अतिव्यापी श्रेणियों के साथ गलत परिणाम देती है।
एरिक

मैंने इसे फिर से जांचा, इस फिडेल को देखें जो तीनों तालिकाओं में पूरी तरह से अतिव्यापी अंतराल है। यह सही परिणाम देता है, जैसा कि मैं देख सकता हूं। क्या आप एक ऐसा मामला प्रदान कर सकते हैं जिसमें गलत परिणाम दिए गए हैं? बेफ़िक्री में डेमो डेटा को समायोजित करने के लिए स्वतंत्र महसूस करें!
निको

ठीक है, मैं आपकी बात मान गया। एक तालिका में अंतराल को अंतर करने के मामले में, परिणाम पागल हो गए। यह जाँच करेगा।
निको
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.