Database Design: Hitting Two Birds with One Stone by using Timestamps
source link: https://alcher.dev/2019/timestamps-hitting-two-birds-one-stone/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Jan 26, 2019 · John Alcher
Database Design: Hitting Two Birds with One Stone by using Timestamps
This is an archived post from my previous blog. Some content may be outdated. FYI!
Introduction
In this article, we’ll take a look at a simple yet effective pattern in designing database tables that I feel like isn’t getting used enough, especially by beginning programmers. That is the use of the TIMESTAMP
data type both as a DATETIME
and a pseudo-BOOLEAN
data type. As for most design decisions, a lot of it comes down to "”it depends”“. So we’ll look into some use cases and see the pros and cons of each.
Note that some code samples uses PHP and Laravel, but a customary glance should still drive the point home if you’re familiar with any programming language. Not familiar with SQL data types? Take a quick look at the MySQL implementations of TIMESTAMP, DATETIME, and BOOLEAN.
The Situation
Our good friends at ACME Web Services decided to add a new feature within their internal ticketing system. The higher ups would like to manually pre-check a high priority ticket before passing it to a special ticket queue. The following diagram visualizes the desired system:
Now, take a minute and think how you would implement this feature on the database level. What fields and their respective data type should we use?
Our Options
In a nutshell, we need to make it so that a Ticket
, the database entity of interest, can be checked by an admin. This seems like a simple case, just use a BOOLEAN
field with something like has_been_checked_by_admin
and toggle it to TRUE
once an admin has got their hands on the ticket:
Now, it’s reasonable to assume that the date and time that the ticket has been checked is an important detail for this ticketing system (seeing that it’s a queue and all). How about we add another DATETIME
field that holds the specific time wherein the ticket has been checked:
With this setup, a ticket can be checked off with a rough API like this:
// PriorityTicketController
public function store()
{
$ticket = Ticket::findOrFail(request()->post('id'));
$ticket->markAsChecked() // toggles the boolean
->on(now()); // sets the datetime
}
// tickets.blade.php
@foreach ($tickets as $ticket)
Approved? : {{ $ticket->has_been_checked_by_admin ? 'yes' : 'no' }}
Date of Approval: {{ $ticket->checked_by_admin_at }}
@endforeach
Now let’s look at the option that this article is pointing to: using a nullable TIMESTAMP
to simplify the process.
We can now use a similar API but using a simpler schema:
// PriorityTicketController
public function store()
{
$ticket = Ticket::findOrFail(request()->post('id'));
$ticket->markAsChecked(); // sets the has_been_checked_by_admin_at to the current time
}
// tickets.blade.php
@foreach ($tickets as $ticket)
// Both information can be inferred by a single field
Approved? : {{ $ticket->has_been_checked_by_admin_at ? 'yes' : 'no' }}
Date of Approval: {{ $ticket->has_been_checked_by_admin_at }}
@endforeach
To clarify, we use the has_been_checked_by_admin_at
as a boolean by checking for truthy-ness (it defaults to null
, which can be reasoned as false
). Once it was set to a datetime, the field becomes truthy and can be used as a good-ol’ datetime for whatever purposes it may serve.
Stepping Back
Okay, was that much better?. I personally think it does. Having a single source of truth for a pair of information that were tightly coupled anyway makes it easier to reason with the data. There’s no "”what if I set the approval date but forgot to toggle the boolean”“ and vice versa.
As for downsides, some may argue that this pattern reeks of a ““hack”” and it would serve us well to use the classic data types for our classic needs (BOOLEAN
for a ““what”” and DATETIME
for a ““when””). It’s also a valid point that what if we need to separate the field into two down the line? If it ever came to be that we want to set the checked_by_admin_at
(which defaults to null) into something without touching the has_been_checked_by_admin
field, then yes, we lost that flexibility by using a timestamp. I personally think that’s overthinking the problem, and it doesn’t make sense at all, but I digress.
Conclusion
In this article, we looked at a common pattern of using a TIMESTAMP
to infer both BOOLEAN
and DATETIME
values. We also covered why it’s a good thing and some point on why it might not be a good idea. Now go ahead and try it out on your next project!
Got any feedback or suggestions? Feel free to send me an email or a tweet.
Ciao!
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK