News for hackers. Stuff that matters.
Re:SELECT wat FROM sql by Pxtl on Sunday April 26, 2020 @09:52PM (#22992179)
Yes this. I always see people raving about the mathematical beauty of three-value Boolean algebra, but any programming language where testing if foo = foo can return something mechanically equivalent to false is just plain broken.

Yes, I get that it's pure and mathematically beautiful, but over here in the real world I've squished an endless supply of bugs caused by this absurdity, and in languages where there is no null-aware equality test, the composing the proper check is absurdly verbose.

Re:SELECT wat FROM sql by adamzochowski on Sunday April 26, 2020 @11:54PM (#22992745)
Sigh,

In all other programming languages the code has to be along the lines of:

    if ((jane.car.colour == james.car.colour) and
        (jane.car.colour != null            ) and 
        (           null != james.car.colour)):
       print ("James and Jane have same car color")
or attempted with a second variable to indicate if value is known

    if (( jane.car.colourKnown == True ) and 
        (james.car.colourKnown == True ) and 
        ( jane.car.colour == james.car.colour)):
       print ("James and Jane have same car colour")

I like to think that SQL nulls are the most human way. I think of a scenario: James and Jane each own a car. I don't know neither car colour. Does it mean they are of same colour? All typical programming languages say "Yes". SQL says "I can't tell you, I don't know", and that is what a human would say.
Re:SELECT wat FROM sql by lmm on Monday April 27, 2020 @08:50AM (#22995302)
Plenty of languages have types that let you ask those kind of operations in a nice way. E.g. in Haskell you'd do

    traverse (flip when (putStrLn "James and Jane have same car color")) $
        liftA2 == james.car.colour jane.car.colour
The problem isn't that SQL supports tri-state logic, it's that it gives you no way of opting out of it. Over 90% of the time a given value should never be null, but there's no way to put SQL into a mode where a null in a given intermediate stage is an error. Instead it will silently give you a strange answer with no indication that anything went wrong.
Re:SELECT wat FROM sql by Pxtl on Monday April 27, 2020 @03:48PM (#22999268)
Then why on earth is NULL being treated the same as false for the purposes of a WHERE clause.

Simple point:

  SELECT 'foo' WHERE bar = baz FROM quux
  UNION ALL
  SELECT 'bar' WHERE bar <> baz FROM quux
To me, there are two acceptable outcomes of this situation:

1) The result is a single row containing "foo" or "bar".

2) The result is an error message that the truthiness of 'bar = baz' cannot be determined.

However, ansi-null-compatible-SQL just will give you an empty resultset if bar or baz are null. That's not the server saying "I don't know" to "bar = baz", that's the server saying "neither" which is very freaking different. "I don't know" is an acceptable answer. "neither" is a lie.

That is unacceptable, and I'm utterly bewildered that people can insist that this is okay.

Re:SELECT wat FROM sql by da_chicken on Monday April 27, 2020 @05:57PM (#23000592)
> Then why on earth is NULL being treated the same as false for the purposes of a WHERE clause.

Because, by definition, the WHERE clause returns a record when it evaluates to TRUE. FALSE and UNKNOWN are not TRUE.

Second, you absolutely can do what you're trying to here, but not the way you've done it:

  SELECT CASE WHEN bar = baz THEN 'foo' ELSE 'bar' END AS corge
  FROM quux
You're asking the wrong question and wondering why you're getting the wrong answer.
Re:SELECT wat FROM sql by atlantique on Monday April 27, 2020 @05:03PM (#22999992)
It sounds like it is operating according to a closed-world assumption, in which this is reasonable behavior.
Re:SELECT wat FROM sql by stouset on Monday April 27, 2020 @12:51AM (#22992981)
Your brain is going to explode like mine did when you learn about Option types[1].

[1] https://en.m.wikipedia.org/wiki/Option_type

Re:SELECT wat FROM sql by ericpauley on Monday April 27, 2020 @12:55AM (#22992994)
This doesn't really solve GP's problem, as the Nothing type is generally still equal to itself. Certainly useful for general purpose programming languages, though.
Re:SELECT wat FROM sql by Smaug123 on Monday April 27, 2020 @03:40AM (#22993764)
The problem is really that you're using the wrong equality type here. You get the right semantics if you use `= : A -> A -> Bool` and then apply it to a `Maybe A` and a `Maybe A` by lifting it: you end up with `Nothing` if either input was `Nothing`, and `Just answer` if both were known.
Re:SELECT wat FROM sql by vimslayer on Monday April 27, 2020 @03:18AM (#22993648)
For what it's worth, in your "all other programming languages" example you don't need the second null check

    null != james.car.colour
It's redundant when we know the first two are true.
Re:SELECT wat FROM sql by thedufer on Sunday April 26, 2020 @11:42PM (#22992694)
> any programming language where testing if foo = foo can return something mechanically equivalent to false is just plain broken

Isn't this pretty much any language? You're describing how IEEE float NaNs work. I'm not aware of many languages that don't have IEEE-compliant floats.

Re:SELECT wat FROM sql by lmm on Monday April 27, 2020 @08:56AM (#22995342)
IEEE floats with non-signalling NaNs make some very aggressive tradeoffs (in the name of performance) that were maybe a reasonable default in 1985 but are certainly not so today. General-purpose programming languages should not be offering them unless very explicitly opted in to.
Re:SELECT wat FROM sql by thedufer on Monday April 27, 2020 @09:16AM (#22995514)
Can you actually name a mainstream language that doesn't have NaNs with that behavior? Python, Ruby, Go, Rust, Java, JavaScript, etc all do.

Or maybe what you're saying is really just that they shouldn't have them, and every modern language has gotten this wrong?

Re:SELECT wat FROM sql by lmm on Monday April 27, 2020 @09:33AM (#22995649)
> Can you actually name a mainstream language that doesn't have NaNs with that behavior? Python, Ruby, Go, Rust, Java, JavaScript, etc all do.

Rust doesn't have that behaviour: Rust floating-point types simply don't have Eq implementations (they have PartialEq only), passing them somewhere where you need an equality-comparable type is a compilation error. OCaml is the only other example I'm aware of (it treats nan as a normal value with a well-behaved place in the order).

> Or maybe what you're saying is really just that they shouldn't have them, and every modern language has gotten this wrong?

Yes. Similar to how modern high-level languages are starting to default to arbitrary-precision integers rather than fixed-size integers (e.g. Python), we should be defaulting to something like Python's decimal or Java's BigDecimal, and have IEEE floating-point as an opt-in case for when you need it.

Re:SELECT wat FROM sql by thedufer on Wednesday April 29, 2020 @07:12AM (#23017825)
> Rust floating-point types simply don't have Eq implementations (they have PartialEq only), passing them somewhere where you need an equality-comparable type is a compilation error.

While this is true, you can still say `NaN == NaN` and get false. I claim that that's still the behavior I was describing.

> OCaml is the only other example I'm aware of (it treats nan as a normal value with a well-behaved place in the order).

Again, kind of true - for physical and structural equality it does a bitwise-comparison. However, most people recommend against those equality operators because they are misleading in many cases, and the float-specific equality operator does what I described.

In what ways are bitwise-comparison wrong? For structured data (say, binary-tree backed maps) this is clearly bad because there are potentially multiple representations that we would consider equivalent. But even for floats it is bad - `0 == -0` should be true, but will not be. Further, two different NaNs will be compare as false even though two of the same NaN will compare as true (you're not supposed to be able to distinguish between different representations of NaN).

Re:SELECT wat FROM sql by lmm on Wednesday April 29, 2020 @08:58AM (#23018538)
> While this is true, you can still say `NaN == NaN` and get false. I claim that that's still the behavior I was describing.

Point taken; I do think it would be better for the method on PartialEq to not be called "==". But Rust does offer a standard distinction between well-behaved equality and not, and floats are not considered to implement well-behaved equality, which comes close to what I'm asking for. Like, if JavaScript had `NaN === NaN` then I'd consider that morally a counterexample to what you're talking about, because === is (in a sense) the standard equality operator in JavaScript; in the same way, I'd consider Eq::== to be the standard equality operator in Rust.

> Again, kind of true - for physical and structural equality it does a bitwise-comparison. However, most people recommend against those equality operators because they are misleading in many cases, and the float-specific equality operator does what I described.

I think that's the right way to do it. Generic code using the generic == expects it to have the standard properties - in particular x == x for any x. Code that uses a float-specific operator can be prepared for float-specific behaviour.

> In what ways are bitwise-comparison wrong? For structured data (say, binary-tree backed maps) this is clearly bad because there are potentially multiple representations that we would consider equivalent. But even for floats it is bad - `0 == -0` should be true, but will not be. Further, two different NaNs will be compare as false even though two of the same NaN will compare as true (you're not supposed to be able to distinguish between different representations of NaN).

This is true as far as it goes. Equally, having 0 == -0 is bad (it breaks substitutability, since 1 / 0.0 != 1 / -0.0), and having x != x is super bad. Really there is just no good general-purpose comparison for IEEE 754 floats, and so they should either not implement generic == at all (in languages where that's possible), be avoided by default and hidden away in an "unsafe" area of the standard library, or both.

Re:SELECT wat FROM sql by hathawsh on Monday April 27, 2020 @01:04AM (#22993029)
The fact that null != null (and NaN != NaN) is, to me, a helpful reminder that the mathematical model behind SQL is not as simple and elegant as newcomers would like it to be. It reminds me of the classic "proof" that 1 = 2, based on simple algebra:

https://www.math.toronto.edu/mathnet/falseProofs/first1eq2.h...

If even basic algebra has a trap like that, then all of us need to be prepared for similar traps.

Re:SELECT wat FROM sql by shezi on Monday April 27, 2020 @03:21AM (#22993663)
There is not a trap in algebra that is shown in that proof: there is a mistake in that proof. They do something that is not allowed, even if it's not obvious what that was at first glance.

I don't think one can compare mistakes with thoughtful and powerful modelling.

Re:SELECT wat FROM sql by hathawsh on Tuesday April 28, 2020 @05:00PM (#23011943)
By trap I mean a surprising exception to the common rules. Most high school graduates learn algebra, yet they rarely learn how to avoid accidental division by zero. It's a surprising exception.

"null != null" is also a surprising exception to the common rules. I honestly wouldn't expect most developers to know it until their code breaks because of it. That doesn't mean the surprising rule is wrong, it just means people have to learn it. Once they learn it, they may try to find the deeper model where the rule is no longer a surprise but rather an important feature.

Re:SELECT wat FROM sql by da_chicken on Monday April 27, 2020 @12:06AM (#22992797)
> composing the proper check is absurdly verbose.

You're kidding, right? Instead of

  WHERE Field = 1
You have to specify

  WHERE Field = 1 OR Field IS NULL
You might even have to add parenthesis! It's so verbose it puts Java to shame!

Yes, I understand that real queries are much longer, but that's not the language's fault. That's the complexity of your data coming through.

NULL doesn't behave the way it does out of some need for mathematical purity. Understanding the model just tells you when you know you need to handle your NULLs. The system behaves the way it does because the system doesn't know what you want to do when a value doesn't exist or is unknown. Logically, there is no answer. That's what you're complaining about here. It's like asking the system to add two numbers and only giving it one of them.

What does a NULL datetime mean? The value wasn't recorded? It wasn't observed? It hasn't happened yet? That field doesn't make sense to record with the other data on the record (i.e., you denormalized your data)? That the value is actually known to be unknown? Or some combination of those? How is the system supposed to know?

When you're telling the system what to do with NULL values, you're telling the system what NULL means for that query. That's all you're doing. You're just telling the system what your data actually means in that query. That's not hard. Complaining that the system that stores structured data actually requires you to understand what your data represent and how your data are being used is... strange.

Re:SELECT wat FROM sql by lmm on Monday April 27, 2020 @08:59AM (#22995371)
> You might even have to add parenthesis! It's so verbose it puts Java to shame!

It's a serious problem, because in the real world programmers are not willing to double the length of their code for the sake of catching bugs that happen maybe 1 in 100 times. If the language doesn't make it easier to do the right thing than not, you'll end up with bugs. http://www.haskellforall.com/2016/04/worst-practices-should-...

> NULL doesn't behave the way it does out of some need for mathematical purity. Understanding the model just tells you when you know you need to handle your NULLs. The system behaves the way it does because the system doesn't know what you want to do when a value doesn't exist or is unknown. Logically, there is no answer. That's what you're complaining about here. It's like asking the system to add two numbers and only giving it one of them.

The problem is that there's no way to have a model that doesn't include that. At least 80% of the time, probably more, having no value for a given field is just an error, there is no correct way to handle it, so you want to mark it as non-nullable and get on with your life. But in SQL there's no type system that can distinguish nullable things from non-nullable, so you have to either treat every single column and intermediate value as nullable, or make error-prone manual checks, or just get weird results every so often.

Re:SELECT wat FROM sql by da_chicken on Monday April 27, 2020 @05:41PM (#23000434)
> It's a serious problem, because in the real world programmers are not willing to double the length of their code for the sake of catching bugs that happen maybe 1 in 100 times.

No, the problem is that programmers don't like to understand the problem before writing the solution. The problem is worshiping code brevity instead of understanding what their code does. No, that's not a new problem, but it doesn't mean that it becomes the language's problem that you have to actually understand what you're telling something to do. Platitudes like "worst practices should be hard" are all well and good, but they're just that. It should be really difficult to include a buffer overflow, or a dereference a null pointer. In practice, languages that do that have a cost overhead. You still had to write that extra code, the compiler or library just did it for you.

I get it. Developers have a lot to know these days, and stacks and frameworks change rapidly. But the relational model has been largely unchanged for 50 years, and SQL has been basically the same for 30 years. The relational model is very old, and it hasn't changed for very good reasons. There's like 5 to 10 basic patterns for queries that you have to know, and that will cover you in 95% of cases if your system is well designed and you actually understand the data.

Either you understand what your application is doing and you understand what your query is doing, or you don't understand either of those things. I'm sorry, there isn't a middle ground, and there never will be regardless of what data store you choose, what language you use, etc. The guy at the keyboard still has to know what he's doing.

> At least 80% of the time, probably more, having no value for a given field is just an error

Then set the column to have a NOT NULL constraint and now it's impossible to store a null value. You'll get an error just like trying to store a character value into an integer field.

> But in SQL there's no type system that can distinguish nullable things from non-nullable

That's not true. Every column in a table can have a NOT NULL constraint. If your data is actually required and guaranteed to have a value, you just specify that the table's column can't be NULL. You will have to literally break the database engine to get a null value stored in that column.

Yes, you might have situations where you need to use an outer join which will have null values. Yes, you will have to handle NULLs from an Nth normal form database in your application in this case. It's still not difficult. You're still going to have non-nullable fields in your output unless your design is incredibly poor.

Re:SELECT wat FROM sql by lmm on Monday April 27, 2020 @07:11PM (#23001271)
> The problem is worshiping code brevity instead of understanding what their code does. No, that's not a new problem, but it doesn't mean that it becomes the language's problem that you have to actually understand what you're telling something to do.

Good language design means accepting that programmers are human and writing for use by programmers as they actually exist, not for some imaginary perfect programmer.

> It should be really difficult to include a buffer overflow, or a dereference a null pointer. In practice, languages that do that have a cost overhead. You still had to write that extra code, the compiler or library just did it for you.

This is a dangerous myth. Most of the problems with C - and SQL - are just deadweight losses. You actually can just use languages that don't have those problems.

> Either you understand what your application is doing and you understand what your query is doing, or you don't understand either of those things. I'm sorry, there isn't a middle ground, and there never will be regardless of what data store you choose, what language you use, etc. The guy at the keyboard still has to know what he's doing.

Languages are tools for understanding. There are good and bad languages, and it's easier to make better languages than make smarter programmers.

> Then set the column to have a NOT NULL constraint and now it's impossible to store a null value. You'll get an error just like trying to store a character value into an integer field.

Right, but there's no way to distinguish that at the point of querying, and no practical way to have NOT NULL constraints on your intermediate variables in the middle of a query.

Re:SELECT wat FROM sql by Pxtl on Monday April 27, 2020 @03:42PM (#22999208)
Do that for two arbitrary variables.

Test that

    t1.f1 = t2.f2
including nulls, for an arbitrary type of f1 and f2.

This is actually a hellscape of gotchas as people try to find a short way to do it and then discover that their short version is treating NULL as equivalent to FALSE, which means it will fail when negated. Or they throw in a COALESCE and then fail because they've now treated a special value as equivalent to NULL.

The correct answer is

    ((t1.f1 IS NULL AND t2.f2 IS NULL) OR (t1.f1 IS NOT NULL AND t2.f2 IS NOT NULL AND t1.f1 = t2.f2))
Re:SELECT wat FROM sql by da_chicken on Monday April 27, 2020 @05:14PM (#23000106)
First of all, you can shorten that to this:

  ((t1.f1 IS NULL AND t2.f2 IS NULL) OR (t1.f1 = t2.f2))
That's logically equivalent because TRUE OR UNKNOWN evaluates to TRUE, while FALSE OR UNKNOWN evaluates to UNKNOWN, which is not TRUE and therefore gets excluded.

Second, the complicated one that shows up in MERGE or UPSERT operations is the other way:

  ((t1.f1 IS NULL AND t2.f2 IS NOT NULL) OR (t1.f1 IS NOT NULL AND t2.f2 IS NULL) OR (t1.f1 <> t2.f2))
Third, this is exactly why many RDBMSs support the IS [NOT] DISTINCT FROM syntax defined in ANSI SQL, which replaces the first:

    t1.f1 IS NOT DISTINCT FROM t2.f2
Or this which replaces the second:

    t1.f1 IS DISTINCT FROM t2.f2
MS SQL Server and MySQL don't, but Oracle and PostgreSQL do. It's been in the standard for over 20 years at this point. So it's not the language that sucks; it's your vendor.
Re:SELECT wat FROM sql by Pxtl on Monday April 27, 2020 @10:18PM (#23002640)
And then somebody negates your "is equal" because they want "not equal" statement and tears their hair out.
Re:SELECT wat FROM sql by Svip on Monday April 27, 2020 @04:46AM (#22994077)
Wouldn't one just use coalesce()? e.g. coalesce(Field, 1) = 1?
Re:SELECT wat FROM sql by da_chicken on Monday April 27, 2020 @05:50PM (#23000520)
No, that's a bad pattern that should be avoided.

First of all, what happens if the value of Field is actually 1? Did you want that record affected? Because now it will be. It's not exactly clear from your query that you did, is it? This is explicit:

  WHERE (Field = 1 OR Field IS NULL)
And it's not actually that much shorter:

  WHERE COALESCE(Field, 1) = 1
Second of all, if Field is indexed, well, you probably just ignored that index or turned a seek into a scan (i.e., a slower operation). Putting a field into a function will often mean the query engine can't use the index to help with query performance. It means the query engine will have to do a whole lot more work. The term in the industry is "sargable": https://en.wikipedia.org/wiki/Sargable
Re:SELECT wat FROM sql by stouset on Monday April 27, 2020 @12:42AM (#22992937)
That’s all great but it’s not like we haven’t figured out how to solve this in other languages with `Option` or `Maybe` types. Obviously SQL predates this, but that shouldn’t stop us from lamenting the fact that SQL repeated the Billion Dollar Mistake and discussing a future alternative that’s learned from this and doesn’t feature such a sharp edge.
Re:SELECT wat FROM sql by wiml on Monday April 27, 2020 @01:47AM (#22993204)
Sum types like Option/Maybe (or C's NULL) don't do the same thing as SQL's NULL, though. By all means, if you don't need NULLs, then don't use them — `NOT NULL` should probably have been the default in table statements — but they serve a purpose.