I have a SQL query that works perfectly that I need to convert to Linq. I need to return all the records one table and join it to a second table. I need to return all of the results from the first table joined with results from the second table where the value of a specific field in the second table equals a variable value (75 in the example below) or returns null.
So the total number of rows in the result should be the total number of rows from table1. The part of the row from the join from table2 should either show values from table2 where a record existed with a value of 75 or null where the record doesn't exist.
EDIT: I should mention that t1.field1 is an int and t2.field1 is a nullable int.
I tried multiple linq statements, grouping joins, asking coworkers, and googling til my eyes bleed. I'm stuck. I realize my question wording may not be clear, and I apologize in advance if it isn't.
Thanks in advance.
Chris
The SQL Query:
SELECT *
FROM table1 AS t1 LEFT OUTER JOIN
table2 AS t2 ON t1.field1 = t2.field1 AND t2.field2 = 75
Use DefaultIfEmpty - see LINQ - Left Join, Group By, and Count and http://msdn.microsoft.com/en-us/library/bb397895.aspx for samples of how to achieve this
An answer that works but isn't as elegant as I'd expected:
var q = from item1 in table1
join item2 in table2 on new { Field1=(int?)item1.Field1, Field2=75 }
equals new { item2.Field1, item2.Field2 } into gj
from subItem2 in gj.DefaultIfEmpty()
select new { F1= item1.Field1, F2 = ( subItem2 == null ? string.Empty : subItem2.Field2.ToString()) };
I couldn't see where to fit the where clause in (Field2 = 75) so instead went with the composite key between the tables to achieve the same effect.
Second bit of ugliness is the cast to the nullable int because or Field1 in the composite key so it can be equated to the corresponding field on table 2.
Obviously you return in the anonymous type whatever values you're interested in. Note you can't return null which is why I showed a string representation of Field2 just so you can see what is returned from the second table.
Thank you for your responses. Because I need some nulls, but only the ones where the specific id is null and not all values that are null, none of the solutions above will work. It seems that to do this in linq will be very tough if it is possible.
So, in the interest of time, I decided to take the SQL query that worked and turn it into a stored procedure and function import. I feel like that's probably not the right way to do it, but time is always a factor.
Thanks again for your responses.
Related
I have table A, B, C,D.
Table D have forgienKey relationship columns(A1,B1,C1) with table A, B, C and Column C1 in table D is nullable column, because of it While loading table D using Include() nullable column row got skipped as below
_context.Entry(F).Collection(x=>x.D).Query().Include(x=>x.A).Include(x=>x.B).Include(x=>x.C).Load();
I could able to get value for Id's 1,3,4 using above Query but full row ID 2, 5 is not in the collection list because of the null value.
If C1 has null value I should get remaining column information. Help needed plz
The most obvious reason for me, why your data for some of the C columns are not coming is because the Include method which you are using, underneath is acting as join operations between tables, and as you know "join" will not bring results for two tables if they don't match with PK->FK relations.
One of the suggestions here is to call the SQL Stored Procedure.
Another suggestion is to use a kind of similar approach to LEFT OUTER JOIN using LINQ.
Here is an excellentemphasized text post about how to do that.
I have two table in sql. Document and User. Document have relation to User and I want to get users that I sent document recently.
I need to sort by the date document was sent and get unique (distinct) user with relation to this document
This is my linq queries
var recentClients = documentCaseRepository.Entities
.Where(docCase => docCase.AssignedByAgentId == WC.UserContext.UserId)
.OrderByDescending(userWithDate => userWithDate.LastUpdateDate)
.Take(1000) // I need this because if I comment this line then EF generate completely different sql query.
.Select(doc => new { doc.AssignedToClient.Id, doc.AssignedToClient.FirstName, doc.AssignedToClient.LastName })
.Distinct()
.Take(configuration.MaxRecentClientsResults)
.ToList();
and generated sql query is:
SELECT DISTINCT TOP(5) [t].*
FROM (
SELECT TOP(1000) [docCase.AssignedToClient].[Id]
FROM [DocumentCase] AS [docCase]
INNER JOIN [User] AS [docCase.AssignedToClient]
ON ([docCase].[AssignedToClientId] = [docCase.AssignedToClient].[Id])
WHERE [docCase].[AssignedByAgentId] = 3
ORDER BY [docCase].[LastUpdateDate] DESC
)
AS [t]
Every thing is correct for now. But if I delete this line
.Take(1000) // I need this because...
EF generated completely different query such as:
SELECT DISTINCT TOP(5)
[docCase.AssignedToClient].[Id]
FROM [DocumentCase] AS [docCase]
INNER JOIN [User] AS [docCase.AssignedToClient]
ON ([docCase].[AssignedToClientId] = [docCase.AssignedToClient].[Id])
WHERE [docCase].[AssignedByAgentId] = 3
My question is: why EF not generated orderby clause and subquery with distinct?
This is a BUG EF or I'm doing something wrong? And what I must do to generate in linq this sql query ()
SELECT DISTINCT TOP 5 [t].*
FROM ( SELECT [docCase.AssignedToClient].[Id]
FROM [DocumentCase] AS [docCase]
INNER JOIN [User] AS [docCase.AssignedToClient]
ON [docCase].[AssignedToClientId] = [docCase.AssignedToClient].[Id]
WHERE [docCase].[AssignedByAgentId] = 1
ORDER BY [docCase].[LastUpdateDate] DESC
) AS [t]
OrderBy information not always retained across other operators such as Distinct. Entity Framework does not document (to my knowledge) how exactly OrderBy is propagated.
This kind of makes sense because some operators have undefined output order. The fact that ordering is retained in many situations is a convenience for the developer.
Move the OrderBy to the end of the query (or at least past the Distinct).
The reason for the difference in queries is that Distinct messes up result order. So when you first execute OrderBy and then Distinct, you can just es well not execute OrderBy, because this order is lost anyway. So EF can just optimize it away.
Calling Take in between causes the result set to be semantically different: You first order the items, take the first 1000 items of that order and then call Distinct on them.
What you can change in your query depends mainly on the result you want to achieve. Maybe you want to first make the result set distinct then order by date and finally take the amount of items. Other options are also thinkable based on your requirements.
I have a stored procedure and I want to average null columns.
This is my stored procedure :
SELECT
AVG(planned) AS Planned,
AVG(achieved) AS Achieved
FROM
Port
INNER JOIN
Technology ON Port.portID = Technology.portRef
I bind this stored procedure to a chart using datasource and when the column is null the C# code throws this error:
Value was either too large or too small for a Decimal.
How can I handle my stored procedure to avg those null columns?
This happens when the query does not return any values. Use this
SELECT
coalesce(avg(planned),0) as Planned,
coalesce(avg(achieved),0) as Achieved
FROM
Port inner join Technology on Port.portID = Technology.portRef
another way
SELECT avg(isnull(planned,0))as Planned,avg(isnull(achieved,0)) as Achieved
FROM Port inner join Technology on Port.portID = Technology.portRef
I have seen several people getting confused whether to use ISNULL or COALESCE.
I would strongly recommend COALESCE. In some cases both will run fine, but in some cases ISNULL if not properly handled will give wrong output.
Please check the below snippet :
SELECT
ISNULL(Nullif('test', 'test'), '12345') AS using_isnull,
COALESCE(Nullif('test', 'test'), '12345') AS using_coalesce,
ISNULL(Nullif('test', 'test'), 12345) AS int_using_isnull,
COALESCE(Nullif('test', 'test'), 12345) AS int_using_coalesce
Use the keyword unpivot
select id, AVG(Q)
from (select * from myTable) a
unpivot(Q for QQ IN(Q1,Q2,Q3,Q4,Q5)) b
group by id
If all the columns are null, it won't return anything though.
I have 2 tables
TableA:
TableAID int,
Col1 varchar(8)
TableB:
TableBID int
Col1 char(8),
Col2 varchar(40)
When I run a SQL query on the 2 tables it returns the following number of rows
SELECT * FROM tableA (7200 rows)
select * FROM tableB (28030 rows)
When joined on col1 and selects the data it returns the following number of rows
select DISTINCT a.Col1,b.Col2 FROM tableA a
join tableB b on a.Col1=b.Col1 (6578 rows)
The above 2 tables on different databases so I created 2 EF models and retried the data separately and tried to join them in the code using linq with the following function. Surprisingly it returns 2886 records instead of 6578 records. Am I doing something wrong?
The individual lists seems to return the correct data but when I join them SQL query and linq query differs in the number of records.
Any help on this greatly appreciated.
// This function is returning 2886 records
public List<tableC_POCO_Object> Get_TableC()
{
IEnumerable<tableC_POCO_Object> result = null;
List<TableA> tableA_POCO_Object = Get_TableA(); // Returns 7200 records
List<TableB> tableB_POCO_Object = Get_TableB(); // Returns 28030 records
result = from tbla in tableA_POCO_Object
join tblb in tableB_POCO_Object on tbla.Col1 equals tblb.Col1
select new tableC_POCO_Object
{
Col1 = tblb.Col1,
Col2 = tbla.Col2
};
return result.Distinct().ToList();
}
The problem lies in the fact that in your POCO world, you're trying to compare two strings using a straight comparison (meaning it's case-sensitive). That might work in the SQL world (unless of course you've enabled case-sensitivity), but doesn't quite work so well when you have "stringA" == "StringA". What you should do is normalize the join columns to be all upper or lower case:
join tblb in tableB_POCO_Object on tbla.Col1.ToUpper() equals tblb.Col1.ToUpper()
Join operator creates a lookup using the specified keys (starts with second collection) and joins the original table/collection back by checking the generated lookup, so if the hashes ever differ they will not join.
Point being, joining OBJECT collections on string data/properties is bad unless you normalize to the same cAsE. For LINQ to some DB provider, if the database is case-insensitive, then this won't matter, but it always matters in the CLR/L2O world.
Edit: Ahh, didn't realize it was CHAR(8) instead of VARCHAR(8), meaning it pads to 8 characters no matter what. In that case, tblb.Col1.Trim() will fix your issue. However, still keep this in mind when dealing with LINQ to Objects queries.
This might happen because you compare a VARCHAR and a CHAR column. In SQL, this depends on the settings of ANSI_PADDING on the sql server, while in C# the string values are read using the DataReader and compared using standard string functions.
Try tblb.Col1.Trim() in your LINQ statement.
As SPFiredrake correctly pointed out this can be caused by case sensitivity, but I also have to ask you why did you write your code in such a way, why not this way:
// This function is returning 2886 records
public List<tableC_POCO_Object> Get_TableC()
{
return from tbla in Get_TableA()
join tblb in Get_TableB() on tbla.Col1 equals tblb.Col1
select new tableC_POCO_Object
{
Col1 = tblb.Col1,
Col2 = tbla.Col2
}.Distinct().ToList();
}
where Get_TableA() and Get_TableB() return IEnumerable instead of List. You have to watch out for that, because when you convert to list the query will be executed instantly. You want to send a single query to the database server.
I'm trying to better utilize the resources of the Entity Sql in the following scenario: I have a table Book which has a Many-To-Many relationship with the Author table. Each book may have from 0 to N authors. I would like to sort the books by the first author name, ie the first record found in this relationship (or null when no authors are linked to a book).
With T-SQL it can be done without difficulty:
SELECT
b.*
FROM
Book AS b
JOIN BookAuthor AS ba ON b.BookId = ba.BookId
JOIN Author AS a ON ba.AuthorId = a.AuthorId
ORDER BY
a.AuthorName;
But I cannot think of how to adapt my code bellow to achieve it. Indeed I don't know how to write something equivalent directly with Entity Sql too.
Entities e = new Entities();
var books = e.Books;
var query = books.Include("Authors");
if (sorting == null)
query = query.OrderBy("it.Title asc");
else
query = query.OrderBy("it.Authors.Name asc"); // This isn't it.
return query.Skip(paging.Skip).Take(paging.Take).ToList();
Could someone explain me how to modify my code to generate the Entity Sql for the desired result? Or even explain me how to write by hand a query using CreateQuery<Book>() to achieve it?
EDIT
Just to elucidate, I'll be working with a very large collection of books (around 100k). Sorting them in memory would be very impactful on the performance. I wish the answers would focus on how to generate the desired ordering using Entity Sql, so the orderby will happens on the database.
The OrderBy method expects you to give it a lambda expression (well, actually a Func delegate, but most people would use lambdas to make them) that can be run to select the field to sort by. Also, OrderBy always orders ascending; if you want descending order there is an OrderByDescending method.
var query = books
.Include("Authors")
.OrderBy(book => book.Authors.Any()
? book.Authors.FirstOrDefault().Name
: string.Empty);
This is basically telling the OrderBy method: "for each book in the sequence, if there are any authors, select the first one's name as my sort key; otherwise, select the empty string. Then return me the books sorted by the sort key."
You could put anything in place of the string.Empty, including for example book.Title or any other property of the book to use in place of the last name for sorting.
EDIT from comments:
As long as the sorting behavior you ask for isn't too complex, the Entity Framework's query provider can usually figure out how to turn it into SQL. It will try really, really hard to do that, and if it can't you'll get a query error. The only time the sorting would be done in client-side objects is if you forced the query to run (e.g. .AsEnumerable()) before the OrderBy was called.
In this case, the EF outputs a select statement that includes the following calculated field:
CASE WHEN ( EXISTS (SELECT
1 AS [C1]
FROM [dbo].[BookAuthor] AS [Extent4]
WHERE [Extent1].[Id] = [Extent4].[Books_Id]
)) THEN [Limit1].[Name] ELSE #p__linq__0 END AS [C1],
Then orders by that.
#p__linq__0 is a parameter, passed in as string.Empty, so you can see it converted the lambda expression into SQL pretty directly. Extent and Limit are just aliases used in the generated SQL for the joined tables etc. Extent1 is [Books] and Limit1 is:
SELECT TOP (1) -- Field list goes here.
FROM [dbo].[BookAuthor] AS [Extent2]
INNER JOIN [dbo].[Authors] AS [Extent3] ON [Extent3].[Id] = [Extent2].[Authors_Id]
WHERE [Extent1].[Id] = [Extent2].[Books_Id]
If you don't care where the sorting is happening (i.e. SQL vs In Code), you can retrieve your result set, and sort it using your own sorting code after the query results have been returned. In my experience, getting specialized sorting like this to work with Entity Framework can be very difficult, frustrating and time consuming.