Home > Back-end >  Convert SQL query to equivalent LINQ
Convert SQL query to equivalent LINQ

Time:11-09

SELECT 
    *
FROM
    T_Fighters   
LEFT OUTER JOIN T_FighterEnlistments ON
    (T_FighterEnlistments.FighterId = T_Fighters.FighterId)
LEFT OUTER JOIN T_FighterTransfers ON 
    (T_FighterTransfers.FighterId = T_Fighters.FighterId)
LEFT OUTER JOIN T_Battalions ON
    (T_Battalions.BattalionId = T_FighterEnlistments.BattalionId OR T_Battalions.BattalionId = T_FighterTransfers.BattalionId)  
LEFT OUTER JOIN T_BattalionSideServed ON
    (T_BattalionSideServed.BattalionSideServedId = T_Battalions.BattalionSideServedId)

Specifically below condition

LEFT OUTER JOIN T_Battalions ON
      (T_Battalions.BattalionId = T_FighterEnlistments.BattalionId OR T_Battalions.BattalionId = T_FighterTransfers.BattalionId)

I know rest of the part just not sure for above left join with or condition.

 from fighter in T_Fighters
    join fighterEnlistment in T_FighterEnlistments on fighter.FighterId equals fighterEnlistment.FighterId into fighterEnlistments  
                            from subFighterEnlistments in fighterEnlistments.Where(x => !x.WolfError).DefaultIfEmpty()
    join soldierTransfer in T_FighterTransfers on fighter.FighterId equals soldierTransfer.FighterId into soldierTransfers
                            from subsoldierTransfers in soldierTransfers.Where(x=>!x.WolfError).DefaultIfEmpty()        
    join battalions in T_Battalions on subsoldierTransfers.BattalionId equals battalions.BattalionId into battalions
                            from subbattalions in battalions.DefaultIfEmpty()
    join battalionsSideServed in T_RegimentSideServeds on subbattalions.RegimentSideServedId equals battalionsSideServed.RegimentSideServedId into battalionsSideServeds
                            from subbattalionsSideServeds in battalionsSideServeds.DefaultIfEmpty()

CodePudding user response:

You are almost there. What's missing is using Any for your unique left-join-with-or-on-a-joined-set:

  • Note: It seems that the original SQL query is not identical to your suggested Linq query. For one, the SQL query uses BattalionId, while the Linq query uses RegimentId. And second, the Linq query uses a filter (on WolfError) that's not present in the SQL query. My suggested query refers to the original SQL query fields, with the additional filter.

Code:

from fighter in T_Fighters
    join fighterEnlistment in T_FighterEnlistments 
    on fighter.FighterId equals fighterEnlistment.FighterId into fighterEnlistments  
        from subFighterEnlistments in fighterEnlistments.Where(x => !x.WolfError).DefaultIfEmpty()
    join fighterTransfer in T_FighterTransfers 
    on fighter.FighterId equals fighterTransfer.FighterId into fighterTransfers
        from subFighterTransfers in fighterTransfers.Where(x=>!x.WolfError).DefaultIfEmpty()        
    from t_battalion in T_Battalions 
      .Where(battalion => subFighterEnlistments.Any(x => x.BattalionId == battalion.BattalionId) || 
                          subsoldierTransfers.Any(x => x.BattalionId == battalion.BattalionId))
      .DefaultIfEmpty()
    join battalionsSideServed in T_BattalionSideServed 
    on t_battalion.BattalionSideServedId equals battalionsSideServed.BattalionSideServedId into battalionsSideServeds
        from subbattalionsSideServeds in battalionsSideServeds.DefaultIfEmpty()
select { ... something ... }

Now, since it's a bit hard to wrap our head around this without some concrete objects, here's a simplification of the above code, done in LinqPad:

void Main()
{
    var a = new [] { new A { Id = "A1", Name = "A1" },  new A { Id = "A2", Name = "A2" } };
    var b = new [] { new B { Id = "B1", Name = "B1" },  new B { Id = "B2", Name = "B2" } };
    var a1b2 = new Base[] { new A { Id = "A1", Name = "A1" },  new B { Id = "B2", Name = "B2" } };
    var b1a2 = new Base[] { new B { Id = "B1", Name = "B1" },  new A { Id = "A2", Name = "A2" } };
    var a_nulls1 = new [] { new A { Id = "A1", Name = null },  new A { Id = null, Name = "A2" } };
    var a_nulls2 = new [] { new B { Id = null, Name = "A1" },  new B { Id = "A2", Name = null } };
    
    var res = 
        from item_a in a
            join item_b in b on item_a.Id equals item_b.Id into joined_ab
                from item_joined_ab in joined_ab.DefaultIfEmpty()
            join item_a1b2 in a1b2 on item_a.Id equals item_a1b2.Id into joined_a_a1b2
                from item_joined_a_a1b2 in joined_a_a1b2.DefaultIfEmpty()
            join item_b1a2 in b1a2 on item_a.Id equals item_b1a2.Id into joined_a_b1a2
                from item_joined_a_b1a2 in joined_a_b1a2.DefaultIfEmpty()
            from item_joined_a1_nulls in a_nulls1
              .Where(a_n => joined_a_a1b2.Any(x => x.Id == a_n.Id) || 
                            joined_a_b1a2.Any(x => x.Name == a_n.Name))
              .DefaultIfEmpty()
            join item_a2_nulls in a_nulls2 on item_joined_a1_nulls.Id equals item_a2_nulls.Id into joined_a_nulls
                from item_joined_a_nulls in joined_a_nulls.DefaultIfEmpty()
        select new { item_joined_ab, item_joined_a_a1b2, item_joined_a_b1a2, item_joined_a1_nulls, item_joined_a_nulls };
        
    res.Dump();

}

class Base 
{ 
    public string Id { get; set; } 
    public string Name { get; set; } 
}
class A : Base {}
class B : Base {}

Result (just run it, ignore the html css):

Show code snippet

body{margin:0.3em 0.3em 0.4em 0.4em;font-family:Verdana;font-size:80%;background:white}p,pre{margin:0;padding:0;font-family:Verdana}table{border-collapse:collapse;border:2px solid #17b;margin:0.3em 0.2em}table.limit{border-bottom-color:#c31}table.expandable{border-bottom-style:dashed}table.error{border-bottom-width:4px}td,th{vertical-align:top;border:1px solid #aaa;padding:0.1em 0.2em;margin:0}th{text-align:left;background-color:#ddd;border:1px solid #777;font-family:tahoma;font-size:.9em;font-weight:bold}th.member{padding:0.1em 0.2em 0.1em 0.2em}td.typeheader{font-family:tahoma;font-weight:bold;background-color:#17b;color:white;padding:0 0.2em 0.15em 0.1em}td.n{text-align:right}a:link.typeheader,a:visited.typeheader,a:link.extenser,a:visited.extenser,a:link.fixedextenser,a:visited.fixedextenser{font-family:tahoma;font-size:.9em;font-weight:bold;text-decoration:none;background-color:#17b;color:white;float:left}a:link.difheader,a:visited.difheader{color:#ff8}a:link.extenser,a:visited.extenser,a:link.fixedextenser,a:visited.fixedextenser{float:right;padding-left:2pt;margin-left:4pt}span.typeglyph,span.typeglyphx{padding:0 0.2em 0 0;margin:0}span.extenser,span.extenserx,span.fixedextenser{margin-top:1.2pt}span.typeglyph,span.extenser,span.fixedextenser{font-family:webdings}span.fixedextenser{display:none;position:fixed;right:6px}td.typeheader:hover .fixedextenser{display:block}span.typeglyphx,span.extenserx{font-family:arial;font-weight:bold;margin:2px}table.group{border:none;margin:0}td.group{border:none;padding:0 0.1em}div.spacer{margin:0.6em 0}table.headingpresenter{border:none;border-left:3px dotted #1a5;margin:.8em 0 1em 0.15em}th.headingpresenter{font-family:Arial;border:none;padding:0 0 0.2em 0.5em;background-color:white;color:green;font-size:1.1em}td.headingpresenter{border:none;padding:0 0 0 0.6em}td.summary{background-color:#def;color:#024;font-family:Tahoma;padding:0 0.1em 0.1em 0.1em}td.columntotal{font-family:Tahoma;background-color:#eee;font-weight:bold;color:#17b;font-size:.9em;text-align:right}span.graphbar{background:#17b;color:#17b;margin-left:-2px;margin-right:-2px}a:link.graphcolumn,a:visited.graphcolumn{color:#17b;text-decoration:none;font-weight:bold;font-family:Arial;font-size:1.1em;letter-spacing:-0.2em;margin-left:0.1em;margin-right:0.2em}a:link.collection,a:visited.collection{color:green}a:link.reference,a:visited.reference{color:blue}i{color:green}em{color:red}.highlight{background:#ff8}.fixedfont{font-family:Consolas,monospace}code{font-family:Consolas}code.xml b{color:blue;font-weight:normal}code.xml i{color:maroon;font-weight:normal;font-style:normal}code.xml em{color:red;font-weight:normal;font-style:normal}span.cc{background:#666;color:white;margin:0 1.5px;padding:0 1px;font-family:Consolas,monospace;border-radius:3px}.difadd{background:#d3f3d3}.difremove{background:#f3d8d8}::-ms-clear{display:none}input,textarea,button,select{font-family:Verdana;font-size:1em;padding:.2em}button{padding:.2em .4em}input,textarea,select{margin:.15em 0}input[type="checkbox"],input[type="radio"]{margin:0 0.4em 0 0;height:0.9em;width:0.9em}input[type="radio"]:focus,input[type="checkbox"]:focus{outline:thin dotted red}.checkbox-label{vertical-align:middle;position:relative;bottom:.07em;margin-right:.5em}fieldset{margin:0 .2em .4em .1em;border:1pt solid #aaa;padding:.1em .6em .4em .6em}legend{padding:.2em .1em}
<div class="spacer"><table id="t1"><tr><td class="typeheader" colspan="5">(2 items)<span class="fixedextenser">4</span></td></tr><tr><th title="UserQuery B">item_joined_ab</th><th title="UserQuery Base">item_joined_a_a1b2</th><th title="UserQuery Base">item_joined_a_b1a2</th><th title="UserQuery A">item_joined_a1_nulls</th><th title="UserQuery B">item_joined_a_nulls</th></tr><tr><td><i>null</i></td><td><table id="t2"><tr><td class="typeheader" colspan="2">A<span class="fixedextenser">4</span></td></tr><tr id="sum3"><td colspan="2" class="summary">UserQuery A</td></tr><tr><th class="member" title="System.String">Id</th><td>A1</td></tr><tr><th class="member" title="System.String">Name</th><td>A1</td></tr></table></td><td><i>null</i></td><td><table id="t4"><tr><td class="typeheader" colspan="2">A<span class="fixedextenser">4</span></td></tr><tr id="sum5"><td colspan="2" class="summary">UserQuery A</td></tr><tr><th class="member" title="System.String">Id</th><td>A1</td></tr><tr><th class="member" title="System.String">Name</th><td><i>null</i></td></tr></table></td><td><i>null</i></td></tr><tr><td><i>null</i></td><td><i>null</i></td><td><table id="t6"><tr><td class="typeheader" colspan="2">A<span class="fixedextenser">4</span></td></tr><tr id="sum7"><td colspan="2" class="summary">UserQuery A</td></tr><tr><th class="member" title="System.String">Id</th><td>A2</td></tr><tr><th class="member" title="System.String">Name</th><td>A2</td></tr></table></td><td><table id="t8"><tr><td class="typeheader" colspan="2">A<span class="fixedextenser">4</span></td></tr><tr id="sum9"><td colspan="2" class="summary">UserQuery A</td></tr><tr><th class="member" title="System.String">Id</th><td><i>null</i></td></tr><tr><th class="member" title="System.String">Name</th><td>A2</td></tr></table></td><td><i>null</i></td></tr></table></div>
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

(Of course, if we'll change one of the Ids in the last join to be Name instead, we'll get a B to be included in the last column).

  • Related