sql - 加入和分组时避免无用的子查询或聚合

标签 sql postgresql postgresql-9.2

我在聊天数据库中有两个表,roommessage:

CREATE TABLE room (
    id serial primary key,
    name varchar(50) UNIQUE NOT NULL,
    private boolean NOT NULL default false,
    description text NOT NULL
);

CREATE TABLE message (
    id bigserial primary key,
    room integer references room(id),
    author integer references player(id),
    created integer NOT NULL,
);

假设我想获取包含来自用户的消息数量和最近消息日期的房间:

 id | number | last_created | description |      name        | private 
----+--------+--------------+-------------+------------------+---------
  2 |   1149 |   1391703964 |             | Dragons & co     | t
  8 |    136 |   1391699600 |             | Javascript       | f
 10 |     71 |   1391684998 |             | WBT              | t
  1 |     86 |   1391682712 |             | Miaou            | f
  3 |    423 |   1391681764 |             | Code & Baguettes | f
  ...

我看到两个解决方案:

1) 选择/分组消息并使用子查询获取房间列:

select m.room as id, count(*) number, max(created) last_created,
(select name from room where room.id=m.room),
(select description from room where room.id=m.room),
(select private from room where room.id=m.room)
from message m where author=$1 group by room order by last_created desc limit 10

这使得 3 个几乎相同的子查询。这看起来很脏。我可以将其反转为仅对消息列执行 2 个 suqueries,但不会好多少。

2)选择两个表并对所有列使用聚合函数:

select room.id, count(*) number, max(created) last_created,
max(name) as name, max(description) as description, bool_or(private) as private
from message, room
where message.room=room.id and author=$1
group by room.id order by last_created desc limit 10

所有这些聚合函数看起来既乱又无用。

这里有干净的解决方案吗?

这对我来说似乎是一个普遍的问题。从理论上讲,这些聚合函数是无用的,因为根据构造,所有连接的行都是同一行。我想知道是否有通用的解决方案。

最佳答案

尝试在子查询中执行分组:

select m.id, m.number, m.last_created, r.name, r.description, r.private
from (
    select m.room as id, count(*) number, max(created) last_created
    from message m 
    where author=$1 
    group by room 
) m
 join room r
   on r.id = m.id
order by m.last_created desc limit 10

编辑:另一种选择(可能具有类似的性能)是将该聚合移动到 View 中,例如:

create view MessagesByRoom
as 
select m.author, m.room, count(*) number, max(created) last_created,
from message m 
group by author, room

然后像这样使用它:

select m.room, m.number, m.last_created, r.name, r.description, r.private
from MessagesByRoom m
 join room r
   on r.id = m.room
where m.author = $1
order by m.last_created desc limit 10

关于sql - 加入和分组时避免无用的子查询或聚合,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21609165/

相关文章:

python - Psycopg2 - 返回前 1000 个结果并随机选择一个

json - JSON 数据类型上的 UNION ALL

mysql - 如果其中一个字段尚未匹配,如何将一个表移动到另一个表

c# - LINQ to Entities 无法识别方法 'Int32 ToInt32(System.String)' 方法,并且无法将此方法翻译成存储表达式

sql - 合并两个 STRING_SPLIT

postgresql - 在 SELECT 中显示每个位置的本地时区

postgresql - 如何将前缀匹配附加到 PostgreSQL 中的 tsquery

postgresql - 引用继承表的外键

java - 查询后如何匹配用户名和密码

postgresql - 已安装 Postgres.app 但无法运行