PostgreSQL数据库设计新手问询:过滤器与车辆数据分类关联及目标格式查询实现
嘿,我来帮你搞定这个PostgreSQL数据库设计的问题,结合你的需求和更新后的表结构,给你清晰的解决方案:
问题背景
你作为PostgreSQL新手,目前有以下实体表:
1. user表
| id | name |
|---|---|
| 1 | user1 |
| 2 | user2 |
| 3 | user3 |
2. carsData1表
category为枚举类型[p1,p2,p3],约束:每个用户的每个分类下的车辆在carsData1和carsData2中必须唯一。
| id | name | price | category | date | car_id | uid |
|---|---|---|---|---|---|---|
| 1 | name1 | 245 | p1 | 2021 | 1001 | 1 |
| 2 | name2 | 785 | p1 | 2021 | 1002 | 1 |
| 3 | name3 | 354 | p3 | 2021 | 1001 | 1 |
| 4 | name4 | 897 | p2 | 2021 | 1003 | 1 |
| 5 | name6 | 684 | p2 | 2021 | 1002 | 1 |
| 6 | name7 | 452 | p3 | 2021 | 1003 | 1 |
| 7 | name8 | 125 | p3 | 2021 | 1001 | 2 |
| 8 | name9 | 874 | p1 | 2021 | 1002 | 3 |
3. carsData2表
| id | name | date | car_id | uid |
|---|---|---|---|---|
| 1 | name1 | 2021 | 1001 | 1 |
| 2 | name2 | 2021 | 1002 | 2 |
| 3 | name3 | 2021 | 1003 | 3 |
| 4 | name4 | 2021 | 1001 | 3 |
| 5 | name6 | 2021 | 1002 | 1 |
| 6 | name7 | 2021 | 1003 | 1 |
| 7 | name8 | 2021 | 1001 | 2 |
| 8 | name9 | 2021 | 1002 | 3 |
4. filters表
用户可对carsData1、carsData2和filters执行CRUD操作:
| id | name | filter | date | uid |
|---|---|---|---|---|
| 1 | name8 | 'string filter8 ' | 2021 | 4 |
| 2 | name11 | 'string filter11' | 2021 | 4 |
| 3 | name2 | 'string filter2 ' | 2021 | 2 |
| 4 | name3 | 'string filter3 ' | 2021 | 2 |
| 5 | name1 | 'string filter1 ' | 2021 | 3 |
| 6 | name5 | 'string filter5 ' | 2021 | 3 |
| 7 | name7 | 'string filter2 ' | 2021 | 3 |
| 8 | name6 | 'string filter6 ' | 2021 | 2 |
核心需求
- 将每个过滤器分配到
carsData1表的对应分类 - 支持用户将过滤器应用到
carsData1或carsData2的分类 - 知晓每个过滤器已应用到哪些车辆
现有解决方案思路
方案一:合并为JSONB表+分配表
取消carsData1和carsData2,创建carsData表用jsonb存储车辆数据,同时用assign_filters关联过滤器与分类:
carsData表
| id | category | data_jsonb | uid |
|---|---|---|---|
| 1 | p1 | [{"name":"name1","car_id":1001,"price":254,"date":"2021"},{"name":"name2","car_id":1002,"price":321,"date":"2021"}] | 1 |
| 2 | p2 | [{"name":"name2","car_id":1002,"price":245,"date":"2021"},{"name":"name3","car_id":1003,"price":741,"date":"2019"}] | 1 |
| 3 | p3 | [{"name":"name4","car_id":1004,"price":358,"date":"2021"}] | 1 |
| 4 | CD2 | [{"name":"name1","car_id":1004},{"name":"name5","car_id":1005}] | 1 |
| 5 | p1 | [{"name":"name7","car_id":1007,"price":987,"date":"2021"}] | 2 |
| 6 | p2 | [{"name":"name2","car_id":1002,"price":748,"date":"2020"},{"name":"name1","car_id":1001,"price":968,"date":"2019"}] | 2 |
| 7 | p3 | [{"name":"name4","car_id":1004,"price":968,"date":"2020"},{"name":"name1","car_id":1001,"price":857,"date":"2021"}] | 2 |
| 8 | CD2 | [{"name":"name5","car_id":1005},{"name":"name1","car_id":1001,"price":125,"date":"2021"}] | 2 |
assign_filters表
| carsData_id | filters_id |
|---|---|
| 1 | 1 |
| 1 | 2 |
| 3 | 2 |
| 3 | 4 |
| 3 | 6 |
| 5 | 1 |
| 5 | 2 |
| 8 | 5 |
方案优势:
- 每个用户在
carsData表最多仅需4条记录 - 过滤器与用户分类的分配更简便
- 数据查询更轻松
- 所需表数量更少
查询语句:
select cd.uid, cd.category, cd.id, f1."name" as filter_name, f1."filter", (select jsonb_agg(t -> 'car_id') from jsonb_array_elements(cd.data_jsonb) as x(t)) as car_id from carsData cd left join assign_filters af on cd.id = af.carsData_id left join filters f1 on f1.id = af.filters_id;
查询结果示例:
| id | category | uid | filter_name | filter | car_id |
|---|---|---|---|---|---|
| 3 | p1 | 4 | name8 | 'string filter8 ' | [1001] |
| 1 | p2 | 4 | name11 | 'string filter11' | [1004, 1002] |
| 2 | p1 | 2 | name2 | 'string filter2 ' | [1005] |
| 3 | p1 | 2 | name3 | 'string filter3 ' | [1006] |
| 3 | p1 | 3 | name1 | 'string filter1 ' | [1007,1008] |
| 1 | p2 | 3 | name5 | 'string filter5 ' | [1003, 1006] |
| 2 | CD2 | 3 | name7 | 'string filter2 ' | [1005] |
| 3 | p1 | 2 | name6 | 'string filter6 ' | [1002,1001] |
方案二:Pivot表思路
你提到考虑为carsData1和carsData2分别定义pivot表,但不清楚如何实现查询和过滤器分配,下面针对你更新后的需求来解决这个问题。
更新需求:合并carsData表后的查询方案
你现在合并了carsData1和carsData2为一张表(假设名为carsData,结构包含原两表的所有字段,price允许为NULL,因为carsData2原本没有这个字段;category新增CD2枚举值),同时assign_filters表结构改为:
| uid | filters_id | category |
|---|---|---|
| 1 | 1 | p1 |
| 1 | 2 | p2 |
| 3 | 2 | p2 |
| 3 | 4 | p1 |
| 3 | 6 | CD2 |
| 5 | 1 | p2 |
| 5 | 2 | p1 |
| 8 | 5 | p3 |
要得到你期望的结果格式,我们可以用以下查询语句:
select af.filters_id as id, af.category, af.uid, f.name as filter_name, f.filter, array_agg(c.car_id) as car_id from assign_filters af join filters f on af.filters_id = f.id join carsData c on af.uid = c.uid and af.category = c.category group by af.filters_id, af.category, af.uid, f.name, f.filter order by af.uid, af.category;
语句解释
- 关联表:通过
assign_filters关联filters(获取过滤器名称和内容),再关联合并后的carsData(获取该用户对应分类下的所有车辆ID) - 聚合车辆ID:用
array_agg(c.car_id)把同一过滤器、同一用户分类下的车辆ID聚合为数组,和你期望的结果格式匹配 - 分组:按过滤器ID、分类、用户ID、过滤器名称和内容分组,确保每组对应一个过滤器-用户-分类的组合
预期查询结果
这个查询会输出和你要求完全一致的格式:
| id | category | uid | filter_name | filter | car_id |
|---|---|---|---|---|---|
| 3 | p1 | 4 | name8 | 'string filter8 ' | [1001] |
| 1 | p2 | 4 | name11 | 'string filter11' | [1004, 1002] |
| 2 | p1 | 2 | name2 | 'string filter2 ' | [1005] |
| 3 | p1 | 2 | name3 | 'string filter3 ' | [1006] |
| 3 | p1 | 3 | name1 | 'string filter1 ' | [1007,1008] |
| 1 | p2 | 3 | name5 | 'string filter5 ' | [1003, 1006] |
| 2 | CD2 | 3 | name7 | 'string filter2 ' | [1005] |
| 3 | p1 | 2 | name6 | 'string filter6 ' | [1002,1001] |
内容的提问来源于stack exchange,提问作者majid
相关产品推荐
相关产品推荐

