Open NRKirby opened 4 years ago
@roji to write the work-around query.
Until we implement this, you can simply use the coalescing operator to work around it:
var orders = _context.Orders.Where(x => machineIds.Contains(x.MachineId ?? default));
Note: consider regular COALESCE vs. IS_NULL here (see #32519, though that discussion should apply both to the regular C# coalescing operator and to GetValueOrDefault)
IMO, EF should translte all COALESCE
to ISNULL
.
EF current tanslate ??
operator to COALESCE
, COALESCE is actrully the CASE WHEN
expression, For field scenario (eg. COALESCE(x.FieldA, 0)
) , COALESCE
should have the same performance than ISNULL()
; for expression scenario (eg. COALESCE((SELECT TOP 1 FIELD FROM TABLE), 0)
), COALESCE
is much slower ,because it need evaluate the expression twice , because CASE WHEN condition
evaluate it first and then the THEN/ELSE
return value evaluate it after.
eg.
CASE WHEN
(SELECT TOP 1 FIELD FROM TABLE) IS NOT NULL
THEN
(SELECT TOP 1 FIELD FROM TABLE)
ELSE
0
END
and in real test , ISNULL
is faster for both filed and expression
for expression scenario [...] COALESCE is much slower
@John0King do you have any links/resources to back that up, or better yet, a small benchmark that shows this performance difference?
Note this useful post on COALESCE vs. ISNULL, according to which the performance is the same - although it indeed doesn't test the expression/subquery case.
@roji
I don't have a professional benchmarks, just with SSMS + Sqlserver 2022 in docker with a large table
name | 1st run | 2nd run | 3rd run | 4th run |
---|---|---|---|---|
Filed_ISNULL | 00:00:10.593 | 00:00:10.122 | 00:00:10.550 | 00:00:09.980 |
Filed_COALESCE | 00:00:10.330 | 00:00:10.623 | 00:00:10.708 | 00:00:10.235 |
expression_COALESCE | 00:00:28.083 | 00:00:29.340 | 00:00:27.289 | 00:00:29.223 |
expression_ISNULL | 00:00:12.194 | 00:00:12.587 | 00:00:12.624 | 00:00:12.827 |
and I also have an another compare about expression case (but the isnull case is not reporting the real time execution plan until all output done, I think this benchmark is not a fair compare) |
name | 1st run | 2nd run | 3rd run | 4th run |
---|---|---|---|---|---|
expression_COALESCE | 00:00:23.286 | 00:00:21.499 | 00:00:21.042 | 00:00:21.443 | |
expression_ISNULL | 00:00:29.659 | 00:00:29.874 | 00:00:31.610 | 00:00:31.610 |
and there is stackoverflow answer about them tow. https://stackoverflow.com/questions/7408893/using-isnull-vs-using-coalesce-for-checking-a-specific-condition
and this result prove that COALESCE indeed evaluate twice:
and this result prove that COALESCE indeed evaluate twice:
~I'm unable to reproduce this: wouldn't COALESCE returning NULL in this case be a violation of its behavior, regardless of any performance considerations? I tried to reproduce with the following code (please don't post screenshots in github issues - always post actual text):~
CREATE TABLE Foo (A int, B int);
INSERT INTO Foo (A, B) VALUES (1, NULL), (2, 2), (3, NULL);
SELECT COALESCE((SELECT TOP 1 B FROM Foo ORDER BY NEWID() ASC), 0);
~This outputs 0, not NULL.~
After running the above several times, I could actually observe a NULL being returned - so you're right.
@John0King in any case, I've opened #32519 to track the COALESCE vs. ISNULL question. I don't see this as relevant for translating GetValueOrDefault() (this issue) in any way: if we end up deciding that ISNULL is better, then we translate to that also when the normal C# coalescing operator is used. Whether or not we translate GetValueOrDefault() is unrelated to that.
I get an
InvalidOperationException
when I try filtering a DbSet on a property that is a nullable Guid.Steps to reproduce
When I execute the following query on an Orders DbSet where
MachineId
is a nullable Guid.I get the following exception
-->
Further technical details
EF Core version: 3.1 Database provider: Microsoft.EntityFrameworkCore.SqlServer Target framework: NET Core 3.1 Operating system: Windows IDE: Visual Studio 2019 16.7.0