16

In our domain model "not filled in" and "unknown" are two different concepts. For example, time of death may be missing or, on the other hand, may be filled in as "unknown"

How do we honor that distinction in our data model (e.g. in Oracle DB)?

I figure we can reserve NULLs for "not filled". Then how do we describe "unknown"? Should we change the column type to some custom EXTENDED_DATE that has a special value for "unknown"? That would be a huge undertaking

CREATE OR REPLACE TYPE date_type AS OBJECT (
    type VARCHAR2(20)
);

CREATE OR REPLACE TYPE extended_date AS OBJECT (
    the_date DATE,
    type date_type
);
9
  • Given the assumption that there is only one 0 to 1 dates possible. I would recommend an additional attribute. @philip-kendall already has a suggestion I'd approve as well. Is your domain model fixed? There might be multiple values possible. You might have a test that came to a result and perform another test giving another result. That way you could create a table for test results that either have a date (known date) or null (unknown date). You might have multiple test results or no test result (not filled).
    – SvenTUM
    Commented Oct 16 at 9:36
  • 14
    Here be dragons! What today is "known," "unknown" and "not filled in" might change. Tomorrow it might be "sometime in 2019" where the month and day is unknown. Do all dates need to support known, unknown, and not specified? I would talk to your users more about what they need before deciding on a data structure. Previous questions indicate you are working on a medical application, which might provide additional constraints on the design. Commented Oct 16 at 10:53
  • When you store individual components, the distinction becomes clear. Then it becomes null, vs {} vs {year: 2024} vs {year:2024, month: 02} and so on, up to maximum detail. Storing the model and retrieving it exactly the same is an implementation detail.
    – S.D.
    Commented Oct 16 at 16:17
  • @S.D. actually, Oracle DB has no notion of {}. NULL is the only special value Commented Oct 16 at 17:20
  • 10
    I was just reminiscing on another SE site that VB classic has "Empty", "Nothing" and "Null" to represent three different kinds of missing data each with different semantics. This problem has a long history. Commented Oct 16 at 23:20

8 Answers 8

34

I always prefer an explicit domain model - have an enum or similar KNOWN_DATE, UNKNOWN_DATE, NOT_FILLED and then a separate date column. The primary reason you might move away from this is for performance reasons, only you can determine if that's important for your use case or not.

Yes, it will be a lot of work. But there's no way round that, if it's important for your business then you need to do it.

10
  • Thank you! Do you suggest adding another column that stores those quasi-enum values describing what the date column value actually stands for? By the way, we typically have more than one date column in our tables Commented Oct 16 at 8:44
  • shrug - I don't know your domain model, your example was the one specific death date. My overall take is just that an implicit domain model (magic values etc) should be avoided, you can work out how best to handle that for your specific case. Commented Oct 16 at 9:44
  • 3
    @SergeyZolotarev If you're using an ORM like Entity Framework, value types allow for reusability here. You define one type with the two properties (enum + nullable datetime), use that everywhere, and EF will semi-automagically handle the two columns needed to store this data. If you're handcrafting your data structure and queries, well then you've inherently signed up for needing to handcraft it.
    – Flater
    Commented Oct 16 at 13:06
  • 1
    Situations may also arise where one may know something about a date, but not know it precisely. One may know that it was between two dates, definitely was or wasn't on various days of the week, was sometime in the summer some years ago, but one doesn't remember how many, etc. No single "date" field can, by itself, accommodate all such information.
    – supercat
    Commented Oct 16 at 22:58
  • 1
    @supercat when you go that deep down the rabbit hole you need a completely different system with infinite flexibility. Probably a separate table with "potential dates", each with a probability factor assigned, and an FK back to the main information table.
    – jwenting
    Commented Oct 17 at 10:25
10

There's no simple "do it that way and you'll be fine" solution.

This is the approach that I would probably take:

  1. Determine the actual use cases (entering and displaying data, storing into and retrieving from the database, searching and sorting). Apparently you've done a bit of that (you list "not filled in" and "unknown" as possible exceptional values which need to be entered and displayed), but there may be more, and it is not clear how these exceptions would affect search operations which is where mapping data into database concepts can get tricky.
  2. Consider whether there are plausible future extensions that you don't need to implement now but which your design should not make too difficult to implement later. Depending on the domain, you may need to represent inexact or estimated dates (genealogy software has this a lot) or dates in different calendar systems (that might be interesting for historical events in different areas of the world). Don't overthink this but also don't ignore obvious plausible extensions.
  3. Map into database concepts. As you already know, SQL databases typically have NULL as the only exceptional column value, so you will likely need more than one column to represent your dates. Using an enum which tells you whether your date is regular or one of a set of exceptional values would be pretty straightforward as long as you can satisfy your requirements with it, especially search and sort operations.

The result would likely look much like what @PhilipKendall wrote, unless there are more requirements which you don't detail here.

2
  • 6
    It is likely also wise to include CHECK constraints to ensure that the enum agrees with the date value (i.e. if the date is known, then it should not be NULL, and vice-versa).
    – Kevin
    Commented Oct 16 at 19:54
  • One such additional "exceptional value" could be a date that's labeled "approximate". It's not truly unknown but still not exactly known. Commented Oct 17 at 12:09
7

It sounds like what you're essentially trying to do here is tease out the various different meanings of NULL and treat them systematically.

The problem is that there are not only a number of possible specific meanings of NULL in different contexts, but there also often remain ambiguous cases where it isn't possible to definitively categorise, or dual cases where the meaning depends on the processing being applied to the data (not on the intrinsic meaning of the data).

I use the example of a dog found injured on the street by a stranger and brought to a vet. From the vet's point of view, not only is the owner unknown, but whether the dog even has an owner is unknown (it could be a stray). If the dog has no owner, then the record of an owner is "inapplicable". If it has an owner who has not had chance to present themselves yet due to the circumstances, then the record is "missing". The problem is that these two cases are themselves ambiguous and unresolved - there is missing information about whether the owner information is missing or inapplicable. You then end up with a third "pending" case which could eventually collapse to one of the other two.

For records which withstand updates, there is also a possible distinction in some contexts between "missing-for-now" (action is being taken to seek the missing information) and "missing-permanently" (the missing condition is considered final).

You will often end up introducing a large amount of extra complexity to deal systematically with what are usually a spuriously small number of cases or relatively unimportant distinctions between unrecorded information.

If someone else has already determined a data model in which there are these distinctions and they make sense and are usable in context, then a solution to storing the different cases in SQL will often depend on desired performance and efficiency characteristics.

For example, simply storing a string value is the most flexible approach, since this can accommodate dates in string form plus any non-date special values. But if you need dates typed as dates when possible, you'll inevitably end up with at least a second field to hold an encoding of the special values which aren't part of SQL's date type.

5

A timestamp has two statuses: null and not null.

You are trying to infer three or more statuses from a datatype which can only communicate two.

What you can consider is another field which articulates the confidence of the timestamp or its absence.

Imagine a dataset more like this:

id  | time_of_death | time_of_death_confidence | translation (illustrative purposes only)
----|---------------|--------------------------|-----------------------------------------
0   | null          | unspecified              | is dead, time not specified, yet?
1   | null          | unknown                  | is dead, time forever unknown
2   | 20:34         | estimated                | is dead, estimated
3   | 10:55         | confirmed                | is dead, pinpointed
4   | 14:26         | null                     | this shouldn't happen...
9
  • This is a quite nice way to put it, I like it +1. Especially because it allows to talk about confidence in the null value as well. As in, "we're confident that the date is null", i.e. we know that the date is unknown.
    – Al.G.
    Commented Oct 17 at 19:19
  • @Al.G. Thanks! I'm cooking up a different way of wording OP's tribulation =)
    – MonkeyZeus
    Commented Oct 17 at 19:28
  • On a first glance it may look that this is the same as Philip's answer, but there is an important difference: in your approach you do not have invalid states. For any date (weather null or not) you can have any confidence in it. Philip's answer though allows invalid states like (non-null date + "unknown" / "not filled" as enum state) which make no sense. (And by the way, I believe you should remove null from the confidence. How could you not know how confident you are otherwise haha)
    – Al.G.
    Commented Oct 17 at 19:56
  • 1
    @MonkeyZeus that's only the case when only one column needs that kind of distinction. If there are, say, ten such columns. you're going to have as much additional "confidence" columns Commented Oct 18 at 12:53
  • 2
    @SergeyZolotarev Then why did you solicit answers? Go and follow through with your kludge. If you wanted to learn how to technically achieve your kludge then you should have asked that. My closing comment is don't let the whims of a business kludge your data model in costly ways.
    – MonkeyZeus
    Commented Oct 18 at 13:33
0

One solution that I have seen for this in practice is to create a 'dates' reference table. Your table will have a row for each actual date used as well as special dates. This allows you to have 'unknown' (known to be unknown) and 'unspecified' (which aligns with your "not filled in"). This can also be used to support invalid dates which is useful if you have to record submitted data regardless of whether it is valid.

This complicates things a fair amount such as by introducing a lot of joins but it does allow for a fully correct data model in situations like this.

Per comments, here's a really simple (and probably naive) example of this kind of table:

key | date       | name             |
------------------------------------|
0   | null       | no value         |
1   | null       | unspecified      |
2   | null       | unknown          |
3   | 2024-10-15 | Oct 15, 2024     |
4   | 2024-10-16 | Oct 16, 2024     |

So instead of the actual date in your data table, you would have a FK constrained column pointing to the key of this table.

I want to be clear: this is not fun to work with and you shouldn't do this if you don't really need it.

25
  • 1
    "Your table will have a row for each actual date used as well as special dates." - how is that supposed to work? How are actual dates separated from special dates? What limits the number of special cases? What are the columns in this table? How would these rows be referenced? This design does appear to violate a number of normalisation rules.
    – Bergi
    Commented Oct 16 at 21:19
  • @bergi My experience with this is using a structure defined by a vendor and I don't think I can divulge specifics, but I am told this is a 'by-the-book design' by people who know more about DBs than me. Give me a minute and I will elaborate in the answer.
    – JimmyJames
    Commented Oct 16 at 21:37
  • 1
    Nothing wrong with lookup tables in general, but they're just not suitable for the use case described by the OP.
    – Bergi
    Commented Oct 16 at 23:49
  • 1
    @GregBurghardt "You double the number of columns to support this data model." - I would rather introduce a composite type or domain type for this, containing a date_value field and a date_type enum field, which could be used as a single column in any table. But that's Postgres, I don't know how Oracle does this (iirc they have object types as well?)
    – Bergi
    Commented Oct 17 at 14:07
  • 1
    @Bergi Sorry, yeah, I was rushing when I put the example up. I think we are done, thanks for the discussion.
    – JimmyJames
    Commented Oct 17 at 20:09
0

You either know the date of birth or you don’t. You may know a partial birthday (someone was born in 1967, or their birthday is 6th of October). I may not have a birthdate for many reasons. Unknown (to the person themselves), never filled out, refused, a hospital might keep track of (yet) unborn babies, a statue has no birthdate, whatever i cannot think of.

You want one date field and one character field describing your information as soon as you have more than one kind of birthdates not in the database.

Don’t use out-of-band information. Many years ago people used 9-9-99 for non-existing birthdates, that thing is going to byte you with names, there is an actress named Rachel Null who was problems with databases all the time for obvious but stupid reasons.

-1

I would challenge your domain model:

  • If the information is unknown, one can't fill it in.
  • If the information is not filled in, your system can't be sure if it is because the information is unknown or for another reason.

This implies that vases with "unknown" are in reality a subset of the cases "not filled in". Storing them differently makes no sense, as they are not mutually exclusive.

You may object that in your case your system is able to make the difference. But how?

  • By deduction from another information? But then you already have what you need and there is no reason to create a redundant "unknown" information, that you would have to maintain in sync.
  • By allowing user to specify it? then you need to foreseen here another information from which your system can deduce if it's unknown. Go to first dash ;-)

P.S: Hardcore logicians would even claim that when an information is not filled in there is a probability that it is nevertheless known and another probability that it's unknown, and if both probabilities are derived from different information sources, the total might not necessarily be 100%: welcome in the world of multivalued logic.

17
  • 6
    I think you might be missing the distinction between 'unknown' and 'unspecified'. 'Unknown' is a specified value which declares that the date is known to not be known. (Cue Donald 'Rummy' Rumsfeld.)
    – JimmyJames
    Commented Oct 16 at 19:49
  • 4
    I agree with @JimmyJames. The OP has asked numerous questions revolving around a medical application. "Unknown" is good to know so every healthcare provider you meet doesn't ask you the same question when your answer is always "I don't know." Contrast that with "not specified" which could cue the healthcare worker to ask the question next time they see the patient. At which point the patient might give a date, or say "I don't know". Then the "not specified" becomes "Unknown" so they don't ask the patient again. Commented Oct 16 at 20:02
  • Of course, none of that is specified in the current question, which is why I voted to close as needing focus. Commented Oct 16 at 20:03
  • @JimmyJames On fact, I know pretty well the distinction between unknown and unspecified. My whole point is that this is quite theoretical and the system is not omniscient and cannot in practice make the difference between the two, unless there is an explicit information that tell it so.
    – Christophe
    Commented Oct 16 at 20:25
  • 1
    @kaya3 I get it. But still, "unknown" is not a date. In a digitised version you would have a flag "unknown" next to the field. And this is exactly what I meant to say in my second bullet at the end: the system cannot make the difference, unless you provide another information telling it explicitly it's unknown or letting it deduce that it's unknown. Then just store this extra information somewhere.
    – Christophe
    Commented Oct 17 at 5:57
-1

One option is to choose a specific date (e.g. 3000-01-01) which means "unknown".

This fulfils your brief but the use cases will be clumsy. Every time you use the field you will have to insert logic to say "but not those dates", in addition to any null handling.

Your code will be easier to read if you create additional field to hold this kind of special information, as suggested in the other answers.

Not the answer you're looking for? Browse other questions tagged or ask your own question.