Recently I wrote a function for a report with a CASE block to take in a start and end date, and if the end date was NULL, to use the start date.
WHERE theDate BETWEEN @dateStart AND CASE WHEN @dateEnd IS NULL THEN @dateStart ELSE @dateEnd END |
However, when NULL was passed for the end date, the function would hang and not return anything.
What was happening was that estimated rows when passing a NULL using the CASE block created an unequal JOIN predicate. The estimated rows for CASE showed only 79 rows, while ISNULL showed a more accurate 28,393 rows.
The difference between using CASE and ISNULL is that CASE* will return the data type of the expression with the highest data type precedence. Using ISNULL returns the same data type as the expression being checked.
* COALESCE is equivalent to the CASE block.
From Paul White:
COALESCE( expression [ ,...n ] )returns the data type of the expression with the highest data type precedence.
ISNULL(check_expression, replacement_value)returns the same type as check_expression.
When comparing a single expression, use ISNULL. It also performs better than COALESCE.
You can read the full discussion and check out the plans here.