MySQL: мне нужно вернуть строки в соответствии с определенными условиями - PullRequest
1 голос
/ 13 января 2020

У меня есть 3 таблицы: работа, запись, выпуск

1 работа может иметь несколько записей и 1 запись появляется только в 1 выпуске

TABLE: работа

+---------+-----------+
| work_id | name      |
+---------+-----------+
| 1       | Hello     | 
| 3       | Luna      | 
| 4       | Feel good | 
| 5       | My self   | 
+---------+-----------+

ТАБЛИЦА: запись

+---------------------------------------------------------------------+
| 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      |
+---------------------------------------------------------------------+

ТАБЛИЦА: выпуск

+--------------------------------------------+
| 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. В этом случае, я думаю, мне нужно найти уникальный идентификатор, поэтому плохо go с последним 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)|
+---------+---------------------------------------+

Пока я создал это запрос, но, если честно, как новичок ie, я знаю, что все неправильно. Он возвращает повторяющиеся строки. Я упал, как будто мне нужно использовать group by и подзапросы, но после 2 дней поиска и тестирования я не могу создать решение ... я схожу с ума

SAMPLE DATA 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 |       |     |

SAMPLE DATA 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                                              |      |       |     |

Ответы [ 3 ]

2 голосов
/ 20 января 2020

Вот запрос, который дает ожидаемые результаты для ваших образцов данных:

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 s при сортировке порядка строк: это не стандартное поведение в MySQL, поэтому мы используем прием, который заключается в упорядочении по - <column_name> desc ( который ставит null s на первое место при сохранении сортировки по возрастанию).

Примечание: release является зарезервированным словом в MySQL, поэтому я назвал эту таблицу releases вместо этого ( в противном случае необходимо заключить его в кавычки).

Демонстрация на DB Fiddle :

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

Демонстрация на DB Fiddle (те же результаты, что и выше)

1 голос
/ 20 января 2020

Кажется, что получен «правильный» ответ:

-- 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.

1 голос
/ 13 января 2020

получая последние 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
...