MySQL Исключить значения

У меня есть две таблицы:

ИСПЫТАНИЯ:

ИДЕНТИФИКАТОР ГОД ОПИСАНИЕ 0 2023 год какой-то текст .. ... ... Н 2024 год какой-то текст

test_prep_materials:

ИДЕНТИФИКАТОР ПУТЬ ТИД 0 /home/user/file0.pdf 0 .. ... ... М /home/user/fileM.pdf 0

Поэтому к любому тесту есть подготовительные материалы. И некоторые тесты содержат тот же материал. Я хочу удалить все материалы за 2023 год и УДАЛИТЬ ФАЙЛЫ С ДИСКА. Но тесты 2024 года могут иметь тот же путь (ТОТ ЖЕ ФАЙЛ НА ДИСКЕ). Поэтому мне нужно выбрать все подготовительные материалы для тестов 2023 года и исключить из них все пути, которые имеют зависимости в тестах 2024 года.

Я попробовал следующее, но результатов слишком много, чтобы понять, правильно ли была выполнена команда.

SELECT `test_prep_materials`.`PATH`, `tests`.`id`, `tests`.`year`, `tests`.`Name_t` 
    from `test_prep_materials` 
    JOIN `tests` ON `test_prep_materials`.`tId` = `tests`.`id` 
    WHERE `tests`.`year` = 2023 
EXCEPT
SELECT `test_prep_materials`.`PATH`, `tests`.`id`, `tests`.`year`, `tests`.`Name_t` 
    from `test_prep_materials` 
    JOIN `tests` ON `test_prep_materials`.`tId` = `tests`.`id` 
    WHERE `tests`.`year` <> 2023 

Может ли кто-нибудь помочь мне понять, правильный ли мой запрос или я что-то упускаю?

Что-то здесь не так. Вы говорите, что хотите удалить материалы, которые актуальны только для испытаний 2023 года и никаких других лет. Я ожидал найти идентификатор материала в таблице тестов, но вместо этого в таблице материалов оказался идентификатор теста. Таким образом, каждая запись материала относится ровно к одному испытанию, а именно к тому, которое указано в tid. В вашей модели данных не может быть записи материала, которая используется более чем в одном тесте. Есть ли что-то, чего я не вижу?

Thorsten Kettner 13.08.2024 11:56

Ваше утверждение: «Я попробовал следующее, но получено слишком много результатов, чтобы понять, правильно ли была выполнена команда». -> почему бы не использовать эту команду, чтобы проверить LIMIT 5;, соответствуют ли результаты тому, что вы хотите.

Derek Roberts 13.08.2024 11:57

Ваш EXCEPT вообще не будет исключать какие-либо строки, поскольку первый SELECT содержит только 2023 год, а второй SELECT содержит только другие годы. Пропустите год и посмотрите, что произойдет. (Или вместо этого используйте NOT EXISTS.)

jarlh 13.08.2024 12:12

@ThorstenKettner, да, если мы говорим только о таблице в базе данных - я могу удалить все строки для тестов 2023 года. Но я ДОЛЖЕН также удалить файлы на диске. Но проблема в том, что один и тот же файл (путь в материалах) может одновременно принадлежать как тесту 2023, так и 2024.

ASDORDSAUSER 13.08.2024 12:20

Чтобы найти эти пути, вам просто нужно ограничить запрос путями: SELECT m.path from test_prep_materials m JOIN tests t ON t.id = m.tId WHERE t.year = 2023 EXCEPT SELECT m.path from test_prep_materials m JOIN tests t ON t.id = m.tId WHERE t.year <> 2023. Или суммировать: SELECT m.path from test_prep_materials m JOIN tests t ON t.id = m.tId GROUP BY m.path HAVING MIN(t.year) = 2023 AND MAX(t.year) = 2023.

Thorsten Kettner 13.08.2024 16:13
ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
1
5
57
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Похоже, вам нужно удалить пути из тестов прошлых лет, которые не используются ни в одном тесте этого года. Если это так, это означает, что вам нужны пути, использованные только в прошлом году и только в прошлом году - используйте Max(YEAR) = Min(YEAR) для каждого PATH и < Max(YEAR) для всех:

WITH    --  S a m p l e    D a t a :
    tests ( ID, YEAR, DESCRIPTION ) AS 
        ( Select 0, 2023, 'some text 0' Union All
          Select 1, 2024, 'some text 1' Union All
          Select 2, 2023, 'some text 2' Union All
          Select 3, 2024, 'some text 3' Union All
          Select 4, 2024, 'some text 4' 
        ),
    test_prep_materials ( ID, PATH, TID ) AS 
        ( Select 100, '/home/user/file0.pdf', 0 Union All 
          Select 101, '/home/user/file0.pdf', 1 Union All 
          Select 102, '/home/user/file3.pdf', 1 Union All 
          Select 103, '/home/user/file3.pdf', 2 Union All 
          Select 104, '/home/user/file4.pdf', 3 Union All 
          Select 105, '/home/user/file4.pdf', 2 Union All 
          Select 106, '/home/user/file4.pdf', 1 Union All 
          Select 107, '/home/user/file5.pdf', 4 Union All 
          Select 108, '/home/user/file6.pdf', 0 
       )
--    S Q L :
Select     tpm.PATH, Max(t.YEAR) as MAX_YEAR, Min(t.YEAR) as MIN_YEAR
From       tests t 
Left Join  test_prep_materials tpm ON(tpm.TID = t.ID)
Group By   tpm.PATH
HAVING     Max(t.YEAR) < ( Select Max(YEAR) From tests )
/*    R e s u l t : 
PATH                    MAX_YEAR  MIN_YEAR
----------------------  --------  --------
/home/user/file6.pdf        2023      2023    */

ИЛИ со всеми данными из таблицы тестов:

--    S Q L :
SELECT  t.*, tpm.PATH
FROM    tests t
INNER JOIN ( Select     tpm.PATH, Max(t.YEAR) as MAX_YEAR, Min(t.YEAR) as MIN_YEAR
             From       tests t 
             Left Join  test_prep_materials tpm ON(tpm.TID = t.ID)
             Group By   tpm.PATH
             HAVING     Max(t.YEAR) < ( Select Max(YEAR) From tests )
          ) tpm ON( t.YEAR Between tpm.MIN_YEAR And tpm.MAX_YEAR )
Where   t.YEAR < ( Select Max(YEAR) From tests )
/*    R e s u l t :
ID  YEAR  DESCRIPTION  PATH
--  ----  -----------  ------------------------
0   2023  some text 0  /home/user/file6.pdf
2   2023  some text 2  /home/user/file6.pd      */

ПРИМЕЧАНИЕ:
Если вы хотите сослаться на какой-либо конкретный год в диапазоне (например, с 2021 по 2026 год), измените подзапрос, выбрав Max(YEAR) для всего конкретного года, скажем, 2025 года, и результатом должны быть пути, использованные до 2025 года, а не в 2025 году. Однако этого не было в ваших выборочных данных.

...
HAVING     Max(t.YEAR) <  2025
...

Это можно еще больше настроить с помощью различных условий предложения Where.

ЕЩЕ ОДИН ПОДХОД:
Если у вас есть период в годах, например, с 2021 по 2026 год, и вы хотите обратиться к 2024 году в поисках предыдущих путей, отсутствующих в 2024 году, вы можете сделать что-то вроде ниже, но с жестко запрограммированным базовым годом (2024):

WITH    --  S a m p l e    D a t a :
    tests ( ID, YEAR, DESCRIPTION ) AS 
        ( Select 0, 2023, 'some text 0' Union All
          Select 1, 2024, 'some text 1' Union All
          Select 2, 2023, 'some text 2' Union All
          Select 3, 2024, 'some text 3' Union All
          Select 4, 2024, 'some text 4' Union All
          --    new rows below
          Select 5, 2021, 'some text 5' Union All
          Select 6, 2024, 'some text 6' Union All
          Select 7, 2026, 'some text 7' 
        ),
    test_prep_materials ( ID, PATH, TID ) AS 
        ( Select 100, '/home/user/file0.pdf', 0 Union All 
          Select 101, '/home/user/file0.pdf', 1 Union All 
          Select 102, '/home/user/file3.pdf', 1 Union All 
          Select 103, '/home/user/file3.pdf', 2 Union All 
          Select 104, '/home/user/file4.pdf', 3 Union All 
          Select 105, '/home/user/file4.pdf', 2 Union All 
          Select 106, '/home/user/file4.pdf', 1 Union All 
          Select 107, '/home/user/file5.pdf', 4 Union All 
          Select 108, '/home/user/file6.pdf', 0 Union All
          --  new rows below
          Select 108, '/home/user/file6.pdf', 5 Union All
          Select 108, '/home/user/file5.pdf', 6 Union All
          Select 108, '/home/user/file5.pdf', 7 
       )
--    S Q L : 
Select     tpm.PATH, Max(t.YEAR) as MAX_YEAR, Min(t.YEAR) as MIN_YEAR
From       tests t 
Left Join  test_prep_materials tpm ON(tpm.TID = t.ID)
Group By   tpm.PATH
HAVING     Count((Select 1 From tests Where ID = t.ID And Year = 2024)) = 0 And 
           Max(Case When t.YEAR <= 2024 Then t.YEAR Else 9999 End ) < 2024
/*    R e s u l t : 
PATH                    MAX_YEAR  MIN_YEAR
----------------------  --------  --------
/home/user/file6.pdf        2023      2021    */

Можете ли вы помочь мне понять ваш первый SQL-скрипт? как его отредактировать в таком случае, что я могу выбрать, материалы какого именно года я удаляю? например в какой-то момент у меня может быть 2023-2026 год. Итак, если я правильно понимаю Query, он вернет все материалы только за 2023 год, только за 2024 год, только за 2025 год. А я например хочу удалить только за 2025 год

ASDORDSAUSER 13.08.2024 13:22

@ASDORDSAUSER Только что обновил ответ с помощью ---> HAVING Max(t.YEAR) < (Выберите Max(YEAR) Из тестов)

d r 13.08.2024 13:31

Я думаю, что что-то не так со вторым запросом. Данные испытаний: 1 - Тест1 - 2023 2 - Тест2 - 2024 3 - Тест3 - 2023 4 - Тест4 - 2024 ---- 1 - /a/b - 1 2 - /a/b - 2 3 - /c/d - 3 4 - /e/f - 4 Так что я ожидаю увидеть только Test3 - 2023 - /c/d Получил: Test1 - 2023 - /c/d Test3 - 2023 - /c/d

ASDORDSAUSER 13.08.2024 13:42

@ASDORDSAUSER Это тоже исправлено. Изменено предложение ON и добавлено предложение WHERE. Это необходимо на протяжении нескольких последних лет.

d r 13.08.2024 13:48

@ASDORDSAUSER Посмотрите скрипку за несколько прошлых лет --> dbfiddle.uk/4iMQrFg1

d r 13.08.2024 13:53

idk второй запрос, похоже, работает неправильно, но все равно возвращает 2 значения. dbfiddle.uk/Yj569teC

ASDORDSAUSER 13.08.2024 14:23

@ASDORDSAUSER Как и предполагалось и как указано в ответе «ИЛИ со всеми данными из таблицы тестов:» - он показывает строки из таблицы тестов, связанные с рассматриваемыми ПУТЯМИ «/home/user/file6.pdf "...

d r 13.08.2024 15:20

ааа! Попался. Вам большое за помощь! Ты спас меня!

ASDORDSAUSER 13.08.2024 15:27

@ASDORDSAUSER Добавлен еще один подход на случай, если у вас есть данные за прошлые и будущие годы...

d r 13.08.2024 15:53

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

select *from test_prep_materials tpm inner join tests t
on t.id = tpm.tid
where t.year = '2023'
and exists (select path from test_prep_materials tpm2 inner join tests t
                on t.id=tpm.id and t.year = 2024  -- t.year != 2023
                where tpm2.path =tpm.path
           )

Другие вопросы по теме