I am using EF4.0, and I wrote a query:
var query = context.Post.Where(p => p.Id == postId).SingleOrDefault();
I need only One post from this query. I thought SingleOrDefault() will generate "SELECT TOP(1) ...", but when I look into SQL Profiler, It was:
exec sp_executesql N'SELECT TOP (2)
[Extent1].[Id] AS [Id],
[Extent1].[Title] AS [Title],
[Extent1].[Slug] AS [Slug],
[Extent1].[PubDate] AS [PubDate],
[Extent1].[PostContent] AS [PostContent],
[Extent1].[Author] AS [Author],
[Extent1].[CommentEnabled] AS [CommentEnabled],
[Extent1].[AttachmentId] AS [AttachmentId],
[Extent1].[IsPublished] AS [IsPublished],
[Extent1].[Hits] AS [Hits],
[Extent1].[CategoryId] AS [CategoryId]
FROM [dbo].[Post] AS [Extent1]
WHERE [Extent1].[Id] = #p__linq__0',N'#p__linq__0 uniqueidentifier',#p__linq__0='ECD9F3BE-3CA9-462E-AE79-2B28C8A16E32'
I wonder why EF result in SELECT TOP (2)? I only need one post.
It selects top 2 so that if there are actually 2 or more than 2 records in the database, an exception would be thrown. If it only selects top 1 there would be no way to error out.
By asking for the SingleOrDefault of a sequence, you are asking for this behaviour:
if the sequence has exactly 0 elements, return the default for the sequence's element type
if the sequence has exactly 1 element, return the element
if the sequence has more than 1 element, throw
Doing a TOP (1) would empower the first two parts of this, but not the third. Only by doing a TOP (2) can we differentiate between exactly 1 record and more than 1 record.
If you don't want or need the third part of the above behviour, instead use FirstOrDefault.
Related
I'm finding that EF6 is generating different SQL when I need to get a count from a table vs a view.
Let's assume I have a table in SQL Server called Students. Assume there's a view that filters that table; we'll call it CurrentStudents.
When I use EF6 to get the count of the table, the query that EF creates is quite different from what it generates when I query the view count. I'm trying to understand why it does it differently.
I have example code below.
Looking at the table count code, we have:
context.Students.Where(x => x.Status == 1).Count()
That generates the EF query:
SELECT [GroupBy1].[A1] AS [C1]
FROM (SELECT
COUNT(1) AS [A1]
FROM [STUDENTS] AS [Extent1]
WHERE 1 = [Extent1].STATUS)
AS [GroupBy1]
EF generates the Count SQL and wraps it in another SELECT. No problem with that.
Now contrast to just changing the code to get the view count:
context.CurrentStudents.Where(x => x.Status == 1).Count()
Generated view query:
SELECT [GroupBy1].[A1] AS [C1]
FROM (SELECT
COUNT(1) AS [A1]
FROM (SELECT [STUDENTS].[ID] as [ID],
[STUDENTS].[STATUS] AS [STATUS],
[STUDENTS].[FIRSTNAME] AS [FIRSTNAME],
[STUDENTS].[LASTNAME] AS [LASTNAME],
[STUDENTS].[AGE] AS [AGE],
[STUDENTS].[MAJOR] AS [MAJOR],
[STUDENTS].[MINOR] AS [MINOR],
[STUDENTS].[DORM] AS [DORM],
[STUDENTS].[GRADDATE] AS [GRADDATE]
FROM [CURRENTSTUDENTS] AS [CURRENTSTUDENTS])
) AS [Extent1]
WHERE 1 = [Extent1].STATUS)
AS [GroupBy1]
The EF query in this case has added another nested SELECT, which is selecting every column that is available in the view. I'm not sure why EF has decided to add that extra level of verbosity just because a view is the count source. Just wondering if anyone has a better understanding of what's going on behind the scenes here.
I have a subquery like this
with subquery as (select host from table_A where << some condition >>)
and in my main query, I am querying data from another table called table_B, and one of the columns is called destination_host. Now I need to check if the destination_host is in the list returned from my subquery, then I want to output TypeA in my select statement or else TypeB. My select statement looks something like
select name, place, destination_host
from table_B
where <<some condition>>
I want to output a fourth column that is based on a condition check, let's say we call this host_category and if the destination_host value exists in the subquery then I want to add value typeA or else typeB. Please can you help me understand how to write this. I understand that it is hard to provide guidance if you don't have actual data to work with.
I tried using case statements such as this one:
when (destination_host in (select host from subquery)) THEN 'typeA'
when (destination_host not in (select host from subquery)) THEN 'typeB'
end as host_category
but I don't think this is the way to solve this problem.
I would use EXISTS:
WITH subquery AS (...)
SELECT CASE WHEN EXISTS (SELECT 1 FROM subquery
WHERE subquery.host = table_b.destination_host)
THEN 'typeA'
ELSE 'typeB'
END
FROM table_b;
With queries like that, you have to take care of NULL values. If table_b.destination_host is NULL, the row will always show up as typeB, because NULL = NULL is not TRUE in SQL.
Here is the query I have built to derive tech number. I can't get it to work. I get this error message:
Subquery returned more than 1 value.
The query:
declare #TechNum int =
(Select Case When CF_Technician='David' Then 9 When
CF_Technician='David N' Then 9 When
CF_Technician='David Nunez' Then 9 When
CF_Technician='Joe Nicholson' Then 8 When
CF_Technician='Joe Nicholson' Then 8 When
CF_Technician='Josh Fogleman' Then 7 When
else 0
End
From OptimazationItems)
Select
ItemID,
TechNum=#TechNum
From
OptimazationItems
What is wrong? Why can't I use a variable in the select statement? What is the best way to accomplish this?
I know I can create another view and join it afterwards. Is there a way to do it in one query?
You can use a variable, but you have assign to it a subquery that returns at most one row. The subquery you assign obviously returns more than one row. Run the subquery in isolation, you'll see that it returns more than one row.
Rewrite the subquery to only return (at most) one row. You'll probably want to add a WHERE clause or a SELECT TOP 1 + ORDER BY some_column to select a specific row.
It looks from the comments that you didn't understand what I was saying. What you probably meant to write is the following.
SELECT
ItemID,
TechNum=CASE CF_Technician
WHEN 'David' THEN 9
WHEN 'David N' THEN 9
WHEN 'David Nunez' THEN 9
WHEN 'Joe Nicholson' THEN 8
WHEN 'Joe Nicholson' THEN 8
WHEN 'Josh Fogleman' THEN 7
ELSE 0
END
FROM
OptimazationItems
You cannot write this select statement using a variable.
I'm trying to execute a query in DB2. But it throws following error:
Error: DB2 SQL Error: SQLCODE=-115, SQLSTATE=42601, SQLERRMC=IN, DRIVER=4.8.86
SQLState: 42601
ErrorCode: -115
Error: DB2 SQL Error: SQLCODE=-514, SQLSTATE=26501, SQLERRMC=SQL_CURSH200C1; STMT0001, DRIVER=4.8.86
SQLState: 26501
ErrorCode: -514
Which does'nt make sense as my query looks correct:
SELECT ROW_NUMBER() OVER() AS ID,
CONCAT(TRIM(TB1.ROW1),CONCAT('_',TRIM(TB1.ROW2))) AS CODE_DESCRIPTION,
CASE
WHEN TRIM(TB1.ROW1) IN (SELECT T1.ROW1 FROM DB1.TABLE1 T1 WHERE T1.ROW3 = 'TEST')
THEN 'Valid'
ELSE 'Invalid'
END,
TB1.* FROM DB1.TABLE1 TB1
WHERE TB1.ROW3 = 'CLASS1';
SQLCode 115 means Comparison is invalid. Which is not ?
Update:
What I'm trying to accomplish here is. I have a Table Table1(Name changed for simplicity). Following is the part of the content.
**Row3** **Row1** **Row2**
KSASPREM SRQ 0 0 Auto Carry SRQ
KSASPREM SCG 0 0 BRT Buses SCG
KSASPREM SCE 0 0 Buses SCE
KSASPREM SRR 0 0 Buses SRR
KSASPREM SDC 0 0 Domestic All Risks SDC
KSASPREM SDA 0 0 Domestic Buildings SDA
Task to accomplish:
Retrieve all the values from Table1 where Row3 is KSASPREM
The result should contain one extra column 'Valid' value Yes/No if value of Row1 is not in the Values retrieved from Table1 where Row3 is 'TEST'
Hope I made myself clear and not more confusing ?
Any Help ?
Thanks
Ps. Updated the Query
As with so many things, a JOIN (here, LEFT JOIN) is the answer. Specifically, we need to put the (slightly modified) subquery as the table reference:
LEFT JOIN (SELECT DISTINCT row1, 'Valid' as valid
FROM Table1
WHERE row3 = 'TEST') AS Test
ON Test.row1 = TB1.row1
LEFT JOIN tells the query engine that "rows in this other table aren't required".
DISTINCT says, "for all value combinations in these columns, give me just one row"
Using a constant value - 'Valid' - returns that constant value.
... so this gets us a (virtual, temp) table containing unique row1 entries where row3 = 'test'.
Here's the full query:
SELECT ROW_NUMBER() OVER(ORDER BY TB1.row1) AS ID,
TRIM(TB1.ROW1) || '_' || TRIM(TB1.ROW2) AS CODE_DESCRIPTION,
COALESCE(Test.valid, 'Invalid') AS valid,
TB1.row3, TB1.row1, TB1.row2
FROM Table1 TB1
LEFT JOIN (SELECT DISTINCT row1, 'Valid' as valid
FROM Table1
WHERE row3 = 'TEST') Test
ON Test.row1 = TB1 .row1
WHERE TB1.ROW3 = 'KSASPREM'
SQL Fiddle Example
COALESCE(...) returns the first non-null value encountered in the value list. Since, if there is no Test row, Test.valid will be null, this outputs 'Invalid' for TB1 rows without a corresponding Test row. (Internally it's calling CASE, I believe, this just makes it prettier)
Note that:
I've put an ORDER BY into the OVER clause, to return (mostly) consistent results. If you only ever plan on running this once it doesn't matter, but if you need to run it multiple times and get consistent IDs, you'll need to use something that won't be shuffled.
DB2 (and apparently PostgreSQL) support || as a concat operator. It makes reading statements so much easier to understand.
Never use SELECT *, it isn't safe for several reasons. Always specify which columns you want.
I have this problem and reproduced it with AdventureWorks2008R2 to make it more easy. Basically, I want to filter a parent table for a list of IN values and I thought it would generate this type of query
but it doesn't.
SELECT * FROM SalesOrderDetail where EXISTS( select * from SalesOrderHeader where d.id=h.id and rowguid IN ('asdf', 'fff', 'weee' )
Any ideas how to change the LINQ statement to query Header only once?
(ignore the fact I'm matching on Guids - it will actually be integers; I was just quickly looking for a 1-1 table in EF because that's when the problem occurs and I happened to find these)
var guidsToFind = new Guid[] { Guid.NewGuid(), Guid.NewGuid(), Guid.NewGuid()};
AdventureWorks2008R2Entities context = new AdventureWorks2008R2Entities();
var g = context.People.Where(p => guidsToFind.Contains(p.BusinessEntity.rowguid)).ToList();
That produces the following more expensive query:
SELECT [Extent1].[BusinessEntityID] AS [BusinessEntityID],
[Extent1].[PersonType] AS [PersonType],
[Extent1].[NameStyle] AS [NameStyle],
[Extent1].[Title] AS [Title],
[Extent1].[FirstName] AS [FirstName],
[Extent1].[MiddleName] AS [MiddleName],
[Extent1].[LastName] AS [LastName],
[Extent1].[Suffix] AS [Suffix],
[Extent1].[EmailPromotion] AS [EmailPromotion],
[Extent1].[AdditionalContactInfo] AS [AdditionalContactInfo],
[Extent1].[Demographics] AS [Demographics],
[Extent1].[rowguid] AS [rowguid],
[Extent1].[ModifiedDate] AS [ModifiedDate]
FROM [Person].[Person] AS [Extent1]
INNER JOIN [Person].[BusinessEntity] AS [Extent2] ON [Extent1].[BusinessEntityID] = [Extent2].[BusinessEntityID]
LEFT OUTER JOIN [Person].[BusinessEntity] AS [Extent3] ON [Extent1].[BusinessEntityID] = [Extent3].[BusinessEntityID]
WHERE [Extent2].[rowguid] = cast('b95b63f9-6304-4626-8e70-0bd2b73b6b0f' as uniqueidentifier) OR [Extent3].[rowguid] IN (cast('f917a037-b86b-4911-95f4-4afc17433086' as uniqueidentifier),cast('3188557d-5df9-40b3-90ae-f83deee2be05' as uniqueidentifier))
Really odd. Looks like a LINQ limitation.
I don't have a system to try this on right now but if you first get a list of BusinessEntityId values based on the provided guids and then get the persons like this
var g = context.People.Where(p => businessEntityIdList.Contains(p.BusinessEntityId)).ToList();
there should not be a reason for additional unnecessary joins anymore.
If that works, you can try to combine the to steps into one LINQ expression to see if the separation stays intact.