我使用Postgres中的以下两个表保存动态对象(我事先不知道其类型):

CREATE TABLE IF NOT EXISTS objects(
    id UUID NOT NULL DEFAULT gen_random_uuid(),
    user_id UUID NOT NULL,

    name TEXT NOT NULL,

    PRIMARY KEY(id)
);

CREATE TABLE IF NOT EXISTS object_values(
    id UUID NOT NULL DEFAULT gen_random_uuid(),
    event_id UUID NOT NULL,

    param TEXT NOT NULL,
    value TEXT NOT NULL,
);

例如,如果我有以下对象:

dog = [
  { breed: "poodle", age: 15, ...},
  { breed: "husky", age: 9, ...},
}
monitors = [
  { manufacturer: "dell", ...},
}

它将按如下方式驻留在数据库中:

-- objects
| id | user_id | name    |
|----|---------|---------|
| 1  | 1       | dog     |
| 2  | 2       | dog     |
| 3  | 1       | monitor |

-- object_values
| id | event_id | param        | value  |
|----|----------|--------------|--------|
| 1  | 1        | breed        | poodle |
| 2  | 1        | age          | 15     |
| 3  | 2        | breed        | husky  |
| 4  | 2        | age          | 9      |
| 5  | 3        | manufacturer | dell   |

注意,这些桌子很大(数以亿计).通常针对写作进行了优化. 根据多个对象参数查询/过滤对象的好方法是什么?例如: Select 每个唯一用户10岁以上的所有husky只狗的数量.

我还想知道,对表进行反规范化并将参数折叠到JSON列(并使用GIN索引)是否会更好.

这里有什么我可以使用的标准吗?

推荐答案

"Select the number of all husky dogs above the age of 10 per unique user"-下面的查询可以做到这一点.

SELECT user_id, COUNT(DISTINCT event_id) AS num_husky_dogs_older_than_10
FROM       objects       o
INNER JOIN object_values ov
        ON o.id_ = ov.event_id
       AND o.name_ = 'dog'
GROUP BY o.user_id
HAVING MAX(CASE WHEN ov.param = 'age' 
                 AND ov.value_::integer >= 10 THEN 1 END) = 1
   AND MAX(CASE WHEN ov.param = 'breed'
                 AND ov.value_ = 'husky'      THEN 1 END) = 1;

由于您的查询很可能受到在相同字段上的这两个表之间始终执行相同的JOIN操作的影响,因此最好将indices设为:

  • 您连接的字段("objects.id"、"object_values.event_id")
  • 您筛选的字段("objects.name"、"object_values.param"、"object_values.value_")

查看演示here.

Postgresql相关问答推荐

Org.postgresql.util.PSQLException:错误:函数LOWER(BYTEA)不存在

Pogresql性能中的枚举与文本数据类型

如何在PostgreSQL中更改分区的表空间?

PG 16 的 AGE 安装抛出错误:无法创建 src/backend/parser/ag_scanner.c

PostgresQL:获取两个数组之间的对应数量

将 postgres 从属提升为主 node

postgres 的密码

如何让 Flask SQLAlchemy 重用数据库连接?

在 postgres 中删除所有共享相同前缀的表

如何 Select 列值为空的行?

为什么 sqlalchemy 的默认列值不起作用

我如何知道我的 PostgreSQL 服务器是否使用C语言环境?

如何在psql中退出查询结果查看器?

与 Oracle 的 CONNECT BY ... START WITH 等效的 PostgreSQL 语法是什么?

在 PostgreSQL 中跳过每 n 个结果行

FOR EACH STATEMENT 触发器示例

如何使 array_agg() 像 mySQL 中的 group_concat() 一样工作

使用 Postgresql 在过滤器中制作 Sqlalchemy 使用日期

如何在 postgres 查询中排名

重命名 Amazon RDS 主用户名