5

I have two tables Student and Marks.

Student table have the following fields: StudentID,Name,MarkID(Nullable). Marks table have the following fields: MarkID,Mark

Student table

StudentID   Name    MarkID

1           Mark    1 

2           Mike    NULL

3           John    NULL

4           Paul    2

Mark table

MarkID  Mark

1       80

2       100

If I use the left join then i getting only mark and paul records. I want all the records in the left table(Student) My Query is:

   var query = (from s in Students  
               join m in Marks on s.MarkID equals m.MarkID 
               into mar from subMark in mar.DefaultIfEmpty()
               where(m.Mark > 80)
               Select s.Name)
               .ToList() 

Note: It is an Example only. While joining two tables using left join and applying where condition on the second table ,If joined column value is null in first table,it won't bring the record from first table.

5 Answers5

6

NULL comparisons are always false. That's the way SQL's three-valued logic works. If you want to match rows where the values are both null you should use a statement that checks both of them for null.

In a SQL statement you would write:

ON S.MARKID=M.MARKID OR (S.MARKID IS NULL AND M.MARKID IS NULL)

In C# you can use the comparison operator and your LINQ provider will convert this to IS NULL, eg:

on s.MarkID == m.MarkID || (s.MarkID == null && m.MarkID==null)
Panagiotis Kanavos
  • 120,703
  • 13
  • 188
  • 236
  • Sir how to check it in LINQ in C# – Sivanantham Padikkasu Jan 09 '15 at 11:34
  • In C# just compare the value to null eg `S.MARKID==null`. EF or LINQ-to-SQL will convert this to `IS NULL`. – Panagiotis Kanavos Jan 09 '15 at 11:50
  • This really works. It gets translated as most of us are used to do with optional parameters in SQL. The only thing is: If, for instance MarkID is an int, the Intellisense will complain and generate a warning because the expression will always be false, though it generates the correct SQL. Not sure how to "workaround" that. – jpgrassi May 24 '16 at 20:54
  • @jpgrassi simply don't use an int. Otherwise any query that *does* return a NULL for that int property will throw an exception. `int` in C# is the equivalent of `INT NOT NULL` in SQL. Also note that outer joins can return nulls for missing records – Panagiotis Kanavos May 25 '16 at 08:25
  • I just ran a test here. I have two tables. Restaurant and Poll. In the poll table I have an int column that holds the week number of that poll. If I want to select all restaurants and bring how many votes each have I need a left join with the Poll table. But, I need to filter to bring only votes for the current week, and of course consider restaurants that didn't had any votes. What I did was: (x.WeekNumber == weekNum || x.WeekNumber == null). For my surprise it worked. – jpgrassi May 25 '16 at 10:39
6

The problem is we use the where clause in Left join.So it will discard the null value records.

var sampleQuery= (from f in food 
            join j in juice on f.ID equals j.ID into juiceDetails from juice in juiceDetails.DefaultIfEmpty()
            where(!j.deleted)
            join fr in fruit on f.ID equals fr.ID into fruitDetails from fruit in fruitDetails.DefaultIfEmpty()
            where(!fr.deleted)
            select new
            {
            // codes

            });

Instead of this we have to check the where clause in table itself.Like this

var sampleQuery= (from f in food 
            join j in juice.Table().where(x=>!x.deleted) on f.ID equals j.ID into juiceDetails from juice in juiceDetails.DefaultIfEmpty()              
            join fr in fruit.Table().where(x=>!x.deleted) on f.ID equals fr.ID into fruitDetails from fruit in fruitDetails.DefaultIfEmpty()                
            select new
            {
            // codes

            });

It will work fine. Thank you.

3

/EDIT: My first answer was using a FULL OUTER JOIN. this was way over the top and probably wrong or not compleltly correct.

The new answer uses a LEFT OUTER JOIN. I have created some sample data using LinqPad to get a working example. Ignore the .Dump() method if you are not using LinqPad.

var Students = new List<Student>() {
        new Student() {StudentId = 1, Name ="John", MarkId = 1},
        new Student() {StudentId = 1, Name ="Paul", MarkId = 1},
        new Student() {StudentId = 1, Name ="Steve", MarkId = 1},
        new Student() {StudentId = 1, Name ="John", MarkId = 2},
        new Student() {StudentId = 1, Name ="Paul", MarkId = 3},
        new Student() {StudentId = 1, Name ="Steve", MarkId = 1},
        new Student() {StudentId = 1, Name ="Paul", MarkId = 3},
        new Student() {StudentId = 1, Name ="John"  },
        new Student() {StudentId = 1, Name ="Steve"  },
        new Student() {StudentId = 1, Name ="John", MarkId = 1}
        
    };
    
    var Marks = new List<Mark>() {
        new Mark() {MarkId = 1, Value = 60},
        new Mark() {MarkId = 2, Value = 80},
        new Mark() {MarkId = 3, Value = 100}
    };
    
    var StudentMarks = Students
                        .GroupJoin(
                            Marks,
                            st => st.MarkId,
                            mk => mk.MarkId,
                            (x,y) => new {
                                            StudentId = x.StudentId, 
                                            Name = x.Name,
                                            Mark = y.Select (z => z.Value).SingleOrDefault()
                                          }
                        )
                        .Dump();

    
}

public class Student
{
    public int StudentId { get; set; }
    public string Name { get; set; }
    public int MarkId { get; set; }
}

public class Mark
{
    public int MarkId { get; set; }
    public int Value { get; set; }
}

Output:

enter image description here

As you cann see in my Students list, there a 2 students without a MarkId. Those 2 get the default value assigned due to .SingleOrDefault(). I think this will solve your problem and gives you a good basis for further fiddeling.

references: How do you perform a left outer join using linq extension methods

Community
  • 1
  • 1
Marco
  • 22,856
  • 9
  • 75
  • 124
  • thank you. it will work fine .consider both that table has Deleted Flag.I want to check the Deleted Flag Condition (before "select new", using Where clause ).It did not bring the full records.It skips the records that containing NULL value.How to resolve this issue??(Sorry for my poor english) – Sivanantham Padikkasu Jan 09 '15 at 13:27
  • Give me a second, I will edit my answer to give you a solution with a "simple" GroupJoin. I wasn't satisfied with my answer either. – Marco Jan 09 '15 at 13:34
  • I want to check the Delete Flag in Where Clause. – Sivanantham Padikkasu Jan 09 '15 at 14:32
  • Neither you're student table, nor your mark table do have a delete flag. what are you talking about? – Marco Jan 09 '15 at 22:11
  • I want to check the deleted flag condition for both table...consider an situation both the table have the deleted flag..how to apply the where clause before selecting the value .if I put the where condition means full records did not come..hope u understand. – Sivanantham Padikkasu Jan 10 '15 at 03:34
  • No sorry, I don't. I do not know what you mean with "deleted flag" – Marco Jan 10 '15 at 07:40
  • Consider an Situation both the tables have the one more column named as "Deleted"(Bool type).i want the records that have the False in that Deleted Column.(normal deleted record Checking Condition) – Sivanantham Padikkasu Jan 12 '15 at 05:47
  • Why didn't you say so in your question. This is a different matter altogether, because you either want the deleted flag vom student, from mark, or you want to join on them. I suggest you ask another question on this topic – Marco Jan 12 '15 at 06:50
1

In your query you have written From in your Join statement while joining it. Instead you should use in::

   from s in Students  
    join m in Marks on s.MarkID equals m.ID into mar
    from subMark in mar.DefaultIfEmpty()
    Select s.Name).ToList() 
Rahul
  • 2,309
  • 6
  • 33
  • 60
0

I had the same problem. This solution only works if you have at least one row in subMark. The rows' ID doesn't matter.

var query = (from s in Students  
               join m in Marks on s.MarkID equals m.MarkID into fullM
               into mar from subMark in mar.DefaultIfEmpty()
               where(m.Mark > 80)
               Select s.Name)
               .ToList() 

the keyword into does the magic. Adding it shows all rows, also those, which have NULL-Values in mar.