TE
科技回声
首页24小时热榜最新最佳问答展示工作
GitHubTwitter
首页

科技回声

基于 Next.js 构建的科技新闻平台,提供全球科技新闻和讨论内容。

GitHubTwitter

首页

首页最新最佳问答展示工作

资源链接

HackerNews API原版 HackerNewsNext.js

© 2025 科技回声. 版权所有。

Ask HN: Constraint to enforce pairwise distinct values in two columns in table

1 点作者 prmph5 个月前
I&#x27;m kind of stumped by a seemingly simple problem in Db design, but the more I think about about it, the more it twists my mind.<p>Let&#x27;s say I have columns C1 and C2 in a table. How do I enforce that the pairing of values of C1 and C2 is pairwise disticnt? That is, a specific composite value for C1 and C2, say X and Y, can appear any number of times in the table, but once X&#x2F;Y appears, you cannot have X&#x2F;Z, or W&#x2F;Y.<p>It seems such a simple constraint to model, but several hours of research provides no clear solutions. I&#x27;m using PostgreSQL, but I guess the issue is db-agnostic. I explored partial indexes, but that feature seems too limited for this problem. Any ideas?<p>To put this in more context, my problem arises from trying to design a users table that maintains version history with the following columns:<p>- internal_id (auto-generated integer, primary key),<p>- external_id (varchar, not null),<p>- auth_id (varchar, not null, phone #, email address, etc),<p>- display_name (varchar),<p>- access_level (integer, not null),<p>- record_version_number (integer),<p>- record_version_created_by (varchar, not null),<p>- when_record_version_created (timestampz, not null),<p>- when_record_version_expired (timestampz),<p>- unique (external_id, record_version_number)<p>A record is valid from <i>when_record_version_created</i> to <i>when_record_version_expired</i>. This should basically work, but without enforcing the pairwise distinct relationship between <i>external_id</i> and <i>auth_id</i>, it would be possible to associate an active <i>auth_id</i> with more than one <i>external_id</i>, which would corrupt the data.

2 条评论

ldj485 个月前
Perhaps doing less in the user table and adding an auth table?<p>Split out the internal_id and external_id from the auth_id and dates.<p>Of course, this assumes you are controlling the input of the value for the external_id column. In the example below, the external_id is populated by calling a function to get a cuid version 2 ID by default. You may have some other mechanism that gives you a unique external_id.<p>Here is an example (in Postgresql):<p>create table users_hn (internal_id int primary key generated always as identity, external_id text not null default cuid2(&#x27;&#x27;::character varying, 12, &#x27;hackernewsusersxmpl&#x27;::character varying), -- or whatever method to generate an external_id display_name text, access_level int not null );<p>create table users_hn_auth (users_hn_auth_id int generated always as identity, users_hn_internal_id int, auth_id text not null, start_date timestamptz check(start_date &gt;= ((current_timestamp::date)::timestamp)), constraint fk_users_hn_internal_id foreign key (users_hn_internal_id) references users_hn(internal_id) );<p>&#x2F;* Intial insert of new user. Only generate this user row once. <i>&#x2F; with insert_user_hn as ( insert into users_hn(display_name, access_level) values (&#x27;John Doe 1&#x27;, 1) returning internal_id ) insert into users_hn_auth(users_hn_internal_id, auth_id, start_date) select insert_user_hn.internal_id, &#x27;johndoe1@example.com&#x27;, current_timestamp from insert_user_hn ;<p>&#x2F;</i> Update user auth table with new data, for a specific user. All changes to auth_id and when that event occurs are recorded here. <i>&#x2F; insert into users_hn_auth(users_hn_internal_id, auth_id, start_date) --values (2, &#x27;johndoe1@example.com&#x27;, current_timestamp) --values (2, &#x27;jdoe1@example.com&#x27;, current_timestamp) --values (2, &#x27;johnd1@example.com&#x27;, current_timestamp) values (2, &#x27;(555) 555-1234&#x27;, current_timestamp) ;<p>&#x2F;</i> Get user info This query will need changes as more users are inserted into the table. A function with parameter of the internal_id to retrieve a specific user, for example.<p>*&#x2F; select u.internal_id, u.external_id, u.display_name, u.access_level, a.users_hn_auth_id, a.auth_id, a.start_date from users_hn u join users_hn_auth a on u.internal_id = a.users_hn_internal_id and a.start_date = (select au.start_date from users_hn_auth au group by au.users_hn_internal_id, au.start_date order by au.start_date desc limit 1 ) ;
rini175 个月前
Use before insert&#x2F;update triggers? Which can do such queries and either fail or ignore if such row is found.
评论 #42327242 未加载