10 ms·
I am the author of this feature. The background here is that the SQL standard was ambiguous about which of the two ways an implementation should behave. So in
by petereisentraut 4y ago
I am the author of this feature. The background here is that the SQL standard was ambiguous about which of the two ways an implementation should behave. So in the upcoming SQL:202x, this was addressed by making the behavior implementation-defined and adding this NULLS [NOT] DISTINCT option to pick the other behavior.
Personally, I think that the existing PostgreSQL behavior (NULLS DISTINCT) is the "right" one, and the other option was mainly intended for compatibility with other SQL implementations. But I'm glad that people are also finding other uses for it.
- zigzag312 4y ago> Personally, I think that the existing PostgreSQL behavior (NULLS DISTINCT) is the "right" one, and the other option was mainly intended for compatibility with other SQL implementations. Care to explain why you think NULLS DISTINCT is the "right" default behavior? What problems does it solve to warrant additional complexity by default?
- knorker 4y agoNot parent commenter, but to me it seems "obviously correct". As explained in the article NULL means "unknown". Let's say I have two people, and a "tax identifier" column. Let's say two people can't have the same tax id. Obviously "don't have one" or "unknown" fit very well in NULL, and two people can both be missing a tax id, even if those who have it need it to be unique.
- chris_wot 4y agoExcept that's the ambiguity of NULL in the standard. It doesn't just mean "unknown". It can also mean "no value". And you can clearly compare two elements with no value. Which is why nulls are so incredibly confusing.
- knorker 4y agoYeah that's true. All the database can say is "I don't have a value here". Not "there exists no value for this column". But this doesn't mean that two NULLs are the same. Two people with NULL tax IDs are not the same (one turns out to be unknown but existing, the other is a toddler without a tax ID). Ten people with NULL address don't live in the same house. Half declined to give address, the other half are homeless. Either way we can't send mail to them. If you use the database to have a UNIQUE constraint of "only one purchase per household" then it makes no sense to allow NULL addresses, but only allow the first NULL address purchaser to buy the thing. Or "sorry, in this hotel we only allow one guest at a time without a car, and someone else already doesn't have a car". Does that guest without a car actually have a car? I don't think that's something that the database can solve. Should databases have two separate values for unknown or no value? That sounds like a world of hurt, with two types of null.
- fendy3002 4y agoWhich happen in world of javascript. It has null and undefined. Null variable is a variable / property that's defined and set with null, while undefined is a variable / property that's not defined yet (or maybe not initialized, I forgot). I hope at least SQL will give another comparison operation, such as the current equal sign (=) means a.prop is not null && b.prop is not null && a.prop equal b.prop (for not defined yet). Let's say another sign like a.prop *= b.prop, meaning a.prop is null && b.prop is null || a.prop = b.prop (for our intention is both value are empty).
- pdw 4y agoSuch a comparison operator exists: IS (NOT) DISTINCT FROM.
- jl6 4y ago> Should databases have two separate values for unknown or no value? That sounds like a world of hurt, with two types of null. It’s also a non-problem in practice, because if an application needs to distinguish between multiple types of nulls, it can very easily just use an extra column holding the information needed to disambiguate.
- jhgb 4y ago> It doesn't just mean "unknown". It can also mean "no value". Isn't the canonical representation of known-no-value an absence of a tuple? Like as opposed to saying "There exists an employee X who works in department NULL", you simply don't make any claim about employee X working in a department? After all, when enumerating the members of a set, you're also omitting the enumeration of non-members of a set, and the law of excluded middle applies.
- dx034 4y agoI'm not the person you're responding to, but also think NULLS DISTINCT makes sense in many cases. NULLs often represent missing data. Imagine storing a customer's address and the street name is NULL. If several customers have a street=null, it doesn't mean that they have the same street. So from a data perspective, it makes sense to treat these unknown values as distinct. For filtering and aggregation I still welcome the change, as it makes sense in many cases to treat them as not distinct.
- zigzag312 4y agoThanks. It seems that the issue arises from value equality vs optional value state equality. To me, a more natural way to treat NULLs is to think of NULL not as a value, but as a state. Several customers with a street=null all have street property in an equal state. However, an equal state doesn't mean the value is also equal, as there is no value in that state. Option type in functional languages models this perfectly: 'a option = |None // no value here |Some of 'a // value of type 'a So, when checking, if customers have the same street we need to check the value. So, comparision is only valid if street has value defined. Adding null check in addition to equality check to ensure that the actual values are equal feels most natural to me (as this is how it's done in most languages). Unique constraints should not be a problem. Unique constraints are about values. So if state is NULL there is no value and so constraint does not treat NULL states as equal values. Adding a keyword to change constraint behavior when needed, would be best IMO, as it would need to be rarely used.
- masklinn 4y ago> So, when checking, if customers have the same street we need to check the value. So, comparision is only valid if street has value defined. Adding null check in addition to equality check to ensure that the actual values are equal feels most natural to me (as this is how it's done in most languages). Is it tho? Languages with ubiquitous nullability will accept both being null or both being non-null and the same value. Languages with option types do the same (as long as the value is equatable obviously), this includes OCaml: https://v2.ocaml.org/api/Option.html#preds https://v2.ocaml.org/api/Option.html#preds. The only languages I can think of which seriously diverge would be C and C++ where this is UB. Maybe if you way stretch it Java because it doesn't have operator overloading, but even there there's Object.equals which is null-safe... and returns `true` if both parameters are `null`.
- richdougherty 4y agoIt's probably a good default because it's more consistent with how NULL equality is handled in SQL generally. From the article: > In Postgres 14 and prior, unique constraints treated NULL values as not equal to other NULL values. ... > This is consistent with the SQL Standard handling of NULL in general, where NULL is unknown. It is impossible to determine if one unknown is equal to another unknown. Because NULL values are of unknown equality to one another, they do not violate UNIQUE constraints.
- zeroimpl 4y agoYour last statement would be better written as: Because NULL values are of unknown equality, they may or may not violate UNIQUE constraints. The safest implementation would assume that they do violate the constraint, rather than the current behavior assuming they don’t. In practice I don’t think I’ve ever added a unique index on a nullable column where null might imply unknown. I have used it in cases where null meant “none”.
- dtech 4y agoNot OP, but this constraint means "the data must either be empty or unique", which is an extremely common constraint. In contrast, I've never encountered "only 1 entry is allowed not to have the data, all others must be unique".
- enkrs 4y ago"only 1 entry is allowed not to have the data, all others must be unique" makes sense in larger schemas as the database grows. It replaces is_default_address, is_primary_child etc. fields and constraints on relational tables. In the perfect world all the data would be normalized not to have such columns and cases, but in real life it just grows that way. So for some cases NULLS NOT DISTINCT will be a welcome addition.
- chris_wot 4y agoWell, if NULL is most often meant to signify an unknown value. You can't compare one unknown value with another and say they are the same. The standard is, I believe, ambiguous about NULL because it can also mean that it is an absence of data. In other words, it is "this is not yet determined" (aka a CAR table has a COLOR table, but the paint is applied as the final operation so NULL could be used as the car is not at this stage yet and the color will be chosen later). In this case, you can compare NULLs against other NULLs, because you can have two cars that are in an indeterminant state. In that case, it is useful to compare to NULL values as it means the same thing.
- undecisive 4y agoI think it's worth pointing out what the opposite means - to say that "this field might have a value, or it might be null - but only one tuple/row can set this field to be null" implies that you have given the null option some kind of real-world value. That is to say, you are using null to encode some kind of meaning, when really this is not what null is supposed to be used for. That's not to say I think we should be morally disapproving of people who do that - I use things for their unintended purpose all the time, and it bugs me when people get on a high horse. Use what makes sense to you - and I love this change for exactly that reason. But the general theoretical approach is that if you want to care about the value in a field, you need to give it a value. Null is for the valueless, and if that isn't an allowed state, you should simply set the field as not-nullable. Theory is fine in theory. For a practical database that exists in the real world, this option is a good addition.
- nightski 4y agoI think a lot of the trouble stems from the fact that databases do not have sum types and there is no way to encode a "None" type without hacks. NULL is the only reasonable option in a lot of cases.
- wongarsu 4y agoYeah, in hindsight the world would be a slightly better place if SQL didn't include NULL, but instead "UNKNOWN" and "NONE", with "NONE = NONE" being true, and "UNKNOWN = UNKNOWN" being unknown.
- petereisentraut 4y ago> Care to explain why you think NULLS DISTINCT is the "right" default behavior? What problems does it solve to warrant additional complexity by default? It's the most consistent with the equality behavior of null values elsewhere.
- jeff-davis 4y agoI generally agree that, in most cases, you want the NULLS DISTINCT behavior. But thank you for providing such a developer-friendly feature that allows flexibility here! NULL in SQL is not terribly consistent overall. Sometimes NULL is treated like "unknown" and sometimes more like "n/a". And the interaction with non-scalar types (like records) is pretty strange. Also, it's common for ORMs to map the app language's NULL (or None/Nil/whatever) to SQL NULL, which adds its own nuance. So I can see this being a useful feature.
- woevdbz 4y agoAn example where NULLS DISTINCT might make sense in a constraint is when a table is used to store an analytical cube roll-up (eg output of GROUP BY ROLLUP), where NULL in a dimension column has special meaning (it indicates subtotals). Having multiple rows with the same NULL key could, in some of those cases, be an error leading to double-counting.
- Pxtl 4y agoOnce you've accepted that X=X doesn't return TRUE when X=NULL, NULLS DISTINCT is consistent with that. ANSI NULL three value boolean algebra is insane, but it's insane in a pretty consistent way.
- nicoburns 4y agoA slight tangent, but do you have any pointers on how one might get involved in either: - The SQL standardisation process (or whether this is even feasible as someone who isn't involved in the development of a major database engine) - Postgres development The feature I am particularly keen to get accepted is trailing commas in SELECT column lists (potentially other places too, but SELECT lists would be a very good start). And there are potentially a bunch of other improvements in the syntax sugar department that I might be interested in spearheading if I could successfully get this accepted (e.g. being able to refer to select list column aliases in GROUP BY clauses). I don't have much experience with C, but I would potentially be up for actually implementing the change if I could get buy in from the maintainers.
- gurjeet 4y agoPostgres project is always looking for contributors. The project has a very nice, detailed, well-defined documentation on how to contribute [1]. The community is very welcoming, and has a process (CommitFest[2]) in place to ensure all submissions get their due attention. FWIW, I am starting to make an effort (e.g. [3]) towards helping the newcomers get their work in shape to be acceptable by the committers. Note: I'm biased towards Postgres community, since I've worked with them for many years. So others' opinion _may_ differ, but it's highly unlikely. [1]: https://wiki.postgresql.org/wiki/Development_information https://wiki.postgresql.org/wiki/Development_information [2]: https://wiki.postgresql.org/wiki/Development_information#CommitFests https://wiki.postgresql.org/wiki/Development_information#Com... [3]: https://www.postgresql.org/message-id/CABwTF4Us8WGMffNGTNY1X%2BirSViC7p-2yMixKQqVepxkApnKhA%40mail.gmail.com https://www.postgresql.org/message-id/CABwTF4Us8WGMffNGTNY1X...
- orthoxerox 4y ago> The feature I am particularly keen to get accepted is trailing commas in SELECT column lists Not "GROUP BY THE OBVIOUS LIST OF EXPRESSIONS, YOU KNOW, THE ONES FROM THE SELECT CLAUSE THAT DON'T CONTAIN AGGREGATE FUNCTIONS"? Aka "GROUP BY ALL" from DuckDB.
- woevdbz 4y ago> Personally, I think that the existing PostgreSQL behavior (NULLS DISTINCT) is the "right" one SQL is old enough and this debate so unsettled still that I think it should be clear there isn't a categorically right behavior here anymore than there is a clear winner between tabs and spaces. As an example of the irreconciliable weirdness of NULL, consider that "NULL = NULL" is false, and so is "NULL != NULL", while rows with NULL still group together in GROUP BY. I appreciate you giving folks the option.
- anamexis 4y ago> As an example of the irreconciliable weirdness of NULL, consider that "NULL = NULL" is false, and so is "NULL != NULL This isn't quite true - these comparisons don't evaluate to false, they evaluate to NULL. I intuitively think of NULL as "unable to compute," a generalized NaN.
- magicalhippo 4y ago> these comparisons don't evaluate to false, they evaluate to NULL In the databases I've used, such as MSSQL[1], they evaluate to UNKNOWN. [1]: https://docs.microsoft.com/en-us/sql/t-sql/language-elements/null-and-unknown-transact-sql https://docs.microsoft.com/en-us/sql/t-sql/language-elements...
- bfgoodrich 4y agoThe document you link uses unknown as a synonym for null. If you inserted the result of such a comparison into a table, the value inserted would be NULL.
- ratsmack 4y agoIt's good to see that everyone agrees on the definition of NULL.
- DaiPlusPlus 4y ago
- rst 4y agoSo, we have a "standard" which says that both behaviors should be supported, and there should be two different ways to express them, but explicitly declines to say which does what? I'm genuinely not sure what purpose this particular standard serves, but it looks like it's not allowing people to write portable code...
- anamexis 4y agoI can't imagine the standard does not specify that NULLS DISTINCT should treat nulls as distinct and NULLS NOT DISTINCT should treat nulls as not distinct.
- rst 4y agoAh. So there are three ways to write it, and explicit ways to call for either behavior. That's sane -- just wasn't what I got from the original writeup.
- mjw1007 4y agoNo, it's not as bad as that. The thing that's implementation defined is what happens if you don't explictly select either option.
- thijsvandien 4y ago> Personally, I think that the existing PostgreSQL behavior (NULLS DISTINCT) is the "right" one I find this to be at odds with IS DISTINCT FROM, which is false for two NULLs.
- deleted 4y ago[deleted]
- Justsignedup 4y agoCurrently nulls are considered not distinct in postgres, no? Doesn't this mean anyone upgrading will have to fix up all their table definitions? Or am I wrong about this? I just care about the backwards compat. To be clear, the second I saw this I thought "finally!!!"
- masklinn 4y ago> Or am I wrong about this? This one. Currently nulls are always distinct (different from one another).
- zeugmasyllepsis 4y agoPlaying around in a docker image of the beta build, it looks like this allows you to add a `unique nulls not distinct` constraint to a composite set of fields, but still does not allow you to specify those same fields a primary key. For example -- This works alter table announcement_destinations add constraint announcement_destinations_pk unique nulls not distinct (sender, receiving_group, optional_filter); but -- Still does not work alter table announcement_destinations add constraint announcement_destinations_pk2 primary key (sender, receiving_group, optional_filter); fails with a message like "ERROR: column "optional_filter" of relation "announcement_destinations" contains null values". Is there a motivation for this distinction?