IVOguy
Enforce uniqueness on null
I have a set of Events, that have a date. The dates are only given in full days (ie 2022-08-06). To keep track of the order of Events within one day, I keep a previous_event_id key for each event so that I can create a linkedlist. When I create a first Event on a particular day, it will have a previous_event_id of NULL.
So a set of events in my DB could look something like this:
| id | date | previous_event_id |
|---|---|---|
| 1 | 2022-01-01 | NULL |
| 2 | 2022-01-01 | 1 |
| 3 | 2022-01-01 | 2 |
| 4 | 2022-01-02 | NULL |
| 5 | 2022-01-02 | 4 |
| 6 | 2022-01-02 | 5 |
| 7 | 2022-01-03 | NULL |
Now, I want to enforce that only one event per day can have a NULL value. Is this even possible, and if so, how do I do this? I have the following code in my model but the unique constraint allows multiple NULL values.
def changeset(event, attrs) do
event
|> cast(attrs, [
:name,
:date,
:previous_event_id
])
|> unique_constraint([:previous_event_id, :date])
|> validate_required([
:name,
:date,
])
end
I considered using -1 instead of NULL, but (when not NULL / -1) it is also a reference, so then I would have a validation error on -1 not being an existing event id.
So, how (if at all) do I enforce a table to have only one NULL row? (Or, in this case, only one NULL row per date).
Marked As Solved
benwilson512
You can do this by making a “partial index” that has a where clause:
create index(:events, [:date], where: "previous_event_id is null", unique: true)
Also Liked
thomas.fortes
Friendly reminder (for future readers that may find this), if you want only one NULL per table, PostgreSQL 15 (which is at Beta 2 at the moment) has support for treating null values as not distinct and you can enforce this with a simple unique constraint instead of a partial index.
https://www.postgresql.org/docs/15/ddl-constraints.html#DDL-CONSTRAINTS-UNIQUE-CONSTRAINTS
Popular in Questions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #javascript
- #code-sync
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance









