Mysql 如何在多个链接行中只有一行的列位于'true',其余的列位于'false'?
我环顾四周,发现问题与我的问题相似,但不完全相同,因此他们得到的答案显然不适合我:( 为了尝试描绘整个画面,我有一个数据库,其中包括一个“用户”表,每个用户在另一个表“电话号码”中有一个或多个电话号码。一个用户有一个主电话号码,所以我试图通过在电话号码表中添加一个布尔列“main_number”来解决这个问题,但我无法找到一种方法来确保在一个用户的所有行中,只有一行的该列为“true” [编辑]:更准确地说,“电话号码”表记录可以按“用户Id”排序,并且在每个记录组(用户Id组)中,只有一个主号码位于“true”。因此,“true”中的“主号码”与用户数量相同 有办法吗 对于类似的问题,我发现一般的答案是使用另一个指向主数字行的表。但这样做显然无法通过一个查询检索用户的所有行(包括第三个表中的列),除非是我的请求出错 有人能给我指出正确的方向吗Mysql 如何在多个链接行中只有一行的列位于'true',其余的列位于'false'?,mysql,sql,Mysql,Sql,我环顾四周,发现问题与我的问题相似,但不完全相同,因此他们得到的答案显然不适合我:( 为了尝试描绘整个画面,我有一个数据库,其中包括一个“用户”表,每个用户在另一个表“电话号码”中有一个或多个电话号码。一个用户有一个主电话号码,所以我试图通过在电话号码表中添加一个布尔列“main_number”来解决这个问题,但我无法找到一种方法来确保在一个用户的所有行中,只有一行的该列为“true” [编辑]:更准确地说,“电话号码”表记录可以按“用户Id”排序,并且在每个记录组(用户Id组)中,只有一个主号
Thx您有两种选择 选项1:用户表中的“主电话”列 只需将此列添加到表中,并将其作为主电话进行编码,“phone_Number”表中的数据作为其他可用电话进行编码 优点:易于实现。不难维护。在电话号码表中显示“主号码”列 缺点:当检索所有手机时,你需要一个连接,这将破坏所有索引的使用和查询的性能。如果你的表太大,这将是个问题 选项2:非电话号码表上的触发器 按照您的建议,添加“main_number”列。 在插入、更新和删除之前对一些触发器进行编码,以控制您的限制 插入前: 你应该控制你的限制 更新前: 应该控制你的限制。 如果删除该用户的“主电话”行(如有必要),则应控制发生的情况 删除前: 如果删除该用户的“主电话”行(如有必要),则应控制发生的情况 优点:易于检索,易于维护(一旦开发)
缺点:难以编码。这不是一个答案,而是一个不适合评论部分的评论 尽管MySQL不支持部分索引,但我想展示如何在PostgreSQL(Oracle、SQL Server、SQLite)中实现这一点,让您知道这是可能的 例如:
create table users (
id int not null,
phone varchar(10) not null,
main_number boolean not null
);
create unique index ix1 on users (id, main_number) where main_number;
insert into users (id, phone, main_number) values (1, '123', true);
insert into users (id, phone, main_number) values (1, '456', false);
insert into users (id, phone, main_number) values (1, '789', false); -- succeeds
insert into users (id, phone, main_number) values (1, '468', true); -- fails
如您所见,第三次插入成功,因为每个
id
允许多个false
值。但是,第四次插入失败,因为每个id
只允许一个true
值。您可以使用唯一的约束和生成的列来执行此操作
alter table t add is_main_number boolean
generated always as (case when main_number then 1 end);
然后,您可以在唯一索引中使用它:
create unique index unq_t_user_number_main on t(user_id, is_main_number)
MySQL允许在唯一索引中重复NULL
值,所以这就是您想要的
是一把小提琴。这很棘手。原因如下: 您希望每个用户都有一个主电话号码。因此,如果用户只有一个电话号码,则这是主电话号码。如果用户有四个号码,则其中一个号码必须是主号码,其他号码必须是辅助号码 …至少在提交到表时! 假设一个用户有两个条目。123456是主号码654321,是次号码。现在用户希望654321成为他们的主号码 这必须起作用:
start transaction;
update user_phone set main = true where number = '654321';
-- Just for this microsecond there are two main numbers for this user.
update user_phone set main = false where number = '123456';
commit;
-- The user has one main number again.
这是:
start transaction;
update user_phone set main = false where number = '123456';
-- Just for this microsecond there are only secondary numbers for this user.
update user_phone set main = true where number = '654321';
commit;
-- The user has one main number again.
但不是这个:
start transaction;
update user_phone set main = true where number = '654321';
commit;
-- There are two main numbers now for the user.
或者这个:
start transaction;
update user_phone set main = false where number = '123456';
commit;
-- There are only secondary numbers now for the user.
在某些DBMS中,您可以使用延迟约束来解决此问题,即仅在提交时应用的约束。在用户表中,您的电话表中还将添加一个主电话ID,然后插入一个用户,然后插入他们的电话,然后在一个事务中使用主电话更新用户。提交时,所有数据将保持一致。如果没有,则违反外键约束将触发。MySQL没有延迟约束
下面是我如何解决这个问题:给电话号码一个等级。这可以是1, 2, 3,或10, 20, 30,…实际上不重要;你会考虑最低等级号码的主要电话。
create table user_phone
(
user_id int not null,
phone varchar(20) not null,
prio int not null,
unique (user_id, phone),
unique (user_id, prio)
);
相关查询:
select
user_id, phone,
case when row_number() over (partition by user_id order by prio) = 1
then 'main'
else 'secondary'
end as type
from user_phone
order by user_id, type;
如果你想让另一部手机成为主手机,那么只需改变等级即可
update user_phone set prio = prio + 1 where user_id = 1;
-- Still the same order, still the same main number.
update user_phone set prio = 1 where user_id = 1 and phone = '54321';
-- Phone '54321' is the new main number for user 1.
分析函数ROW\u NUMBER
需要MySQL版本8。早期版本中不提供该函数
演示:您可以创建一个函数,该函数接受用户Id和电话号码(在电话号码上)作为参数。该函数将用户的所有main_Number值更新为false,然后将传递的电话号码设置为true。这是一个常见的问题,仅凭约束是不容易解决的。在某些DBMS中,这是可能的,但我不知道是否在MySQL中。simpel解决方案是为用户表提供一个冗余的main_phone列。这很容易实现NicolaLepetit:这是我在更新数据库之前想到的java解决方案,但我发现它有点重,所以我想我应该要求另一个解决方案:)谢谢你的提示:-)@ThorstenKettner我一开始是这样做的,当时我每个用户只有一个电话号码,但是现在就像你说的,在用户表中有一个电话号码感觉是多余的:)thx作为你的提示:-)理论上这可以使用“部分索引”实现。不幸的是,MySQL没有实现它们(在MySQL行话中,有一个部分索引,但引用了一个不同的概念)。作为参考,PostgreSQL、Oracle、SQL Server甚至SQLite都支持部分索引,但不支持输入的MySQL.Thx,感觉触发器完全可以解决我这里的问题。现在要实现它:)不完全是我想要的:-)与您的