架构如下:
create table users (
id text constraint users_pk primary key,
followers text[]
);
users.followers
包含字符串格式的id
数组。
我想选择那些作为某个用户的关注者的用户,但以下SQL不起作用:
select *
from users
where id in (
select followers::text[]
from users
where id = '12345678'
);
OR
select *
from users
where id = any (
select followers::text[]
from users
where id = '2219144865'
);
两者都给出错误ERROR: operator does not exist: text = text[]
答案 0 :(得分:2)
您最好更改架构:如果您的id
是数字,请使用
create table users(
id serial not null primary_key
-- other fields
);
并添加一个followers
表,每个关注者包含一行:
create table followers (
user_id int not null references users(id),
follower_id int not null references users(id),
unique (user_id, follower_id)
);
然后您只需为id为X的用户选择关注者:
select * from users
where id in (select follower_id from followers where user_id = X)
关系数据库非常擅长管理关系,但您必须使用它们为此提供的功能:表,行,外键和索引。
将ID列表存储在列中通常是不好的做法,因为这需要使用特殊代码将这样的text
列拆分为单独的ID。必须为结果集中的每一行运行此代码,从而大大减慢了速度。
PostgreSQL确实广泛支持array
columns,但并非所有数据库都支持(例如,MySQL不支持)。它是PostgreSQL特有的功能,它告诉您这不是管理RDBMS中关系的常用方法。
使用上面的双表方法,SQL数据库可以非常有效地运行。例如,由于users.id
是主键,因此它会针对id
使用内部哈希值查找记录进行优化。
根据经验,只要您有一个列表或一组东西,您就会想要按照每一行的方式存储它们。
此处使用数组的另一个缺点是数据库无法保证followers
中的值实际指向现有用户。就数据库而言,它只是一个字符串。通过指定foreign key
数据库将确保数据完整性,也就是说,您不能存储不存在的关注者ID。
你不应该担心空间。 text[]
数组方法甚至可能比使用第二个表使用更多空间。您可以将text[]
视为嵌套表格'在users
表中。它必须存储条目的数量和长度。在实践中,它不会产生太大的影响。
使用像followers
这样的关系表的好处远远超过您对空间的担忧。这就是RDBMS的运作方式,它们在如何操作方面非常有效。
答案 1 :(得分:0)
在我看来,错误的原因是内部select
查询不仅返回数组,而是返回一组数组。每个followers
本身就是一个数组,而select
会返回许多followers
数组。因此,ID不是结果集的成员,因为ID是文本,而不是数组。
您需要将它们连接成一个大数组,然后您可以检查是否存在使用。所以我建议如下(我不确定语法是否正确):
select *
from users
where id in array_cat(
select followers::text[]
from users
where id = '12345678'
);