2010-01-18 14 views
36

मुझे एक डेटाबेस मिला है जो बिल्कुल सही तरीके से डिज़ाइन नहीं किया गया था, और मुझे कुछ डेटा में हेरफेर करने की आवश्यकता है। मुझे कुछ ऐसी बातें की एक अधिक सामान्य सादृश्य मैं क्या करना है देता हूँ:लिंक में पूर्ण बाहरी सम्मिलन कैसे करें?

मान लीजिए कि हम एक Student मेज, सभी वर्गों वह भाग लिया के StudentClass तालिका रिकॉर्ड रखने के लिए, और एक StudentTeacher तालिका कि सभी शिक्षकों को संग्रहीत करता है चलो जिन्होंने इस छात्र को पढ़ाया। हां, मुझे पता है कि यह एक बेवकूफ डिजाइन है और शिक्षक कक्षा को कक्षा में स्टोर करने के लिए और अधिक समझदारी होगी - लेकिन यही वह है जिसके साथ हम काम कर रहे हैं।

अब मैं डेटा साफ़ करना चाहता हूं, और मैं उन सभी स्थानों को ढूंढना चाहता हूं जहां एक छात्र के पास शिक्षक हो लेकिन कोई वर्ग, या कक्षा नहीं, लेकिन कोई शिक्षक नहीं। एसक्यूएल इस प्रकार:

select * 
from StudentClass sc 
full outer join StudentTeacher st on st.StudentID = sc.StudentID 
where st.id is null or sc.id is null 

लिंकक में आप यह कैसे करते हैं?

+11

नोट: यह वास्तव में एक पूर्ण बाहरी शामिल नहीं है - क्योंकि आप उन पंक्तियों को बाहर करना चाहते हैं जहां आंतरिक शामिल हो। मैं बस इसका जिक्र कर रहा हूं क्योंकि यह 'पूर्ण बाहरी लिनक' के लिए एक शीर्ष खोज परिणाम है - इसलिए अगर कोई ऐसा ढूंढ रहा है तो उत्तर सही नहीं हो सकता है –

+1

यह एक उच्च प्रदर्शन समाधान हो सकता है, नीचे दिए गए प्रश्न का संदर्भ लें। http: // stackoverflow।कॉम/प्रश्न/548 9987/linq-full-outer-join/8501701 # 8501701 – vrluckyin

उत्तर

28

मुझे लगता है कि मेरे पास है यहाँ का जवाब है, जो के रूप में सुंदर के रूप में मैं आशा व्यक्त की थी नहीं है, लेकिन यह चाल करना चाहिए:

var studentIDs = StudentClasses.Select(sc => sc.StudentID) 
    .Union(StudentTeachers.Select(st => st.StudentID); 
    //.Distinct(); -- Distinct not necessary after Union 
var q = 
    from id in studentIDs 
    join sc in StudentClasses on id equals sc.StudentID into jsc 
    from sc in jsc.DefaultIfEmpty() 
    join st in StudentTeachers on id equals st.StudentID into jst 
    from st in jst.DefaultIfEmpty() 
    where st == null^sc == null 
    select new { sc, st }; 

आप शायद के बाद इन दोनों बयानों निचोड़ सकता है, लेकिन मैं आपको लगता है ' डी बलिदान कोड स्पष्टता।

+3

यूनियन सेट स्वचालित रूप से चीजों को अलग करते हैं http://en.wikipedia.org/wiki/Union_(set_theory) –

+0

@ विज़नरी सॉफ्टवेयर सॉफ्टवेयर आप सही हैं - उत्तर संपादित –

+0

आपको बहुत बहुत धन्यवाद! विस्तार विधि की अवधारणा के लिए –

1

एक शुरुआत ...

var q = from sc in StudentClass 
      join st in StudentTeachers on sc.StudentID equals st.StudentID into g 
      from st in g.DefaultIfEmpty() 
      select new {StudentID = sc.StudentID, StudentIDParent = st == null ? "(no StudentTeacher)" : st.StudentID...........}; 

+0

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

+0

;-)) आपके पास लिंककैड में अधिक सुरुचिपूर्ण उदाहरण हैं। इसमें एक अच्छा डीबी कनेक्शन है और आपके पास अपने डीएलएल: एस आदि में लिंक करने की संभावनाएं हैं ... लेखक ने संक्षेप में सबसे अच्छी पुस्तक सी # भी लिखा है http://www.youtube.com/watch?v=Z6-iUNfJsJw&feature=channel – salgo60

+3

नोट करने के लिए दो बिंदु: 1) यह एक बाएं बाहरी जॉइन उत्पन्न करता है जो पूर्ण बाहरी शामिल नहीं होता है और 2) सेंट == नल चेक नहीं है linq-to-sql में आवश्यक है, इसके बजाय आप बस st.StudentID कर सकते हैं ?? "(कोई छात्र शिक्षक नहीं)" –

18
दिया 2 संग्रह एक और के लिए

, एक आवश्यक पूर्ण बाहरी में शामिल होने के साथ खेलने के लिए और अधिक नमूनों के लिए भी http://www.linqpad.net/ देखें अच्छा उपकरण निम्न के रूप में हो सकता है:

a.Union(b).Except(a.Intersect(b)); 

यदि कोई

var studentsWithoutTeachers = 
    from sc in studentClasses 
    join st in studentTeachers on sc.StudentId equals st.StudentId into g 
    from st in g.DefaultIfEmpty() 
    where st == null 
    select sc; 
var teachersWithoutStudents = 
    from st in studentTeachers 
    join sc in studentClasses on st.StudentId equals sc.StudentId into g 
    from sc in g.DefaultIfEmpty() 
    where sc == null 
    select st; 
यहाँ

() Concat का उपयोग कर एक एक पंक्ति विकल्प है:: और ख तो 2 अलग बाईं बाहरी मिलती आवश्यक हैं, एक ही प्रकार के नहीं हैं

(from l in left 
join r in right on l.Id equals r.Id into g 
from r in g.DefaultIfEmpty() 
where r == null 
select new {l, r}) 
    .Concat(
    from r in right 
    join sc in left on r.Id equals sc.Id into g 
    from l in g.DefaultIfEmpty() 
    where l == null 
    select new {l, r}); 
+0

यह एक पूर्ण अर्थपूर्ण बयान है, लेकिन यह मदद नहीं करता है, क्योंकि लिंकक में काम करने के लिए, ए और बी होना चाहिए उसी प्रकार का, जो यहां मामला नहीं है। –

+0

यह गलत है। सही बाहरी सम्मिलन कथन यहां उपलब्ध है: http://msdn.microsoft.com/en-us/library/vstudio/bb397895.aspx। शून्य जांच आवश्यक नहीं है और समूह –

+1

@grzegorz_p msdn उदाहरण से चुनने के लिए एक और चर सेट किया जाना है उदाहरण के लिए बाएं बाहरी शामिल हों। प्रश्न पूर्ण बाहरी –

17

एक्सटेंशन विधि:

public static IEnumerable<TResult> FullOuterJoin<TOuter, TInner, TKey, TResult>(this IEnumerable<TOuter> outer, IEnumerable<TInner> inner, Func<TOuter,TKey> outerKeySelector, Func<TInner,TKey> innerKeySelector, Func<TOuter,TInner,TResult> resultSelector) 
       where TInner : class 
       where TOuter : class 
      { 
       var innerLookup = inner.ToLookup(innerKeySelector); 
       var outerLookup = outer.ToLookup(outerKeySelector); 

       var innerJoinItems = inner 
        .Where(innerItem => !outerLookup.Contains(innerKeySelector(innerItem))) 
        .Select(innerItem => resultSelector(null, innerItem)); 

       return outer 
        .SelectMany(outerItem => 
         { 
          var innerItems = innerLookup[outerKeySelector(outerItem)]; 

          return innerItems.Any() ? innerItems : new TInner[] { null }; 
         }, resultSelector) 
        .Concat(innerJoinItems); 
      } 

टेस्ट:

[Test] 
public void CanDoFullOuterJoin() 
{ 
    var list1 = new[] {"A", "B"}; 
    var list2 = new[] { "B", "C" }; 

    list1.FullOuterJoin(list2, x => x, x => x, (x1, x2) => (x1 ?? "") + (x2 ?? "")) 
     .ShouldCollectionEqual(new [] { "A", "BB", "C"}); 
} 
+1

+1! मुझे लगता है कि इसे आंतरिक रूप से अनुकूलित किया जा सकता है, लेकिन फिर भी एक अच्छा जवाब है। –

1

शाऊल के जवाब के आधार पर, लेकिन एक छोटे से व्यवस्थित बनाने के साथ:

var q = 
    from id in studentIDs 
    join sc in StudentClasses on id equals sc.StudentID into jsc 
    join st in StudentTeachers on id equals st.StudentID into jst 
    where jst.Any()^jsc.Any() //exclusive OR, so one must be empty 

    //this will return the group with the student's teachers, and an empty group 
    // for the student's classes - 
    // or group of classes, and empty group of teachers 
    select new { classes = jsc, teachers = jst }; 

    //or, if you know that the non-empty group will always have only one element: 
    select new { class = jsc.DefaultIfEmpty(), teacher = jst.DefaultIfEmpty() }; 

नोट एक पूर्ण बाहरी के लिए शामिल होने कि, इस भी काम कर सकते हैं। where खंड छोड़ दें और दूसरे की बजाय ऊपर select का उपयोग करें।

संबंधित मुद्दे