我有3个表:工作,记录,发布
1个作品可以有多个录音,并且只有1个发行版中出现1个录音
TABLE:work
+---------+-----------+
| 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
,我想返回recording
,其中is_art = 1
和is_vid = 0
与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)|
+---------+---------------------------------------+
到目前为止,我已经创建了此查询,但是老实说,作为一个新手,我知道这是完全错误的。它返回重复的行。我跌倒了,好像需要使用group by
和子查询,但是经过两天的搜索和测试工作之后,我无法创建解决方案……我发疯了
样本数据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 | | | |
这似乎得到了正确的答案:
-- 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
或将临时表复制到另一个临时表中。
MySQL 8.0和MariaDB 10.2+可以使用WITH
,以有效地重复使用临时表。但是,潜在的问题是需要在临时表中添加AUTO_INCREMENT
列。
确定,这是解决“重新打开”问题的方法:
-- 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。
这里是为您的样本数据产生预期结果的查询:
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
表与recording
联接在一起。从示例数据和结果看,您似乎希望在对行顺序进行排序时将null
放在首位:这不是MySQL的默认行为,因此我们使用了一种技巧,即按- <column_name> desc
进行排序( null
首先是升序排列。
注意:release
是一个reserved word in MySQL,所以我将该表命名为releases
(否则,您需要用反引号将其括起来)。
work_id | work_name | recording_id | recording_name------:| :-------- | -----------:| :-------------1 |你好45 |你好43 |露娜| 78 |露娜55 |我的自我23 |我的自我(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(与上述结果相同)
获取每个recording
的最新work_id
,您可以使用聚合函数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