एक्सेस (जेट) एसक्यूएल: टेबलबी में डेटाइम स्टैम्प्स टेबलए में प्रत्येक डेटटाइम स्टैंप को फ्लैंक करते हुए


21

पहला शब्द

आप सुरक्षित रूप से नीचे दिए गए अनुभागों को अनदेखा कर सकते हैं (और शामिल हैं) JOINs: यदि आप कोड की एक दरार लेना चाहते हैं तो शुरू करना। पृष्ठभूमि और परिणाम सिर्फ संदर्भ के रूप में सेवा करते हैं। कृपया 2015-10-06 से पहले के संपादन इतिहास को देखें यदि आप यह देखना चाहते हैं कि कोड शुरू में कैसा दिखता था।


लक्ष्य

अंत में मैं तालिका में उपलब्ध जीपीएस डेटा की तारीख समय टिकटों के आधार पर ट्रांसमीटर ( Xया Xmit) के लिए प्रक्षेपित जीपीएस निर्देशांक की गणना करना चाहता हूं जो तालिका में SecondTableसीधे अवलोकन को फ्लैंक करता है FirstTable

अंतिम उद्देश्य को पूरा करने का मेरा तात्कालिक उद्देश्य यह पता लगाना है कि उन फ़्लैंकिंग समय बिंदुओं को प्राप्त FirstTableकरने के SecondTableलिए सबसे अच्छा कैसे शामिल किया जाए। बाद में मैं उस जानकारी का उपयोग कर सकता हूं जो मैं एक समभुज निर्देशांक प्रणाली के साथ रैखिक फिटिंग मानने वाले मध्यवर्ती जीपीएस निर्देशांक की गणना कर सकता हूं (यह कहने के लिए फैंसी शब्द कि मुझे परवाह नहीं है कि पृथ्वी इस पैमाने पर एक क्षेत्र है)।


प्रशन

  1. क्या समय से पहले टिकटों के निकटतम और उत्पन्न करने का एक अधिक कुशल तरीका है?
    • केवल "के बाद" को हथियाने और फिर "पहले" प्राप्त करने के द्वारा खुद के द्वारा निर्धारित किया गया है क्योंकि यह "बाद" से संबंधित है।
  2. क्या कोई अधिक सहज तरीका है जो (A<>B OR A=B)संरचना को शामिल नहीं करता है ।
    • Byrdzeye ने मूल विकल्प प्रदान किए, हालाँकि मेरा "वास्तविक दुनिया" का अनुभव उनकी सभी 4 रणनीतियों के साथ समान प्रदर्शन नहीं कर पाया। लेकिन वैकल्पिक जुड़ाव शैलियों को संबोधित करने के लिए उन्हें पूरा श्रेय।
  3. कोई अन्य विचार, चाल और सलाह जो आपके पास हो सकती है।
    • दोनों thusfar byrdzeye और Phrancis इस संबंध में काफी मददगार रहे हैं। मैंने पाया कि फ्राँसिस की सलाह को बहुत अच्छी तरह से सामने रखा गया था और एक महत्वपूर्ण स्तर पर सहायता प्रदान की थी, इसलिए मैं उसे यहाँ किनारे दे दूँगा।

मैं अभी भी किसी भी अतिरिक्त मदद की सराहना करता हूं जो मुझे प्रश्न 3 के संबंध में मिल सकती है। बुलेटप्वाइंट यह दर्शाता है कि मैं कौन मानता हूं कि मैंने व्यक्तिगत प्रश्न पर सबसे अधिक मदद की।


टेबल परिभाषाएँ

अर्ध-दृश्य प्रतिनिधित्व

FirstTable

Fields
  RecTStamp | DateTime  --can contain milliseconds via VBA code (see Ref 1) 
  ReceivID  | LONG
  XmitID    | TEXT(25)
Keys and Indices
  PK_DT     | Primary, Unique, No Null, Compound
    XmitID    | ASC
    RecTStamp | ASC
    ReceivID  | ASC
  UK_DRX    | Unique, No Null, Compound
    RecTStamp | ASC
    ReceivID  | ASC
    XmitID    | ASC

SecondTable

Fields
  X_ID      | LONG AUTONUMBER -- seeded after main table has been created and already sorted on the primary key
  XTStamp   | DateTime --will not contain partial seconds
  Latitude  | Double   --these are in decimal degrees, not degrees/minutes/seconds
  Longitude | Double   --this way straight decimal math can be performed
Keys and Indices
  PK_D      | Primary, Unique, No Null, Simple
    XTStamp   | ASC
  UIDX_ID   | Unique, No Null, Simple
    X_ID      | ASC

ReceiverDetails टेबल

Fields
  ReceivID                      | LONG
  Receiver_Location_Description | TEXT -- NULL OK
  Beginning                     | DateTime --no partial seconds
  Ending                        | DateTime --no partial seconds
  Lat                           | DOUBLE
  Lon                           | DOUBLE
Keys and Indicies
  PK_RID  | Primary, Unique, No Null, Simple
    ReceivID | ASC

ValidXmitters तालिका

Field (and primary key)
  XmitID    | TEXT(25) -- primary, unique, no null, simple

एसक्यूएल फिडल ...

... ताकि आप टेबल परिभाषाओं और कोड के साथ खेल सकें। यह सवाल MSAccess के लिए है, लेकिन जैसा कि Phrancis ने बताया है, Access के लिए कोई SQL फिडल स्टाइल नहीं है। तो, आपको यहाँ जाने में सक्षम होना चाहिए कि मेरी सारणी की परिभाषाएँ और कोड Phrancis के उत्तर के आधार पर देखें : http://sqlfiddle.com/## .6/e9942
/4 (बाहरी लिंक)


JOINs: शुरू हो रहा है

मेरी वर्तमान "आंतरिक हिम्मत" जोइन रणनीति

सबसे पहले एक FirstTable_rekeyed को कॉलम ऑर्डर और कंपाउंड प्राइमरी की के साथ (RecTStamp, ReceivID, XmitID)अनुक्रमित / सॉर्ट करें ASC। मैंने प्रत्येक कॉलम पर अलग-अलग इंडेक्स भी बनाए। फिर इसे ऐसे भरें।

INSERT INTO FirstTable_rekeyed (RecTStamp, ReceivID, XmitID)
  SELECT DISTINCT ROW RecTStamp, ReceivID, XmitID
  FROM FirstTable
  WHERE XmitID IN (SELECT XmitID from ValidXmitters)
  ORDER BY RecTStamp, ReceivID, XmitID;

उपरोक्त क्वेरी नई तालिका को 153006 रिकॉर्ड के साथ भरती है और 10 सेकंड या उससे अधिक के भीतर लौटती है।

जब यह पूरी विधि "सिलेक्ट काउंट (*) FROM (...)" में लिपट जाती है तो एक या दो सेकंड के भीतर पूरा हो जाता है जब TOP 1 सबक्वेरी विधि का उपयोग किया जाता है

SELECT 
    ReceiverRecord.RecTStamp, 
    ReceiverRecord.ReceivID, 
    ReceiverRecord.XmitID,
    (SELECT TOP 1 XmitGPS.X_ID FROM SecondTable as XmitGPS WHERE ReceiverRecord.RecTStamp < XmitGPS.XTStamp ORDER BY XmitGPS.X_ID) AS AfterXmit_ID
    FROM FirstTable_rekeyed AS ReceiverRecord
    -- INNER JOIN SecondTable AS XmitGPS ON (ReceiverRecord.RecTStamp < XmitGPS.XTStamp)
         GROUP BY RecTStamp, ReceivID, XmitID;
-- No separate join needed for the Top 1 method, but it would be required for the other methods. 
-- Additionally no restriction of the returned set is needed if I create the _rekeyed table.
-- May not need GROUP BY either. Could try ORDER BY.
-- The three AfterXmit_ID alternatives below take longer than 3 minutes to complete (or do not ever complete).
  -- FIRST(XmitGPS.X_ID)
  -- MIN(XmitGPS.X_ID)
  -- MIN(SWITCH(XmitGPS.XTStamp > ReceiverRecord.RecTStamp, XmitGPS.X_ID, Null))

पिछले "आंतरिक हिम्मत" क्वेरी में शामिल हों

पहला (व्रत ... लेकिन बहुत अच्छा नहीं)

SELECT 
  A.RecTStamp,
  A.ReceivID,
  A.XmitID,
  MAX(IIF(B.XTStamp<= A.RecTStamp,B.XTStamp,Null)) as BeforeXTStamp,
  MIN(IIF(B.XTStamp > A.RecTStamp,B.XTStamp,Null)) as AfterXTStamp
FROM FirstTable as A
INNER JOIN SecondTable as B ON 
  (A.RecTStamp<>B.XTStamp OR A.RecTStamp=B.XTStamp)
GROUP BY A.RecTStamp, A.ReceivID, A.XmitID
  -- alternative for BeforeXTStamp MAX(-(B.XTStamp<=A.RecTStamp)*B.XTStamp)
  -- alternatives for AfterXTStamp (see "Aside" note below)
  -- 1.0/(MAX(1.0/(-(B.XTStamp>A.RecTStamp)*B.XTStamp)))
  -- -1.0/(MIN(1.0/((B.XTStamp>A.RecTStamp)*B.XTStamp)))

दूसरा (धीमा)

SELECT
  A.RecTStamp, AbyB1.XTStamp AS BeforeXTStamp, AbyB2.XTStamp AS AfterXTStamp
FROM (FirstTable AS A INNER JOIN 
  (select top 1 B1.XTStamp, A1.RecTStamp 
   from SecondTable as B1, FirstTable as A1
   where B1.XTStamp<=A1.RecTStamp
   order by B1.XTStamp DESC) AS AbyB1 --MAX (time points before)
ON A.RecTStamp = AbyB1.RecTStamp) INNER JOIN 
  (select top 1 B2.XTStamp, A2.RecTStamp 
   from SecondTable as B2, FirstTable as A2
   where B2.XTStamp>A2.RecTStamp
   order by B2.XTStamp ASC) AS AbyB2 --MIN (time points after)
ON A.RecTStamp = AbyB2.RecTStamp; 

पृष्ठभूमि

मेरे पास 1 मिलियन प्रविष्टियों के तहत एक टेलीमेट्री टेबल (ए के रूप में उर्फ) है DateTime, जिसमें एक स्टांप, एक ट्रांसमीटर आईडी और एक रिकॉर्डिंग डिवाइस आईडी के आधार पर एक प्राथमिक प्राथमिक कुंजी है । मेरे नियंत्रण से परे परिस्थितियों के कारण, मेरी SQL भाषा Microsoft Access में मानक जेट DB है (उपयोगकर्ता 2007 और बाद के संस्करणों का उपयोग करेंगे)। इनमें से केवल 200,000 प्रविष्टियाँ ट्रांसमीटर आईडी के कारण क्वेरी के लिए प्रासंगिक हैं।

एक दूसरी टेलीमेट्री टेबल (उर्फ बी) है जिसमें एक ही DateTimeप्राथमिक कुंजी के साथ लगभग 50,000 प्रविष्टियां शामिल हैं

पहले चरण के लिए, मैंने दूसरी तालिका से पहली तालिका में टिकटों के निकटतम टाइमस्टैम्प को खोजने पर ध्यान केंद्रित किया।


परिणाम प्राप्त करें

Quirks कि मैंने खोजा है ...

... डिबगिंग के दौरान रास्ते के साथ

JOINतर्क लिखने में बहुत अजीब लगता है, जैसा FROM FirstTable as A INNER JOIN SecondTable as B ON (A.RecTStamp<>B.XTStamp OR A.RecTStamp=B.XTStamp)कि @byrdzeye ने एक टिप्पणी में बताया (जो गायब हो गया है) क्रॉस-जॉइन का एक रूप है। ध्यान दें कि प्रतिस्थापन LEFT OUTER JOINके लिए INNER JOINप्रकट होता है इसके बाद के संस्करण कोड में मात्रा या लाइनों लौटे की पहचान करने में कोई प्रभाव बनाने के लिए। मैं भी बंद खंड या कहने के लिए छोड़ने के लिए प्रतीत नहीं कर सकते ON (1=1)। इस क्वेरी में दी गई पंक्तियों के परिणाम में (बजाय INNERया उससे LEFT OUTER JOIN) सम्मिलित होने के लिए Count(select * from A) * Count(select * from B)केवल एक प्रति तालिका ए के बजाय (ए <> बी या ए = बी) के रूप में स्पष्ट JOINरिटर्न देता है। यह स्पष्ट रूप से उपयुक्त नहीं है। FIRSTएक यौगिक प्राथमिक कुंजी प्रकार दिए गए उपयोग करने के लिए उपलब्ध प्रतीत नहीं होता है।

दूसरी JOINशैली, हालांकि यकीनन अधिक सुपाठ्य है, धीमे होने के कारण। ऐसा इसलिए हो सकता है क्योंकि JOINबड़ी तालिका के साथ-साथ CROSS JOINदोनों विकल्पों में पाए गए दो s के लिए एक अतिरिक्त दो आंतरिक s की आवश्यकता होती है ।

एक IIFसाथ : क्लॉज को बदलने MIN/ MAXसमान संख्या में प्रविष्टियों को वापस करने के लिए प्रकट होता है।
MAX(-(B.XTStamp<=A.RecTStamp)*B.XTStamp)
"पहले" ( MAX) टाइमस्टैम्प के लिए काम करता है, लेकिन "बाद में" ( MIN) के लिए सीधे काम नहीं करता है इस प्रकार है:
MIN(-(B.XTStamp>A.RecTStamp)*B.XTStamp)
क्योंकि स्थिति के लिए न्यूनतम हमेशा 0 होता है FALSE। यह 0 किसी भी पोस्ट-एपोच से कम है DOUBLE(जो कि एक DateTimeक्षेत्र एक्सेस का सबसेट है और यह गणना फ़ील्ड को में बदल देती है)। IIFऔर MIN/ MAXतरीकों विकल्पों शून्य से क्योंकि विभाजन AfterXTStamp मूल्य काम के लिए प्रस्तावित ( FALSE) शून्य मान है, जो कुल कार्यों MIN और MAX छोड़ उत्पन्न करता है।

अगला कदम

इसे और आगे ले जाते हुए, मैं दूसरी तालिका में टाइमस्टैम्प को खोजने की इच्छा रखता हूं जो सीधे पहली तालिका में टाइमस्टैम्प को फ्लैंक करता है और उन बिंदुओं पर समय दूरी के आधार पर दूसरी तालिका से डेटा मानों का एक रैखिक प्रक्षेप करता है (अर्थात यदि टाइमस्टैम्प से पहली तालिका "पहले" और "के बाद" के बीच 25% है, मैं "तालिका के बाद" बिंदु से जुड़े 2 तालिका मूल्य डेटा और "पहले" से 75% से आने वाले गणना मूल्य का 25% चाहूंगा। )। आंतरिक हिम्मत के हिस्से के रूप में संशोधित शामिल प्रकार का उपयोग करना, और नीचे दिए गए उत्तर के बाद मैं उत्पादन ...

    SELECT
        AvgGPS.XmitID,
        StrDateIso8601Msec(AvgGPS.RecTStamp) AS RecTStamp_ms,
        -- StrDateIso8601MSec is a VBA function returning a TEXT string in yyyy-mm-dd hh:nn:ss.lll format
        AvgGPS.ReceivID,
        RD.Receiver_Location_Description,
        RD.Lat AS Receiver_Lat,
        RD.Lon AS Receiver_Lon,
        AvgGPS.Before_Lat * (1 - AvgGPS.AfterWeight) + AvgGPS.After_Lat * AvgGPS.AfterWeight AS Xmit_Lat,
        AvgGPS.Before_Lon * (1 - AvgGPS.AfterWeight) + AvgGPS.After_Lon * AvgGPS.AfterWeight AS Xmit_Lon,
        AvgGPS.RecTStamp AS RecTStamp_basic
    FROM ( SELECT 
        AfterTimestampID.RecTStamp,
        AfterTimestampID.XmitID,
        AfterTimestampID.ReceivID,
        GPSBefore.BeforeXTStamp, 
        GPSBefore.Latitude AS Before_Lat, 
        GPSBefore.Longitude AS Before_Lon,
        GPSAfter.AfterXTStamp, 
        GPSAfter.Latitude AS After_Lat, 
        GPSAfter.Longitude AS After_Lon,
        ( (AfterTimestampID.RecTStamp - GPSBefore.XTStamp) / (GPSAfter.XTStamp - GPSBefore.XTStamp) ) AS AfterWeight
        FROM (
            (SELECT 
                ReceiverRecord.RecTStamp, 
                ReceiverRecord.ReceivID, 
                ReceiverRecord.XmitID,
               (SELECT TOP 1 XmitGPS.X_ID FROM SecondTable as XmitGPS WHERE ReceiverRecord.RecTStamp < XmitGPS.XTStamp ORDER BY XmitGPS.X_ID) AS AfterXmit_ID
             FROM FirstTable AS ReceiverRecord 
             -- WHERE ReceiverRecord.XmitID IN (select XmitID from ValidXmitters)
             GROUP BY RecTStamp, ReceivID, XmitID
            ) AS AfterTimestampID INNER JOIN SecondTable AS GPSAfter ON AfterTimestampID.AfterXmit_ID = GPSAfter.X_ID
        ) INNER JOIN SecondTable AS GPSBefore ON AfterTimestampID.AfterXmit_ID = GPSBefore.X_ID + 1
    ) AS AvgGPS INNER JOIN ReceiverDetails AS RD ON (AvgGPS.ReceivID = RD.ReceivID) AND (AvgGPS.RecTStamp BETWEEN RD.Beginning AND RD.Ending)
    ORDER BY AvgGPS.RecTStamp, AvgGPS.ReceivID;

... जो 152928 रिकॉर्ड लौटाता है, (कम से कम लगभग) अपेक्षित रिकॉर्ड के अंतिम संख्या के अनुरूप। मेरे i7-4790, 16GB RAM, कोई SSD, विन 8.1 प्रो सिस्टम पर रन समय शायद 5-10 मिनट है।


संदर्भ 1: एमएस एक्सेस मिलिसकॉन्ड टाइम मानों को संभाल सकता है - वास्तव में और साथ में स्रोत फ़ाइल [08080011.txt]

जवाबों:


10

मुझे सबसे पहले आपको एक्सेस डीबी के साथ ऐसा करने के लिए आपके साहस की सराहना करनी चाहिए, जो मेरे अनुभव से एसक्यूएल जैसा कुछ भी करना बहुत मुश्किल है। वैसे भी, समीक्षा पर।


पहले सम्मिलित हों

आपके IIFफ़ील्ड चयनों को इसके बजाय स्विच स्टेटमेंट का उपयोग करने से लाभ हो सकता है । यह कभी-कभी ऐसा प्रतीत होता है, विशेष रूप से चीजों के साथ एसक्यूएल, कि एक SWITCH(आमतौर पर CASEविशिष्ट एसक्यूएल के रूप में जाना जाता है) काफी तेज है जब सिर्फ एक के शरीर में सरल तुलना कर रहा है SELECT। आपके मामले में वाक्यविन्यास लगभग समान होगा, हालांकि एक क्षेत्र में तुलना के एक बड़े हिस्से को कवर करने के लिए एक स्विच का विस्तार किया जा सकता है। कुछ विचार करने के लिए।

  SWITCH (
    expr1, val1,
    expr2, val2,
    val3        -- default value or "else"
  )

एक स्विच पठनीयता को बड़े बयानों में भी मदद कर सकता है। प्रसंग में:

  MAX(SWITCH(B.XTStamp <= A.RecTStamp,B.XTStamp,Null)) as BeforeXTStamp,
  --alternatively MAX(-(B.XTStamp<=A.RecTStamp)*B.XTStamp) as BeforeXTStamp,
  MIN(SWITCH(B.XTStamp>A.RecTStamp,B.XTStamp,Null)) as AfterXTStamp

खुद से जुड़ने के लिए, मुझे लगता (A.RecTStamp<>B.XTStamp OR A.RecTStamp=B.XTStamp)है कि आप जितना अच्छा करने जा रहे हैं, उतना अच्छा है, जो आप करने की कोशिश कर रहे हैं। यह इतना तेज़ नहीं है, लेकिन मुझे उम्मीद है कि यह भी नहीं होगा।


दूसरा सम्मिलित हों

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

एक टिप्पणी, मैंने देखा कि आपने पुराने ANSI-89 को वाक्यविन्यास में शामिल किया। इससे बचने के लिए सबसे अच्छा है, प्रदर्शन अधिक आधुनिक सम्मिलित वाक्य रचना के साथ समान या बेहतर होगा, और वे कम अस्पष्ट या पढ़ने में आसान, गलतियाँ करने के लिए कठिन हैं।

FROM (FirstTable AS A INNER JOIN 
  (select top 1 B1.XTStamp, A1.RecTStamp 
   from SecondTable as B1
   inner join FirstTable as A1
     on B1.XTStamp <= A1.RecTStamp
   order by B1.XTStamp DESC) AS AbyB1 --MAX (time points before)

नामकरण की बातें

मुझे लगता है कि जिस तरह से आपकी चीजों का नाम रखा गया है वह सबसे अच्छा है और सबसे खराब तरीके से गूढ़ है। A, B, A1, B1आदि के रूप में टेबल उपनाम मुझे लगता है कि बेहतर हो सकता है। इसके अलावा, मुझे लगता है कि क्षेत्र के नाम बहुत अच्छे नहीं हैं, लेकिन मुझे लगता है कि इस पर आपका नियंत्रण नहीं हो सकता है। मैं बस जल्दी से कोडनाम कोड को चीजों के नामकरण के विषय पर उद्धृत करूंगा, और इसे उस पर छोड़ दूंगा ...

"अविवेकी!" ने पुजारी को जवाब दिया। "अपनी निष्क्रीय संज्ञाओं को सत्यापित करें!"


"अगला चरण" क्वेरी

मैं इसके बारे में ज्यादा समझ नहीं बना सका कि यह कैसे लिखा गया था, मुझे इसे एक टेक्स्ट एडिटर के पास ले जाना पड़ा और इसे और अधिक पठनीय बनाने के लिए कुछ स्टाइल में बदलाव करना पड़ा। मुझे पता है कि एक्सेस 'एसक्यूएल एडिटर क्लंकी से परे है, इसलिए मैं आमतौर पर अपने प्रश्नों को नोटपैड ++ या उदात्त पाठ जैसे अच्छे संपादक में लिखता हूं। मैंने इसे अधिक पठनीय बनाने के लिए कुछ शैलीगत परिवर्तन लागू किए:

  • 2 स्थानों के बजाय 4 स्थान इंडेंट
  • गणितीय और तुलना ऑपरेटरों के आसपास रिक्त स्थान
  • ब्रेसिज़ और इंडेंटेशन के अधिक प्राकृतिक रखने (मैं जावा-शैली ब्रेसिज़ के साथ गया, लेकिन आपकी पसंद पर सी-स्टाइल भी हो सकता है)

जैसा कि यह पता चला है, यह वास्तव में एक बहुत ही जटिल प्रश्न है। इसका अर्थ समझने के लिए, मुझे अंतरतम क्वेरी से शुरू करना होगा, आपका IDडेटा सेट, जो मुझे समझ में आता है कि आपका फर्स्ट जॉइन वही है। यह उन डिवाइसों की आईडी और टाइमस्टैम्प लौटाता है, जहां टाइमस्टैम्प से पहले / बाद में आपके डिवाइस में रुचि रखने वाले डिवाइस के सबसेट के भीतर सबसे नज़दीकी होती IDहै ClosestTimestampID

आपका Detजुड़ाव केवल एक बार उपयोग किया जाता है:

यहाँ छवि विवरण दर्ज करें

बाकी समय, यह केवल उन मूल्यों से जुड़ता है जो आपके पास पहले से हैं ClosestTimestampID। इसलिए इसके बजाय हमें ऐसा करने में सक्षम होना चाहिए:

    ) AS ClosestTimestampID
    INNER JOIN SecondTable AS TL1 
        ON ClosestTimestampID.BeforeXTStamp = TL1.XTStamp) 
    INNER JOIN SecondTable AS TL2 
        ON ClosestTimestampID.AfterXTStamp = TL2.XTStamp
    WHERE ClosestTimestampID.XmitID IN (<limited subset S>)

हो सकता है कि एक बहुत बड़ा प्रदर्शन न हो, लेकिन जेट जेट डीबी ऑप्टिमाइज़र की मदद करने के लिए हम कुछ भी कर सकते हैं!


मैं यह महसूस नहीं कर सकता कि गणना BeforeWeightऔर एल्गोरिथ्म के लिए और AfterWeightजिसे आप प्रक्षेप करने के लिए उपयोग करते हैं, बेहतर किया जा सकता है, लेकिन दुर्भाग्य से मैं उन लोगों के साथ बहुत अच्छा नहीं हूं।

दुर्घटनाग्रस्त होने से बचने के लिए एक सुझाव (हालांकि यह आपके आवेदन के आधार पर आदर्श नहीं है) अपनी नेस्टेड उप-श्रृंखलाओं को अपने स्वयं के तालिकाओं में तोड़ना और जब आवश्यक हो उन्हें अद्यतन करना होगा। मुझे यकीन नहीं है कि आपको अपने स्रोत डेटा को कितनी बार रीफ़्रेश करने की आवश्यकता है, लेकिन यदि यह अक्सर नहीं होता है, तो आप टेबल और व्युत्पन्न टेबल के अपडेट को शेड्यूल करने के लिए कुछ VBA कोड लिखने के बारे में सोच सकते हैं, और बस खींचने के लिए अपनी सबसे बाहरी क्वेरी छोड़ दें। मूल स्रोत के बजाय उन तालिकाओं से। बस एक विचार, जैसे मैंने कहा कि आदर्श नहीं है, लेकिन इस उपकरण को देखते हुए आपके पास कोई विकल्प नहीं हो सकता है।


सब कुछ एक साथ:

SELECT
    InGPS.XmitID,
    StrDateIso8601Msec(InGPS.RecTStamp) AS RecTStamp_ms,
       -- StrDateIso8601MSec is a VBA function returning a TEXT string in yyyy-mm-dd hh:nn:ss.lll format
    InGPS.ReceivID,
    RD.Receiver_Location_Description,
    RD.Lat AS Receiver_Lat,
    RD.Lon AS Receiver_Lon,
    InGPS.Before_Lat * InGPS.BeforeWeight + InGPS.After_Lat * InGPS.AfterWeight AS Xmit_Lat,
    InGPS.Before_Lon * InGPS.BeforeWeight + InGPS.After_Lon * InGPS.AfterWeight AS Xmit_Lon,
    InGPS.RecTStamp AS RecTStamp_basic
FROM (
    SELECT 
        ClosestTimestampID.RecTStamp,
        ClosestTimestampID.XmitID,
        ClosestTimestampID.ReceivID,
        ClosestTimestampID.BeforeXTStamp, 
        TL1.Latitude AS Before_Lat, 
        TL1.Longitude AS Before_Lon,
        (1 - ((ClosestTimestampID.RecTStamp - ClosestTimestampID.BeforeXTStamp) 
            / (ClosestTimestampID.AfterXTStamp - ClosestTimestampID.BeforeXTStamp))) AS BeforeWeight,
        ClosestTimestampID.AfterXTStamp, 
        TL2.Latitude AS After_Lat, 
        TL2.Longitude AS After_Lon,
        (     (ClosestTimestampID.RecTStamp - ClosestTimestampID.BeforeXTStamp) 
            / (ClosestTimestampID.AfterXTStamp - ClosestTimestampID.BeforeXTStamp)) AS AfterWeight
        FROM (((
            SELECT 
                A.RecTStamp, 
                A.ReceivID, 
                A.XmitID,
                MAX(SWITCH(B.XTStamp <= A.RecTStamp, B.XTStamp, Null)) AS BeforeXTStamp,
                MIN(SWITCH(B.XTStamp > A.RecTStamp, B.XTStamp, Null)) AS AfterXTStamp
            FROM FirstTable AS A
            INNER JOIN SecondTable AS B 
                ON (A.RecTStamp <> B.XTStamp OR A.RecTStamp = B.XTStamp)
            WHERE A.XmitID IN (<limited subset S>)
            GROUP BY A.RecTStamp, ReceivID, XmitID
        ) AS ClosestTimestampID
        INNER JOIN FirstTable AS Det 
            ON (Det.XmitID = ClosestTimestampID.XmitID) 
            AND (Det.ReceivID = ClosestTimestampID.ReceivID) 
            AND (Det.RecTStamp = ClosestTimestampID.RecTStamp)) 
        INNER JOIN SecondTable AS TL1 
            ON ClosestTimestampID.BeforeXTStamp = TL1.XTStamp) 
        INNER JOIN SecondTable AS TL2 
            ON ClosestTimestampID.AfterXTStamp = TL2.XTStamp
        WHERE Det.XmitID IN (<limited subset S>)
    ) AS InGPS
INNER JOIN ReceiverDetails AS RD 
    ON (InGPS.ReceivID = RD.ReceivID) 
    AND (InGPS.RecTStamp BETWEEN <valid parameters from another table>)
ORDER BY StrDateIso8601Msec(InGPS.RecTStamp), InGPS.ReceivID;

5
  • अतिरिक्त विशेषताओं और फ़िल्टर शर्तों को जोड़ा गया।
  • मिनट और अधिकतम नेस्टेड क्वेरी का उपयोग करके क्रॉस जॉइन के किसी भी रूप को समाप्त कर दिया जाता है। यह सबसे बड़ा प्रदर्शन लाभ है।
  • आंतरिक और सबसे नेस्टेड क्वेरी द्वारा लौटाए गए न्यूनतम और अधिकतम फ्लैक मान प्राथमिक कुंजी मान (स्कैन) हैं जो अंतिम गणना के लिए एक तलाश का उपयोग करके अतिरिक्त फ्लैंक विशेषताओं (लाट और लोन) को पुनः प्राप्त करने के लिए उपयोग किया जाता है (एक्सेस में एक समतुल्य समतुल्य है)।
  • प्राथमिक तालिका विशेषताओं को पुनर्प्राप्त किया जाता है और अंतरतम क्वेरी में फ़िल्टर किया जाता है और प्रदर्शन में मदद करनी चाहिए।
  • छँटाई के लिए समय मान (StrDateIso8601Msec) को प्रारूपित करने की कोई आवश्यकता नहीं है। तालिका से डेटाटाइम मान का उपयोग करना बराबर है।

SQL सर्वर निष्पादन योजनाएं (क्योंकि प्रवेश इसे दिखा नहीं सकता है)
अंतिम आदेश के बिना क्योंकि इसके महंगे:
क्लस्टर किए गए इंडेक्स स्कैन [ReceiverDetails]। [PK_ReceiverDetails] लागत 16%
क्लस्टर किए गए इंडेक्स [FirstTable] की मांग करते हैं। [PK_FirstTable] लागत 19%
क्लस्टर इंडेक्स। [SecondTable] की तलाश करें। [PK_SecondTable] लागत 16%
क्लस्टर इंडेक्स की तलाश करें [SecondTable]। [PK_SecondTable की लागत] लागत 16%
क्लस्टर किए गए इंडेक्स [SecondTable] की तलाश करें। [PK_econdTable] [TL2] लागत 16%
क्लस्टर किए गए इंडेक्स [SecondTable] की तलाश करें। [TL1] लागत १६%

अंतिम आदेश के साथ:
क्रमबद्ध लागत ३६%
क्लस्टर इंडेक्स स्कैन [प्राप्तकर्ता] [PK_ReceiverDetails] लागत १०%
क्लस्टर इंडेक्स सीक [फर्स्टटेबल] [PK_FirstTable] लागत १२%।
क्लस्टर सूचकांक की तलाश [SecondTable]। [PK_SecondTable] लागत 10%
क्लस्टर सूचकांक की तलाश [SecondTable]। [PK_SecondTable] लागत 10%
क्लस्टर सूचकांक की शोध [SecondTable]। [PK_SecondTable] [TL2] लागत 10%
क्लस्टर सूचकांक की शोध [SecondTable]। [ PK_SecondTable] [TL1] लागत 10%

कोड:

select
     ClosestTimestampID.XmitID
    --,StrDateIso8601Msec(InGPS.RecTStamp) AS RecTStamp_ms
    ,ClosestTimestampID.ReceivID
    ,ClosestTimestampID.Receiver_Location_Description
    ,ClosestTimestampID.Lat
    ,ClosestTimestampID.Lon
,[TL1].[Latitude] * (1 - ((ClosestTimestampID.RecTStamp - ClosestTimestampID.BeforeXTStamp) / (ClosestTimestampID.AfterXTStamp - ClosestTimestampID.BeforeXTStamp))) + [TL2].[Latitude] * ((ClosestTimestampID.RecTStamp - ClosestTimestampID.BeforeXTStamp) / (ClosestTimestampID.AfterXTStamp - ClosestTimestampID.BeforeXTStamp)) AS Xmit_Lat
,[TL1].[Longitude] * (1 - ((ClosestTimestampID.RecTStamp - ClosestTimestampID.BeforeXTStamp) / (ClosestTimestampID.AfterXTStamp - ClosestTimestampID.BeforeXTStamp))) + [TL2].[Longitude] * ((ClosestTimestampID.RecTStamp - ClosestTimestampID.BeforeXTStamp) / (ClosestTimestampID.AfterXTStamp - ClosestTimestampID.BeforeXTStamp)) AS Xmit_Lon
    ,ClosestTimestampID.RecTStamp as RecTStamp_basic
from (
        (
            (
                select
                     FirstTable.RecTStamp
                    ,FirstTable.ReceivID
                    ,FirstTable.XmitID
                    ,ReceiverDetails.Receiver_Location_Description
                    ,ReceiverDetails.Lat
                    ,ReceiverDetails.Lon
                    ,(
                        select max(XTStamp) as val
                        from SecondTable
                        where XTStamp <= FirstTable.RecTStamp
                     ) as BeforeXTStamp
                    ,(
                        select min(XTStamp) as val
                        from SecondTable
                        where XTStamp > FirstTable.RecTStamp
                     ) as AfterXTStamp
                from FirstTable
                inner join ReceiverDetails
                on ReceiverDetails.ReceivID = FirstTable.ReceivID
                where FirstTable.RecTStamp between #1/1/1990# and #1/1/2020#
                and FirstTable.XmitID in (100,110)
            ) as ClosestTimestampID
            inner join SecondTable as TL1
            on ClosestTimestampID.BeforeXTStamp = TL1.XTStamp
        )
        inner join SecondTable as TL2
        on ClosestTimestampID.AfterXTStamp = TL2.XTStamp
    )
order by ClosestTimestampID.RecTStamp, ClosestTimestampID.ReceivID;

प्रदर्शन में क्रॉस से जुड़ने वाले क्वेरी के खिलाफ मेरी क्वेरी का परीक्षण करना।

FirstTable को 13 रिकॉर्ड के साथ भरा गया था और 1,000,000 के साथ SecondTable को।
मेरी क्वेरी के लिए निष्पादन की योजना जो पोस्ट की गई है उससे बहुत कुछ नहीं बदला।
क्रॉस में शामिल होने के लिए निष्पादन की योजना:
नेस्टेड लूप्स का उपयोग करते हुए नेस्टेड लूप्स की लागत 81% होती है, INNER JOIN SecondTable AS B ON (A.RecTStamp <> B.XTStamp OR A.RecTStamp = B.XTStamp
अगर CROSS JOIN SecondTable AS B' or ',SecondTable AS B
स्ट्रीम एग्रीगेट 8%
इंडेक्स स्कैन [SecondTable] [UK_ID] का उपयोग करके 75% तक गिर जाता है, [B] 6%
टेबल स्पूल 5%
कई क्लस्टर इंडेक्स सीक और इंडेक्स खोजता है। (पोस्ट के अनुसार मेरी क्वेरी के समान) 0% की लागत के साथ।

निष्पादन का समय मेरी क्वेरी और क्रॉस जॉइन के लिए .007 और 8-9 सेकंड है।
लागत तुलना 0% और 100%।

मैंने 50,000 रिकॉर्ड के साथ FirstTable को लोड किया और एक सम्मिलित स्थिति के लिए ReceiverDetails के लिए एक रिकॉर्ड बनाया और अपनी क्वेरी चलाई।
50,013 0.9 और 1.0 सेकंड के बीच लौटा।

मैंने क्रॉस ज्वाइन के साथ दूसरी क्वेरी चलाई और इसे मारने से पहले लगभग 20 मिनट तक चलने दिया।
यदि क्रॉस जॉइन क्वेरी को केवल मूल 13 को वापस करने के लिए फ़िल्टर किया गया है, तो निष्पादन समय फिर से 8-9 सेकंड है।
फ़िल्टर स्थिति का प्लेसमेंट आंतरिक सबसे चुनिंदा, बाहरी सबसे चुनिंदा और दोनों पर था। कोई फर्क नहीं।

CROSS JOIN के पक्ष में इन दो सम्मिलित स्थितियों में अंतर होता है, पहला एक विधेय का उपयोग करता है, CROSS JOIN नहीं:
INNER JOIN SecondTable AS B ON (A.RecTStamp <> B.XTStamp OR A.RecTStamp = B.XTStamp) CROSS JOIN SecondTable AS B


मेरे सिस्टम पर ClosestTimestampID भाग को चलाने पर गणना (*) में संलग्न होने पर तुरंत 152928 रिकॉर्ड मिलते हैं। उस समय वास्तविक रिकॉर्ड वापस करते समय मेरी MSAccess लॉक हो गई - शायद अन्य विधि से टेम्प टेबल सभी प्रकार की मेमोरी को हॉगिंग कर रहे थे। मुझे लगता है कि मैं आपकी कार्यप्रणाली से जो अंतिम प्रश्न उत्पन्न करता हूं, वह मेरे द्वारा वर्तमान में उपयोग किए जा रहे समान होगा। जो मुझे लगता है कि एक अच्छी बात है :)
20

1
अपनी मूल टिप्पणी में आपने संकेत दिया कि आपने कुछ रिकॉर्ड तुरंत वापस कर लिए हैं। एक्सेस की रणनीति के साथ आने और निष्पादन के समय की अपेक्षाएं निर्धारित करने के तरीके के बारे में यह महत्वपूर्ण है। इसे आस्थगित निष्पादन कहा जाता है। (जब आप पिछले रिकॉर्ड से टकराते हैं तो यह दुर्घटनाग्रस्त हो जाता है।) अंतिम क्वेरी में ऊपरी सीमा वापसी रिकॉर्ड गणना क्या है?
byrdzeye

मेरा मानना ​​है कि 152928
एमपाग

नए रिकॉर्ड के रूप में दोनों तालिकाओं में दिनांक समय मानों की प्रकृति क्या है। क्या वे वर्तमान समय के टिकटों या हाल के मूल्यों या पूरी तरह से यादृच्छिक हैं?
१५:१५ बजे १५:१५

पहली तालिका में डेटाइम टिकट हैं जो 2013 या अधिक हाल के हैं। दूसरी तालिका में DateTime टिकटें हैं जो कुछ महीनों के भीतर 2015 के मध्य में हैं। यदि नए मान जोड़े जाते हैं, तो वे मौजूदा सेट के बाद होने की संभावना होगी (लेकिन गारंटी नहीं दी जाएगी)। नए मानों को या तो तालिका में जोड़ा जा सकता है।
मपग

2

एक दूसरे उत्तर को जोड़ना, पहले से बेहतर नहीं है, लेकिन प्रस्तुत किसी भी आवश्यकता को बदलने के बिना, प्रवेश को प्रस्तुत करने और तड़क-भड़क प्रकट करने के कुछ तरीके हैं। 'ट्रिगर्स' का उपयोग करके समय प्रभावकारिता में जटिलताओं को थोड़ा सा 'भौतिककृत' करें। एक्सेस टेबल में अवरोधक नहीं होते हैं और क्रूड प्रक्रियाओं को इंजेक्ट करते हैं।

--*** Create a table for flank values.
    create table Flank (
         RecTStamp      datetime not null
        ,BeforeXTStamp  datetime null
        ,AfterXTStamp   datetime null
        ,constraint PK_Flank primary key clustered ( RecTStamp asc )
        )

--*** Create a FlankUpdateLoop sub. (create what is missing)
    -- loop until rowcount < 5000 or rowcount = 0
    -- a 5K limit appears to be manageable for Access, especially for the initial population.
    insert into Flank (
         RecTStamp
        ,BeforeXTStamp
        ,AfterXTStamp
        )
    select top 5000 FirstTable.RecTStamp
        ,(
            select max(XTStamp) as val
            from SecondTable
            where XTStamp <= FirstTable.RecTStamp
            ) as BeforeXTStamp
        ,(
            select min(XTStamp) as val
            from SecondTable
            where XTStamp > FirstTable.RecTStamp
            ) as AfterXTStamp
    from FirstTable
    left join Flank
        on FirstTable.RecTStamp = Flank.RecTStamp
    where Flank.RecTStamp is null;

--*** For FirstTable Adds, Changes or Deletes:
    delete from Flank where Flank.RecTStamp = CRUD_RecTStamp
    execute FlankUpdateLoop --See above. This will handle Adds, Changes or Deletes.

--*** For SecondTable Adds, Changes or Deletes:
    --delete from Flank where the old value is immediately before and after the new flank value.
    --They may or may not get be assigned a new value. Let FlankUpdate figure it out.

    --execute deletes for both beforextstamp and afterxtstamp
    --then update flank

    delete *
    from flank
    where beforextstamp between (
                    select min(beforextstamp)
                    from flank
                    where beforextstamp >= '3/16/2009 10:00:46 AM'
                    ) and (
                    select max(beforextstamp)
                    from flank
                    where beforextstamp <= '3/16/2009 10:00:46 AM'
                    );

    delete *
    from flank
    where afterxtstamp between (
                    select min(afterxtstamp)
                    from flank
                    where afterxtstamp >= '3/16/2009 10:00:46 AM'
                    ) and (
                    select max(afterxtstamp)
                    from flank
                    where afterxtstamp <= '3/16/2009 10:00:46 AM'
                    );

    execute FlankUpdateLoop

--*** Final Report Query***--
    --Should execute without issues including 'deferred execution' problem.
    --Add filters as needed.
    select FirstTable.XmitID
        ,FirstTable.ReceivID
        ,ReceiverDetails.Lat
        ,ReceiverDetails.Lon
        ,BeforeTable.Latitude * (1 - ((FirstTable.RecTStamp - BeforeXTStamp) / (AfterXTStamp - BeforeXTStamp))) + AfterTable.Latitude * ((FirstTable.RecTStamp - BeforeXTStamp) / (AfterXTStamp - BeforeXTStamp)) as Xmit_Lat
        ,BeforeTable.Longitude * (1 - ((FirstTable.RecTStamp - BeforeXTStamp) / (AfterXTStamp - BeforeXTStamp))) + AfterTable.Longitude * ((FirstTable.RecTStamp - BeforeXTStamp) / (AfterXTStamp - BeforeXTStamp)) as Xmit_Lon
        ,FirstTable.RecTStamp as RecTStamp_basic
    from (((
        FirstTable
    inner join Flank on FirstTable.RecTStamp = Flank.RecTStamp)
    inner join SecondTable as BeforeTable on Flank.BeforeXTStamp = BeforeTable.XTStamp)
    inner join SecondTable as AfterTable on Flank.AfterXTStamp = AfterTable.XTStamp)
    inner join ReceiverDetails on FirstTable.ReceivID = ReceiverDetails.ReceivID
    order by FirstTable.RecTStamp;
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.