2013-02-25 19 views
11

मैं मेज एक और टेबल बी शामिल होने के लिए आईडी के लिए तालिका सीअतिव्यापी शामिल होने की तिथि पर्वतमाला

तालिका A और टेबल बी दुकान स्थिति झंडे बनाना होगा। स्थिति झंडे (ए_फ्लैग और बी_फ्लैग) समय-समय पर बदल सकते हैं, इसलिए एक आईडी में कई पंक्तियां हो सकती हैं, जो आईडी की स्थिति के इतिहास का प्रतिनिधित्व करती हैं। किसी विशेष आईडी के लिए झंडे एक-दूसरे से स्वतंत्र रूप से बदल सकते हैं, जिसके परिणामस्वरूप तालिका ए में एक पंक्ति में तालिका बी में कई पंक्तियों से संबंधित हो सकता है, और इसके विपरीत।

परिणामी तालिका (तालिका सी) को आईडी दिनांक (01/01/2008-18/08/2008) के भीतर प्रत्येक तिथि को कवर करने वाली अद्वितीय तिथि सीमाओं की सूची होना चाहिए, और प्रत्येक दिनांक सीमा के लिए ए_फ्लैग और बी_फ्लैग मान ।

वास्तविक सारणी में प्रत्येक आईडी के साथ सैकड़ों आईडी होती हैं जिसमें प्रति तालिका पंक्तियों की एक अलग संख्या होती है।

मेरे पास अंतिम परिणाम प्राप्त करने के लिए SQL और SAS टूल तक पहुंच है।

Source - Table A 
ID Start   End  A_Flag 
1 01/01/2008 23/03/2008 1 
1 23/03/2008 15/06/2008 0 
1 15/06/2008 18/08/2008 1 

Source - Table B 
ID Start   End  B_Flag 
1 19/01/2008 17/02/2008 1 
1 17/02/2008 15/06/2008 0 
1 15/06/2008 18/08/2008 1 

Result - Table C 
ID Start   End A_Flag B_Flag 
1 01/01/2008 19/01/2008 1 0 
1 19/01/2008 17/02/2008 1 1 
1 17/02/2008 23/03/2008 1 0 
1 23/03/2008 15/06/2008 0 0 
1 15/06/2008 18/08/2008 1 1 
+0

मैं इसे केवल मानक एसक्यूएल के साथ पूरा करने के तरीके के बारे में नहीं सोच सकता और मुझे एसएएस नहीं पता है। अगर मुझे पता चले कि कौन सा स्वाद है, तो मुझे एक ऐसी प्रक्रिया लिखने में सक्षम होना चाहिए जो काम करेगा। – pahoughton

+0

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

उत्तर

3

मैं यह सोचते हैं आप एक समारोह lag (SQL सर्वर 2012, ओरेकल, Postgres, डीबी 2) कहा जाता है कि SQL में इस को हल करने के लिए जा रहा हूँ,। आप एक सहसंबंधित सबक्वायरी के साथ एक ही प्रभाव प्राप्त कर सकते हैं।

विचार सभी अलग-अलग समयावधि प्राप्त करना है। फिर झंडे पाने के लिए मूल सारणी में वापस शामिल हों।

मुझे कोड अपलोड करने में परेशानी हो रही है, लेकिन इसमें से अधिकांश प्राप्त कर सकते हैं। हालांकि, यह प्रारंभिक सिरों से शुरू होता है, जिसे आप एक कॉलम में चार तिथियों के union (union all नहीं) द्वारा बनाते हैं: a.start को दिनांक के रूप में चुनें। इसके बाद इसे a.end, b.start, और b.end के साथ जोड़ा जाता है।

with driver as (
    select thedate as start, lag(thedate) over (order by thedate) as end 
    from startends 
    )  

select startdate, enddate, a.flag, b.flag 
from driver left outer join 
    a 
    on a.start >= driver.start and a.end <= driver.end left outer join 
    b 
    on b.start >= driver.start and b.end <= driver.end 
+0

गॉर्डन, इसके लिए धन्यवाद। मेरे पास केवल प्रोक एसक्यूएल (एसएएस के भीतर एक प्रक्रिया) के माध्यम से एसक्यूएल सिंटैक्स तक पहुंच है, इसलिए एक सहसंबंधित उप क्वेरी संभव है लेकिन अंतराल समारोह नहीं है। इस मधुमक्खी को सहसंबंधित उप क्वेरी का उपयोग करके कैसे किया जा सकता है? – geebees

+0

यदि आपके पास ओरेकल (वह समर्थन अंतराल) जैसे अंतर्निहित डेटाबेस है, तो आप पास-थ्रू मोड सेट अप कर सकते हैं और ओरेकल सिंटैक्स में डाल सकते हैं। क्या यह एक संभावना है? –

+0

एसएएस के पास एक अंतराल कार्य है, मुझे लगता है कि मुझे इसके वांछित परिणाम – geebees

0

इसका एक संभावित एसएएस समाधान आंशिक जुड़ने के लिए है, और फिर डेटा चरण में आवश्यक अतिरिक्त पंक्तियां बनाना है। यह माना जाता है कि टेबल ए के सभी संभावित रिकॉर्ड हैं; अगर ऐसा नहीं है (यदि tableB tableA से पहले शुरू हो सकता है), तो उस संभावना पर विचार करने के लिए कुछ अतिरिक्त तर्क की आवश्यकता हो सकती है (यदि first.id और gt b_start प्रारंभ करें)। उदाहरण डेटा में मौजूद मुद्दों के लिए अतिरिक्त तर्क भी हो सकते हैं - मेरे पास आज सुबह बहुत समय नहीं है और उदाहरण डेटा मामलों से परे किसी भी चीज़ के लिए इसे डीबग नहीं किया है, लेकिन अवधारणा स्पष्ट होनी चाहिए।

data tableA; 
informat start end DDMMYY10.; 
format start end DATE9.; 
input ID Start   End  A_Flag; 
datalines; 
1 01/01/2008 23/03/2008 1 
1 23/03/2008 15/06/2008 0 
1 15/06/2008 18/08/2008 1 
;;;; 
run; 

data tableB; 
informat start end DDMMYY10.; 
format start end DATE9.; 
input ID Start   End  B_Flag; 
datalines; 
1 19/01/2008 17/02/2008 1 
1 17/02/2008 15/06/2008 0 
1 15/06/2008 18/08/2008 1 
;;;; 
run; 


proc sql; 
create table c_temp as 
    select * from tableA A 
     left join (select id, start as b_start, end as b_end, b_flag from tableB) B 
    on A.Id = B.id 
    where (A.start le B.b_start and A.end gt B.b_start) or (A.start lt B.b_end and A.end ge B.b_end) 
    order by A.ID, A.start, B.b_start; 
quit; 

data tableC; 
set c_temp; 
by id start; 
retain b_flag_ret; 
format start_fin end_fin DATE9.; 
if first.id then b_flag_ret=0; 
do until (start=end); 
    if (start lt b_start) and first.start then do; 
     start_fin=start; 
     end_fin=b_start; 
     a_flag_fin=a_flag; 
     b_flag_fin=b_flag_ret; 
     output; 
     start=b_start; 
    end;  
    else do; *start=b_start; 
      start_fin=ifn(start ge b_start, start, b_start); 
      end_fin = ifn(b_end le end, b_end, end); 
      a_flag_fin=a_flag; 
      b_flag_fin=b_flag; 
      output; 
      start=end; *leave the loop as there will be a later row that matches; 
    end; 
end; 
run; 
0

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

इसके अलावा, डेटा चरण के आधार पर समाधान बहुत ही कुशल होते हैं। यह सिद्धांत में ओ (एन लॉग एन) समय में चलता है, लेकिन अभ्यास में ओ (एन) के करीब, और निरंतर अंतरिक्ष में।

पहले दो डेटा चरण केवल डेटा लोड कर रहे हैं, जो कि उत्तर से थोड़ा संशोधित है, कई आईडी (अन्यथा सिंटैक्स बहुत आसान है) और कुछ कोने के मामलों को जोड़ने के लिए, यानी, एक आईडी जिसके लिए यह निर्धारित करना असंभव है प्रारम्भिक अवस्था।

data tableA; 
informat start end DDMMYY10.; 
format start end DATE9.; 
input ID Start   End  A_Flag; 
datalines; 
1 01/01/2008 23/03/2008 1 
2 23/03/2008 15/06/2008 0 
2 15/06/2008 18/08/2008 1 
;;;; 
run; 

data tableB; 
informat start end DDMMYY10.; 
format start end DATE9.; 
input ID Start   End  B_Flag; 
datalines; 
1 19/01/2008 17/02/2008 1 
2 17/02/2008 15/06/2008 0 
4 15/06/2008 18/08/2008 1 
;;;; 
run; 

अगले डेटा कदम प्रत्येक आईडी और ध्वज के लिए पहले संशोधन पाता है और यह क्या पाया के विपरीत करने के लिए प्रारंभिक मूल्य निर्धारित करता है।

/* Get initial state by inverting first change */ 
data firstA; 
    set tableA; 
    by id; 
    if first.id; 
    A_Flag = ~A_Flag; 
run; 

data firstB; 
    set tableB; 
    by id; 
    if first.id; 
    B_Flag = ~B_Flag; 
run; 
data first; 
    merge firstA firstB; 
    by id; 
run; 

अगले डेटा कदम अन्य दो के साथ कृत्रिम "पहली" तालिका विलीन हो जाती है, पिछले राज्य में जाना जाता है को बनाए रखना और कृत्रिम प्रारंभिक पंक्ति को त्यागकर।

data tableAB (drop=lastA lastB); 
    set first tableA tableB; 
    by id start; 
    retain lastA lastB lastStart; 
    if A_flag = . and ~first.id then A_flag = lastA; 
    else lastA = A_flag; 
    if B_flag = . and ~first.id then B_flag = lastB; 
    else lastB = B_flag; 
    if ~first.id; /* drop artificial first row per id */ 
run; 

ऊपर दिए गए कदम लगभग सबकुछ करते हैं। एकमात्र बग यह है कि अंत तिथियां गलत होंगी, क्योंकि उन्हें मूल पंक्ति से कॉपी किया गया है। इसे ठीक करने के लिए, प्रत्येक पंक्ति के अंत में अगली शुरुआत की प्रतिलिपि बनाएँ, जब तक कि यह अंतिम पंक्ति न हो। सबसे आसान तरीका प्रत्येक आईडी को रिवर्स स्टार्ट से सॉर्ट करना है, एक रिकॉर्ड वापस देखें, फिर अंत में फिर से आरोही क्रमबद्ध करें।

/* sort descending to ... */ 
proc sort data=tableAB; 
    by id descending start; 
run; 
/* ... copy next start to this row's "end" field if not final */ 
data tableAB(drop=nextStart); 
    set tableAB; 
    by id descending start; 
    nextStart=lag(start); 
    if ~first.id then end=nextStart; 
run; 

proc sort data=tableAB; 
    by id start; 
run; 
3

आपके द्वारा उत्पन्न की गई समस्या को गैर-मानक एक्सटेंशन के बिना एक SQL कथन में हल किया जा सकता है।

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

मैंने आपके Canonical SQL पृष्ठ पर अपना उदाहरण (और एक समाधान) जोड़ा। विस्तृत चर्चा के लिए वहां देखें। एसओ के लिए निष्पक्षता में, यहां क्वेरी स्वयं

with D (ID, bound) as (
    select ID 
     , case T when 's' then StartDate else EndDate end as bound 
    from (
    select ID, StartDate, EndDate from so.A 
    UNION 
    select ID, StartDate, EndDate from so.B 
    ) as U 
    cross join (select 's' as T union select 'e') as T 
) 
select P.*, a.Flag as A_Flag, b.Flag as B_Flag 
from (
    select s.ID, s.bound as StartDate, min(e.bound) as EndDate 
    from D as s join D as e 
    on s.ID = e.ID 
    and s.bound < e.bound 
    group by s.ID, s.bound 
) as P 
left join so.A as a 
on P.ID = a.ID 
and a.StartDate <= P.StartDate and P.EndDate <= a.EndDate 
left join so.B as b 
on P.ID = b.ID 
and b.StartDate <= P.StartDate and P.EndDate <= b.EndDate 
order by P.ID, P.StartDate, P.EndDate