Comparing anything to NULL results in UNKNOWN rather than FALSE.
This does essentially have the same effect as returning false in that the query will only return rows where the amount is not null and greater than 0 but you should be careful not to assume that when amount is null the result of the comparison is false
For example this will also fail to return null rows :
WHERE NOT(amount > 0)
amount > 0
is UNKNOWN not FALSE. If it were false then NOT would turn it true
You could conceive that NULL is a contaminant that makes its way all the up through every Boolean condition and operation it touches, turning them all UNKNOWN and then "becomes false" right at the very last moment because the final assessor of the Boolean predicate demands a TRUE
By and large you can consider UNKNOWN as synonymous with NULL; UNKNOWN is relatively confined to Boolean logic. In other contexts such as mathematical operations involving NULL results in null (0+NULL = NULL). The only operation that can work on null and produce a Boolean is amount IS [NOT] NULL
Per your code review, if they're going to insist on a blanket "must coalesce any value that might be null" It would appear that the business rule is that the system is intolerant of nulls – in this case it should be that the column is made not null and filled only with actual values. Passing any value through a function that may or may not change it cripples the use of indexes and introduces problems for query planning. One could hope that a smart optimizer could expand/rewrite ISNULL(amount, 0) > 0
to amount > 0 or (amount is null and 0>0)
before simplifying to remove the bracketed condition entirely but it's not necessarily always that simple. Better not to feed crap in in the first place than shrug and go "well it doesn't seem to make things slower in my simple test case"
Applying a blanket rule without any thought or justification could lead to a very poorly performing system. How you will feed this back to the people who make the rules could be s difficult office politic to handle, because their approach is wrong but they're unlikely to admit it or want to be told
As an aside you might feel that the equivalent query better captures the notion of what you're trying to do:
SELECT m.Name
FROM Members m
WHERE NOT EXISTS(
SELECT 1
FROM Fees f
WHERE m.Id = F.memberId and F.AmountDue > 0
)
The left anti join pattern is a favourite of mine for all sorts but I do feel that this is more self explanatory - "all members that don't have a related record where they owe money". NOT IN also makes this quite readable but should be used with care as some database implementations can be quite naive:
SELECT m.Name
FROM Members m
WHERE NOT IN (
SELECT F.MemberId
FROM Fees f
WHERE F.AmountDue > 0
)
SQL server will highly likely rewrite a LEFT JOIN WHERE NULL/WHERE NOT EXISTS/WHERE NOT IN so they're planned and executed identically, but you might find there isn't much appreciation of this/dim view of the IN approach at the next code review :)