从另一个选择查询的数组中选择行

时间:2015-11-28 15:21:55

标签: sql postgresql

架构如下:

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[]

2 个答案:

答案 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'
);