【发布时间】:2019-05-29 05:36:01
【问题描述】:
假设我有一个简化模型,其中patient 可以有零个或多个events。一个事件有一个category 和一个date。我想支持以下问题:
Find all patients that were given a medication after an operation and
the operation happened after an admission.
其中药物、手术和入院是所有类型的事件类别。有大约 100 个可能的类别。
我预计会有 1000 名患者,每个患者每个类别都有大约 10 个事件。
我想出的天真的解决方案是有两个表,一个patient 和一个event 表。在event.category 上创建一个索引,然后使用内部连接进行查询,例如:
SELECT COUNT(DISTINCT(patient.id)) FROM patient
INNER JOIN event AS medication
ON medication.patient_id = patient.id
AND medication.category = 'medication'
INNER JOIN event AS operation
ON operation.patient_id = patient.id
AND operation.category = 'operation'
INNER JOIN event AS admission
ON admission.patient_id = patient.id
AND admission.category = 'admission'
WHERE medication.date > operation.date
AND operation.date > admission.date;
但是,随着添加更多类别/过滤器,此解决方案无法很好地扩展。对于 1,000 名患者和 45,000 个事件,我看到以下性能行为:
| number of inner joins | approx. query response |
| --------------------- | ---------------------- |
| 2 | 100ms |
| 3 | 500ms |
| 4 | 2000ms |
| 5 | 8000ms |
有人对如何优化此查询/数据模型有任何建议吗?
额外信息:
- Postgres 10.6
- 在 Explain 输出中,
project_result等效于简化模型中的patient。
高级用例:
Find all patients that were given a medication within 30 days after an
operation and the operation happened within 7 days after an admission.
【问题讨论】:
-
感谢 @ErwinBrandstetter 的提醒,添加了 postgres 版本。
-
您对事件表所做的操作称为 EAV 模型。它有利有弊。我个人喜欢它,但我知道它的极限是什么。 (少数)缺点之一是您正在尝试的性能问题。这里没有神奇的解决方案。完全改变模型是其中之一,为查询最多的事件创建物化视图是另一个。如果你用谷歌搜索 EAV 性能问题,你会发现很多想法。
-
@ThomasG:我建议的重写应该导致 3 次仅索引扫描(在理想的读取条件下),并且相比之下表现得像所说的“奇迹”。
-
@ErwinBrandstetter 您的解决方案很好(我赞成),但是它有其局限性,您知道 :) 您使用 3 个类别,但是 10 会发生什么? 3 只猫他有 500 毫秒,5 只他有 8000 毫秒。你的方法也会发生同样的情况,但希望不是相同的规模。
-
@ThomasG:当然,有限制。但这对于 任何 个级别应该很快,因为每个添加的级别都会减少下一步的行数。所以它应该在这方面很好地扩展——除非每个级别都没有选择性。如果早期步骤是有选择性的,这将很有帮助……也许我们会从 OP 那里得到反馈。
标签: sql postgresql performance data-modeling exists