MySQL:我需要return行根据某些条件
MySQL: I need to return rows according to certain conditions
我有3个表:工作,录音,发布
1 件作品可以有多个录音,1 个录音仅出现在 1 个版本中
TABLE: 工作
+---------+-----------+
| work_id | name |
+---------+-----------+
| 1 | Hello |
| 3 | Luna |
| 4 | Feel good |
| 5 | My self |
+---------+-----------+
TABLE: 录音
+---------------------------------------------------------------------+
| recording_id | work_id | release_id | name | is_art | is_vid |
+---------------------------------------------------------------------+
| 45 | 1 | 45 | Hello4 | 1 | 0 |
| 78 | 3 | 67 | Luna5 | 1 | 0 |
| 23 | 5 | 128 | My self (r) | 1 | 0 |
| 95 | 5 | 156 | My self II | 1 | 0 |
| 17 | 4 | 67 | Luna67 | 1 | 0 |
+---------------------------------------------------------------------+
TABLE: 发布
+--------------------------------------------+
| release_id | name | year | month | day |
+--------------------------------------------+
| 45 | Yo | 1998 | 12 | NULL |
| 67 | Testing | 1967 | 3 | 3 |
| 128 | Maybe | 2018 | 10 | 21 |
| 156 | Again | 2018 | 10 | NULL |
+--------------------------------------------+
基本上,对于每个 work
,我想 return recording
其中 is_art = 1
和 is_vid = 0
AND 是 release
最早的(最早的年份、月份和日期)。我可能是一个recording
release
可以有相同的year
、month
和day
。在那种情况下,我想我需要找到一个唯一标识符,所以我会选择最新的 release_id
结果集应如下所示:
+---------+---------------------------------------+
| work_id | name | recording_id | name |
+---------+---------------------------------------+
| 1 | Hello | 45 | Hello4 |
| 3 | Luna | 78 | Luna5 |
| 4 | Feel good | 17 | Luna67 |
| 5 | My self | 23 | My self (r)|
+---------+---------------------------------------+
到目前为止,我创建了这个查询,但老实说,作为一个新手,我知道这完全错了。它 return 重复行。我觉得我需要使用 group by
和子查询,但是经过 2 天的搜索和测试后,我无法创建解决方案...我要疯了
样本数据 1
| work_id | work_name | recording_id | release_id | rec_name | year | month | day |
|---------|---------------------|--------------|------------|-----------------------------------------------------|------|-------|-----|
| 201 | Me ha dicho la luna | 253 | 5 | Me ha dicho la luna | 1998 | 4 | 22 |
| 201 | Me ha dicho la luna | 579 | 528 | Me ha dicho la luna (Moonlight Radio Edit) | 1998 | | |
| 201 | Me ha dicho la luna | 580 | 528 | Me ha dicho la luna (Luna llena Ambience Mix) | 1998 | | |
| 201 | Me ha dicho la luna | 581 | 528 | Me ha dicho la luna (Extended Callejuela's Version) | 1998 | | |
| 201 | Me ha dicho la luna | 582 | 528 | Me ha dicho la luna (Stoned Baby Free Version) | 1998 | | |
| 201 | Me ha dicho la luna | 252 | 1 | Me ha dicho la luna (con Chayanne) | 2006 | | |
样本数据 2
| work_id | work_name | recording_id | release_id | rec_name | year | month | day |
|---------|------------|--------------|------------|---------------------------------------------------------|------|-------|-----|
| 401 | Si amanece | 397 | 26 | Si amanece | 1978 | 7 | 1 |
| 401 | Si amanece | 634 | 309 | Si amanece | 1978 | 7 | 1 |
| 401 | Si amanece | 396 | 257 | Si amanece (con el Mariachi Oro y Plata de Pepe Chávez) | 1979 | | |
| 401 | Si amanece | 564 | 188 | Si amanece | 2001 | | |
| 401 | Si amanece | 394 | 213 | Si amanece | 2001 | | |
| 401 | Si amanece | 395 | 1 | Si amanece | 2006 | | |
| 401 | Si amanece | 638 | 295 | Si amanece | | | |
根据 work_id
获取最新的 recording
您可以使用聚合函数 max()
后跟 group by
子句。
select w.work_id, w.name, r.recording_Id, r.name,
max(cast(concat(coalesce(year, '1000'), coalesce(month, '01'), coalesce(day, '01')) as date))
from work w
join recording r on w.work_id = r.work_id
join release rl on rl.release_id = r.release_id
where r.is_art = 1 and r.is_vid = 0
group by w.work_id, w.name, r.recording_Id, r.name
order by w.work_id
这是一个为您的示例数据生成预期结果的查询:
select
w.work_id,
w.name work_name,
r.recording_id,
r.name recording_name
from work w
inner join recording r
on r.recording_id = (
select r1.recording_id
from recording r1
inner join releases l1 on l1.release_id = r1.release_id
where r1.work_id = w.work_id and r1.is_art = 1 and r1.is_vid = 0
order by -l1.year desc, -l1.month desc, -l1.day desc, r1.release_id desc
limit 1
)
这是通过将 work
table 与 recording
连接起来实现的,使用相关子查询 select 正确的行。从您的示例数据和结果来看,您似乎希望在对行顺序进行排序时将 null
放在首位:这不是 MySQL 中的默认行为,因此我们使用了一个技巧,该技巧包括按- <column_name> desc
(在尊重升序的同时将 null
放在第一位)。
注意:release
是一个 reserved word in MySQL,所以我将其命名为 table releases
(否则,您需要用反引号括起来)。
work_id | work_name | recording_id | recording_name
------: | :-------- | -----------: | :-------------
1 | Hello | 45 | Hello4
3 | Luna | 78 | Luna5
5 | My self | 23 | My self (r)
或者,如果您是 运行 MySQL 8.0,您可以使用 row_number()
来识别正确的录音。根据您的数据集,这可能会或可能不会表现得更好:
select work_id, work_name, recording_id, recording_name
from (
select
w.work_id,
w.name work_name,
r.recording_id,
r.name recording_name,
row_number() over(
partition by r.work_id
order by -l.year desc, -l.month desc, -l.day desc, r.release_id desc
) rn
from work w
inner join recording r
on r.work_id = w.work_id
and r.is_art = 1
and r.is_vid = 0
inner join releases l
on l.release_id = r.release_id
) t
where rn = 1
Demo on DB Fiddle(结果同上)
这似乎得到了 'right' 答案:
-- Query 1
CREATE TEMPORARY TABLE t (
new_id INT AUTO_INCREMENT PRIMARY KEY
)
SELECT w.work_id,
w.name AS work_name,
rec.recording_id,
rec.release_id,
rec.name AS rec_name,
year, month, day
FROM work AS w
JOIN recording AS rec ON rec.work_id = w.work_id
JOIN releaset AS rel ON rel.release_id = rec.release_id
WHERE is_art = 1
AND is_vid = 0
ORDER BY work_id, year, month, day, release_id;
-- Query 2
SELECT work_id, work_name, recording_id, rec_name
FROM ( SELECT MIN(new_id) AS first_id FROM t
GROUP BY work_id, year, month, day, release_id ) AS x
JOIN t ON t.new_id = x.first_id;
不幸的是,它在某些版本上会失败。
MariaDB 10.2+ 不会抱怨 Can't reopen table: 't'
。有两种解决方法:使 t
不是 TEMPORARY
或将临时文件 table 复制到另一个临时文件 table.
MySQL 8.0 和 MariaDB 10.2+ 可以使用 WITH
,有效地重复使用临时 table。但是,一个潜在的问题是需要将 AUTO_INCREMENT
列添加到临时 table.
好的,这是绕过 "reopen" 问题的方法:
-- Query 3
CREATE TEMPORARY TABLE x
SELECT MIN(new_id) AS first_id FROM t
GROUP BY work_id;
-- Query 4
SELECT work_id, work_name, recording_id, rec_name
FROM x
JOIN t ON t.new_id = x.first_id;
然后使用查询 1,3,4。
我有3个表:工作,录音,发布
1 件作品可以有多个录音,1 个录音仅出现在 1 个版本中
TABLE: 工作
+---------+-----------+
| work_id | name |
+---------+-----------+
| 1 | Hello |
| 3 | Luna |
| 4 | Feel good |
| 5 | My self |
+---------+-----------+
TABLE: 录音
+---------------------------------------------------------------------+
| recording_id | work_id | release_id | name | is_art | is_vid |
+---------------------------------------------------------------------+
| 45 | 1 | 45 | Hello4 | 1 | 0 |
| 78 | 3 | 67 | Luna5 | 1 | 0 |
| 23 | 5 | 128 | My self (r) | 1 | 0 |
| 95 | 5 | 156 | My self II | 1 | 0 |
| 17 | 4 | 67 | Luna67 | 1 | 0 |
+---------------------------------------------------------------------+
TABLE: 发布
+--------------------------------------------+
| release_id | name | year | month | day |
+--------------------------------------------+
| 45 | Yo | 1998 | 12 | NULL |
| 67 | Testing | 1967 | 3 | 3 |
| 128 | Maybe | 2018 | 10 | 21 |
| 156 | Again | 2018 | 10 | NULL |
+--------------------------------------------+
基本上,对于每个 work
,我想 return recording
其中 is_art = 1
和 is_vid = 0
AND 是 release
最早的(最早的年份、月份和日期)。我可能是一个recording
release
可以有相同的year
、month
和day
。在那种情况下,我想我需要找到一个唯一标识符,所以我会选择最新的 release_id
结果集应如下所示:
+---------+---------------------------------------+
| work_id | name | recording_id | name |
+---------+---------------------------------------+
| 1 | Hello | 45 | Hello4 |
| 3 | Luna | 78 | Luna5 |
| 4 | Feel good | 17 | Luna67 |
| 5 | My self | 23 | My self (r)|
+---------+---------------------------------------+
到目前为止,我创建了这个查询,但老实说,作为一个新手,我知道这完全错了。它 return 重复行。我觉得我需要使用 group by
和子查询,但是经过 2 天的搜索和测试后,我无法创建解决方案...我要疯了
样本数据 1
| work_id | work_name | recording_id | release_id | rec_name | year | month | day |
|---------|---------------------|--------------|------------|-----------------------------------------------------|------|-------|-----|
| 201 | Me ha dicho la luna | 253 | 5 | Me ha dicho la luna | 1998 | 4 | 22 |
| 201 | Me ha dicho la luna | 579 | 528 | Me ha dicho la luna (Moonlight Radio Edit) | 1998 | | |
| 201 | Me ha dicho la luna | 580 | 528 | Me ha dicho la luna (Luna llena Ambience Mix) | 1998 | | |
| 201 | Me ha dicho la luna | 581 | 528 | Me ha dicho la luna (Extended Callejuela's Version) | 1998 | | |
| 201 | Me ha dicho la luna | 582 | 528 | Me ha dicho la luna (Stoned Baby Free Version) | 1998 | | |
| 201 | Me ha dicho la luna | 252 | 1 | Me ha dicho la luna (con Chayanne) | 2006 | | |
样本数据 2
| work_id | work_name | recording_id | release_id | rec_name | year | month | day |
|---------|------------|--------------|------------|---------------------------------------------------------|------|-------|-----|
| 401 | Si amanece | 397 | 26 | Si amanece | 1978 | 7 | 1 |
| 401 | Si amanece | 634 | 309 | Si amanece | 1978 | 7 | 1 |
| 401 | Si amanece | 396 | 257 | Si amanece (con el Mariachi Oro y Plata de Pepe Chávez) | 1979 | | |
| 401 | Si amanece | 564 | 188 | Si amanece | 2001 | | |
| 401 | Si amanece | 394 | 213 | Si amanece | 2001 | | |
| 401 | Si amanece | 395 | 1 | Si amanece | 2006 | | |
| 401 | Si amanece | 638 | 295 | Si amanece | | | |
根据 work_id
获取最新的 recording
您可以使用聚合函数 max()
后跟 group by
子句。
select w.work_id, w.name, r.recording_Id, r.name,
max(cast(concat(coalesce(year, '1000'), coalesce(month, '01'), coalesce(day, '01')) as date))
from work w
join recording r on w.work_id = r.work_id
join release rl on rl.release_id = r.release_id
where r.is_art = 1 and r.is_vid = 0
group by w.work_id, w.name, r.recording_Id, r.name
order by w.work_id
这是一个为您的示例数据生成预期结果的查询:
select
w.work_id,
w.name work_name,
r.recording_id,
r.name recording_name
from work w
inner join recording r
on r.recording_id = (
select r1.recording_id
from recording r1
inner join releases l1 on l1.release_id = r1.release_id
where r1.work_id = w.work_id and r1.is_art = 1 and r1.is_vid = 0
order by -l1.year desc, -l1.month desc, -l1.day desc, r1.release_id desc
limit 1
)
这是通过将 work
table 与 recording
连接起来实现的,使用相关子查询 select 正确的行。从您的示例数据和结果来看,您似乎希望在对行顺序进行排序时将 null
放在首位:这不是 MySQL 中的默认行为,因此我们使用了一个技巧,该技巧包括按- <column_name> desc
(在尊重升序的同时将 null
放在第一位)。
注意:release
是一个 reserved word in MySQL,所以我将其命名为 table releases
(否则,您需要用反引号括起来)。
work_id | work_name | recording_id | recording_name ------: | :-------- | -----------: | :------------- 1 | Hello | 45 | Hello4 3 | Luna | 78 | Luna5 5 | My self | 23 | My self (r)
或者,如果您是 运行 MySQL 8.0,您可以使用 row_number()
来识别正确的录音。根据您的数据集,这可能会或可能不会表现得更好:
select work_id, work_name, recording_id, recording_name
from (
select
w.work_id,
w.name work_name,
r.recording_id,
r.name recording_name,
row_number() over(
partition by r.work_id
order by -l.year desc, -l.month desc, -l.day desc, r.release_id desc
) rn
from work w
inner join recording r
on r.work_id = w.work_id
and r.is_art = 1
and r.is_vid = 0
inner join releases l
on l.release_id = r.release_id
) t
where rn = 1
Demo on DB Fiddle(结果同上)
这似乎得到了 'right' 答案:
-- Query 1
CREATE TEMPORARY TABLE t (
new_id INT AUTO_INCREMENT PRIMARY KEY
)
SELECT w.work_id,
w.name AS work_name,
rec.recording_id,
rec.release_id,
rec.name AS rec_name,
year, month, day
FROM work AS w
JOIN recording AS rec ON rec.work_id = w.work_id
JOIN releaset AS rel ON rel.release_id = rec.release_id
WHERE is_art = 1
AND is_vid = 0
ORDER BY work_id, year, month, day, release_id;
-- Query 2
SELECT work_id, work_name, recording_id, rec_name
FROM ( SELECT MIN(new_id) AS first_id FROM t
GROUP BY work_id, year, month, day, release_id ) AS x
JOIN t ON t.new_id = x.first_id;
不幸的是,它在某些版本上会失败。
MariaDB 10.2+ 不会抱怨
Can't reopen table: 't'
。有两种解决方法:使t
不是TEMPORARY
或将临时文件 table 复制到另一个临时文件 table.MySQL 8.0 和 MariaDB 10.2+ 可以使用
WITH
,有效地重复使用临时 table。但是,一个潜在的问题是需要将AUTO_INCREMENT
列添加到临时 table.
好的,这是绕过 "reopen" 问题的方法:
-- Query 3
CREATE TEMPORARY TABLE x
SELECT MIN(new_id) AS first_id FROM t
GROUP BY work_id;
-- Query 4
SELECT work_id, work_name, recording_id, rec_name
FROM x
JOIN t ON t.new_id = x.first_id;
然后使用查询 1,3,4。