2016-10-06 9 views
5

Mam stosunkowo prostą F# wyrażenie kwerendy z sprzężenia:leftOuterJoin i `.DefaultIfEmpty()` zapytań w F #

let mdrQuery = 
    query { 
     for header in db.CustomerDetails do 
     leftOuterJoin row in db.MDR_0916 
      on (header.PID = row.PID) into result 
     select (result, header) 
     } 

ta zwraca każdy header i result ale dla header że nie pasuje w row, result jest po prostu pustą sekwencją, a gdy wyniki zapytania zostaną przekazane do niestandardowego typu, pojawia się błąd, że konstruktor powiązany z polem w row nie jest zdefiniowany. Ma to sens, podobnie jak w przypadku każdego header, który nie ma odpowiednika w row, zwracana jest sekwencja . Przykład:

mdrQuery |> Seq.head;; 
val it : 
    seq<dbSchema.ServiceTypes.MDR_0916> * dbSchema.ServiceTypes.CustomerDetails 
= (seq [null], CustomerDetails {ACCOUNTMANAGER = null; 
          ACCOUNTSTATUS = "XC"; 
          ADDRESSLINE1 = null; 
          ADDRESSLINE2 = null; 
          ADDRESSLINE3 = null; 
          ADDRESSLINE4 = "123 PIG ROAD"... 

Podejrzewam, że istnieje sposób obejścia tego powodu the leftOuterJoin documentation here. Ale gdy próbuję przy użyciu tego przykładu jako szablon do moim zapytania:

let mdrQuery = 
    query { 
     for header in db.CustomerDetails do 
     leftOuterJoin row in db.MDR_0916 
      on (header.PID = row.PID) into result 
     for row in result.DefaultIfEmpty() do 
     select (result, header) 
     } 

się .DefaultIfEmpty() kawałek błędów zewnątrz z

error FS0039: The field, constructor or member 'DefaultIfEmpty' is not defined 

Czy istnieje sposób, że mogę zrobić to przyłączyć się zdarzyć i wybierz każdy wiersz, wypełniając niedopasowane wiersze w result za pomocą None (lub jakiejś innej pustej wartości zerowej SQL), aby całość zapytania mogła zostać przekazana do mojego typu rekordu?

Idealnie, wyjście na niezrównaną rzędu byłoby coś jak (skrócony wyników tworzonych ręcznie poniżej)

mdrQuery |> Seq.head;; 
val it : 
    seq<dbSchema.ServiceTypes.MDR_0916> * dbSchema.ServiceTypes.CustomerDetails 
= (MDR_0916 {AIMExp = null; 
     AP = null; 
     APComp = null; 
     APEng = null; 
     APFine = null; 
     APForl = null;...}, 
CustomerDetails {ACCOUNTMANAGER = null; 
          ACCOUNTSTATUS = "XC"; 
          ADDRESSLINE1 = null; 
          ADDRESSLINE2 = null; 
          ADDRESSLINE3 = null; 
          ADDRESSLINE4 = "123 PIG ROAD"... 

EDIT:This question/answer jest podobny do kopalni, ale w tym ToOption result prostu wyprowadza Some (seq [null]).

+1

W twoim interaktywnym wyjściu 'wynik' nie jest pustą sekwencją, ale raczej sekwencją jednego elementu, a ten element to' null'. –

+0

Dzięki za wyjaśnienia. – Steven

+4

'DefaultIfEmpty' jest metodą rozszerzenia, więc musisz' otworzyć System.Linq'. – kvb

Odpowiedz

0

Dokumentacja jest nieprawidłowa; w C# nie ma bezpośredniego odpowiednika operatora leftOuterJoin, więc DefaultIfEmpty jest używany z normalnym łączeniem, ale w F # nie potrzebujesz tego (konstruktor zapytań wykonuje to tłumaczenie dla Ciebie - zobacz QueryBuilder.LeftOuterJoin w source jeśli jesteś ciekawy).

Jeśli chcesz wyniki tradycyjnego LEFT JOIN, a potem po prostu dodać dodatkowy for pętlę bezDefaultIfEmpty (ale uwaga - chcesz wybrać nowo związany wartość row, nie sekwencji result):

let mdrQuery = 
    query { 
     for header in db.CustomerDetails do 
     leftOuterJoin row in db.MDR_0916 
      on (header.PID = row.PID) into result 
     for row in result do 
     select (row, header) 
    } 

Zauważ, że to daje null wartości MDR_0916 wpisów, które zostały brakuje, a nie szczególnych walorach MDR_0916 z null wartości pól, choć, więc może chcesz zastosować etap przetwarzania końcowego, jeśli potrzebujesz tego drugiego.

+0

Doskonała odpowiedź i podziękowania za sugestię zastosowania kroku przetwarzania końcowego do utworzenia wartości 'MDR_0916' z wartościami pola" null ". Skończyło się na tym, że wybrałem tylko te pola z 'MDR ...', które były mi potrzebne i przepisałem, jak przekazałem je do niestandardowego typu. – Steven