我有一张桌子,里面有这样的行:
+----------+--------------+---------------------+------+
| CASE_ID | CAT | BIRTH | TYPE |
+----------+--------------+---------------------+------+
| 20033738 | CASE_OWNER | 1996-04-08 00:00:00 | NULL |
| 20033738 | WIFE | 1995-08-22 00:00:00 | NULL |
| 20039334 | CASE_OWNER | 1994-03-10 00:00:00 | NULL |
| 20039301 | CASE_OWNER | 1999-07-27 00:00:00 | NULL |
| 20039301 | WIFE | 2001-07-05 00:00:00 | NULL |
| 20039301 | CHILD | 2018-10-22 00:00:00 | NULL |
| 20033831 | CASE_OWNER | 1975-03-05 00:00:00 | NULL |
| 20033831 | CHILD | 2005-03-19 00:00:00 | NULL |
| 20033831 | CHILD | 2006-03-25 00:00:00 | NULL |
| 20033831 | CHILD | 2010-05-20 00:00:00 | NULL |
| 20033831 | CHILD | 2013-10-25 00:00:00 | NULL |
+----------+--------------+---------------------+------+
对于一个 CASE_ID,每个 CASE_OWNER 的组合都有或没有 WIFE 和/或 CHILD(1 个或多个)都是可能的。
对于每个 CASE_ID,我想根据在 CAT 和 BIRTH 中找到的信息设置列 TYPE:
a) 如果 CASE_ID 中只有 CASE_OWNER,则 TYPE 应为
- a1) 匹配 CASE_ID 的列 TYPE 中的 SINGLE_PERSON,如果 CASE_OWNER 早于 21
- a2) 与 CASE_ID 匹配的列 TYPE 中的 UNKNOWN,如果 CASE_OWNER 小于 21
b) 如果 CASE_ID 的 CASE_ID 具有 CAT CASE_OWNER AND WIFE (WITHOUT CHILD),则在与特定 CASE_ID 匹配的每一行中,TYPE 都应为 PAIR_NO_CHILD。
c) 如果一个 CASE_ID 有 CAT CASE_OWNER AND WIFE AND 1 个或多个 CHILD(ren) 对应一个 CASE_ID,则 TYPE 应该是
- c1) PAIR_WITH_CHILD 如果在匹配特定 CASE_ID 的每一行中有一个或多个 CHILD(ren) 低于 21。
- c2) OTHER 如果所有 CHILD(ren) 在与特定 CASE_ID 匹配的每一行中都是 21。
d) 如果一个 CASE_ID 有 CAT CASE_OWNER 并且一个 CASE_ID 有 1 个或多个 CHILD(ren),则 TYPE 应该是
- d1) SINGLE_WITH_CHILD,以防匹配特定 CASE_ID 的每一行中一个或多个(不是全部)CHILD(ren) 低于 21。
- d2) 如果所有 CHILD(ren) 都在 21 岁以上,则为 MULTIPLE。
e) 所有其他组合将是 TYPE == UNKNOWN。
我的问题是:
- 使用 SQL 是否可行?
- 这应该使用 SQL 还是使用编程语言来解决?
- 如果这在 SQL 中是可行的 - 应该怎么做?
非常感谢您的反馈
介绍:
你有两种可能做你想做的事——一种适用于 MySQL 5.5(使用聚合)及更高版本,另一种适用于 MySQL 8 及更高版本(使用窗口函数)。
下面的所有代码都可以在 fiddle here上找到。注意:小提琴适用于 MySQL 版本 8。如果您希望运行版本 5.5(或 5.6 或 5.7),请在小提琴顶部的下拉列表中更改服务器。我这样做是因为
EXPLAIN ANALYZE
只能与 MySQL > 8.0.18 一起使用 - 以前的版本无法使用!首先创建您的表格(您应该以小提琴的形式自己提供此类问题的表格)。我对您的架构进行了一些更改:
然后填充它。我添加了一些记录用于测试目的:
很重要
理解该
CASE
陈述对于遵循此答案的其余部分至关重要。查询将向下进行CASE
,当遇到第一个 匹配条件时,它将执行分配,然后退出CASE
并从下一条记录重新开始——这有点像C
(和其他)编程语言CONTINUE
语句,用于跳出循环并从下一次迭代重新开始。这就是为什么
a
DEFAULT
对于跟踪您的作业是否正确进行以及您没有错过任何内容很重要你必须为你的条件有一条清晰的道路。别人里面有
CASE
说法的时候,很容易迷惑自己!第一种查询形式(使用窗口函数 - 仅在 MySQL >= 8 中可用)。
我将逐节介绍它,因为有几个棘手的地方!
第 1 节:
这涵盖
a) 如果 CASE_ID 中只有 CASE_OWNER,则 TYPE 应为
这是 a
CASE
内的一个示例CASE
!如果只有一个给定的记录,case_id
那么根据定义,它必须是案例所有者!然后,我们检查他们的生日,如果他们超过 21 岁(正常情况),则将值设置为SINGLE_PERSON
,UNKNOWN
否则!这
COUNT(case_id) OVER(...
是一个窗口函数的例子。这些功能非常强大,非常值得深入了解(此处的简短介绍)——它们将回报您多次学习它们所付出的任何努力!这里还有其他计算年龄的方法- 取决于您需要的精度。
第2节:
这涵盖了这种情况:
这里有趣的片段是
SUM(CASE WHEN...
允许我们区分case_id
有和没有 s 的结构WIFE
。第三节:
这涵盖了以下情况:
c) 如果一个 CASE_ID 有 CAT CASE_OWNER AND WIFE AND 1 个或多个 CHILD(ren) 对应一个 CASE_ID,则 TYPE 应该是
第四节:
这涵盖了以下情况:
d) 如果一个 CASE_ID 有 CAT CASE_OWNER 并且一个 CASE_ID 有 1 个或多个 CHILD(ren),则 TYPE 应该是
第五节:
在这里,我们使用
ORDER BY
带有CASE
“嵌入”的 an。这使我们能够完全控制记录的排序 - 考虑到要求,这是一种合乎逻辑的排序方法,对测试非常有帮助。结果:
查询的第二种形式(使用聚合和子查询) - 从至少 5.5 开始工作:
结果:
只需注意几点:
如上所述,请始终在适当的时候提出您的问题——通常是如果您想显示任何类型的数据!
PAIR_WITH_CHILD
sounds incongruous - a"pair"
normally refers to wildlife of some sort, or possibly domestic or farm animals, but not humans! However,both '"child"and
"wife"` definitely refer to human beings. You might want to put "Couple with children" or similar!I've included an
UPDATE
at the bottom of the fiddle.So, to answer the questions:
Yes, see above.
There's no reason not to use SQL in this case. SQL is now Turing complete, however just because you can to do something in a given language, doesn't mean that you should do it in that language.
There will come a point where you have very complex requirements where using SQL will lead to diminishing returns in terms of your effort vs. outcome - experience will tell you when it's better to use another tool!
See above! A fiddle showing how to update using aggregates and a CTE is given here.
Finally, a Performance analysis:
I looked at the plans (from MySQL >= 8) and can't make much sense of them! The usual caveats about performance testing apply - there are only 22 records in this dataset. You should test your query/queries on your own full dataset with your own hardware and other system constraints. Just for the record however, on a locally installed instance of MySQL (8.0.27), Windows 11, 16GB of RAM, 8-core processor, 512GB NVMe drive I obtained these results:
MySQL:
Q2: 'Aggregates_no_ORDER_BY - beginning'; 0.187165 s
Q1: 'Window_no_ORDER_BY - end'; 0.229719 s
Q2: 'Aggregates_with_ORDER_BY - end'; 0.296987 s
Q1 'Window_with_ORDER_BY - end'; 0.344441 s
PostgreSQL (same machine) - using EnterpriseDB's 14.1 binary from here. See here for a PostgreSQL fiddle with
EXPLAIN (ANALYZE, BUFFERS, VERBOSE) <query>
.Q1: Windows_order_by 1.328 ms
Q2: Windows-NO-order_by 1.35 ms
Q1: Aggregate_order_by 1.8 ms
Q2: Aggregate_no_order_by 2.7 ms
The results for MySQL don't appear to align with the complexity of the plans or the fact that the table has to be scanned 4 times (or does it?).
What is really puzzling is that MySQL is 140 times slower than PostgreSQL? Frankly, I'm baffled - you'll have to test for yourself.
UPDATE
KISS——为每个案例编写(并运行)单独的。CASE
有时可以用来将多个条件组合成一个更新,但读到这样会让我的大脑受伤。如果一个更新正在修改后续更新将测试的列,请确保以合适的顺序运行更新。