2016-05-16 4 views
2

Думаю, я теряю его! : -/Почему этот MySQL UPDATE с подзапросом действует странно?

Итак, у меня есть этот запрос UPDATE MySQL ...

UPDATE `Jobs` 
SET `Archived` = 'Y' 
WHERE `JobCode` IN (
    SELECT `JobCode` FROM (
     SELECT m.`JobCode` 
     FROM `Jobs` as jx 
     JOIN (
      SELECT j.`JobCode`, MAX(c.`CutDate`) AS MaxCutDate 
      FROM `Cuts` AS c 
      LEFT JOIN `Jobs` AS j ON (j.`JobCode`=c.`JobCode`) 
      GROUP BY j.`JobCode` 
     ) AS m ON (m.`JobCode`=jx.`JobCode`) 
     WHERE m.MaxCutDate < '2014-01-01' 
    ) AS tmp_table 
) 

Таблица Customers, Jobs и Cuts являются частью простой реляционной группы. Customers имеют несколько Jobs, Jobs имеют несколько Cuts. Я оставил эту сложность для моего вопроса здесь, потому что я не уверен, является ли это частью проблемы или нет, и не хотел, чтобы упростить проблему.

Целью запроса является установка Jobs. Archived до 'Y' для всех Jobs тех, у кого не было Cut запись создана с: 2014-01-01 OK, в соответствии с Cuts. CutDate поле. Казалось, достаточно просто в то время.

Итак, проблема в этом; В следующем разделе запроса правильно выдает один результат столбца, содержащий только JobCode, которые не имеют записей Cut файла с номером Cut. CutDate так 2014-01-01 ...

Это работает как задумано ...

SELECT `JobCode` FROM (
     SELECT m.`JobCode` 
     FROM `Jobs` as jx 
     JOIN (
      SELECT j.`JobCode`, MAX(c.`CutDate`) AS MaxCutDate 
      FROM `Cuts` AS c 
      LEFT JOIN `Jobs` AS j ON (j.`JobCode`=c.`JobCode`) 
      GROUP BY j.`JobCode` 
     ) AS m ON (m.`JobCode`=jx.`JobCode`) 
     WHERE m.MaxCutDate < '2014-01-01' 
    ) AS tmp_table 

[Примечание: Двойной SELECT (SELECT ...))), чтобы получить вокруг глупой, но хорошо известная проблема с MySQL , Я не вижу здесь проблем. ]

В качестве примера выше может произвести следующие ...

JobCode 
------- 
    930 
    935 
    936 

Там случается быть JobCode «s 931 через 934 в Jobs таблице. Это не, включенные в вышеуказанные результаты, потому что у них нет Cut записей с CutDate в последнее время, чем 2014-01-01. Это то, чего я хочу и чего я ожидал.

ОДНАКО ... когда я запускаю полный запрос, включая UPDATE часть ...

UPDATE `Jobs` 
SET `Archived` = 'Y' 
WHERE `JobCode` IN (
    SELECT `JobCode` FROM (
     SELECT m.`JobCode` 
     FROM `Jobs` as jx 
     JOIN (
      SELECT j.`JobCode`, MAX(c.`CutDate`) AS MaxCutDate 
      FROM `Cuts` AS c 
      LEFT JOIN `Jobs` AS j ON (j.`JobCode`=c.`JobCode`) 
      GROUP BY j.`JobCode` 
     ) AS m ON (m.`JobCode`=jx.`JobCode`) 
     WHERE m.MaxCutDate < '2014-01-01' 
    ) AS tmp_table 
) 

... то хорошо, сумасшедшие вещи, кажется, бывает! Что я ожидают, так это то, что ТОЛЬКО Jobs записи, которые есть в выходе из SELECT, должны обновляться. Но случайные другие записи Jobs также делают! Я говорю «случайный», потому что: а) я не могу найти рифму или причину для них.

Итак, в этом проблема. Пожалуйста, о, милая, довольно пожалуйста ... WTF я здесь пропал?

Спасибо, связка!

+0

вы можете попробовать это: UPDATE 'Jobs' SET' Archived' = WHERE 'JobCode' IN ( ВЫБОР m.'JobCode' FROM' Jobs', как JX JOIN ( ВЫБОР j.' 'Y' JobCode', MAX (c.'CutDate') AS MaxCutDate ОТ 'Cuts' AS c LEFT JOIN' Работа' AS j ON (j.'JobCode' = c.'JobCode') GROUP BY j.'JobCode' ) AS m ON (m.'JobCode' = jx.'JobCode') ГДЕ m.MaxCutDate <'2014-01-01' ) –

ответ

1

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

Вы также не обрабатываете то, что происходит, когда запись разреза не существует, в этом случае CutDate будет null.

SELECT j.`JobCode` 
     FROM `Jobs` as j 
LEFT JOIN `Cuts` AS c ON c.`JobCode` = j.`JobCode` 
    GROUP BY j.`JobCode` 
    HAVING MAX(IFNULL(c.`CutDate`, '2016-01-01')) < '2014-01-01' 

Так дата может быть установлена ​​после 2014 года или ранее в зависимости от того, если вы хотите архивировать запись или нет, если не Порезы существуют для записи. Вы можете использовать инструкции для работы над агрегатными функциями inorder для использования функции max, а затем сравнить, если она меньше 2014.

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

# ju will stand for "Jobs Update" 
SELECT ju.`JobCode` 
    FROM `Jobs` ju 
WHERE ju.`JobCode` IN (
     SELECT j.`JobCode` 
      FROM `Jobs` as j 
    LEFT JOIN `Cuts` AS c ON c.`JobCode` = j.`JobCode` 
     GROUP BY j.`JobCode` 
     HAVING MAX(IFNULL(c.`CutDate`, '2016-01-01')) < '2014-01-01' 
) 

Если вы готовы запустить обновление, вы можете его переключить.

# ju will stand for "Jobs Update" 
UPDATE `Jobs` ju 
    SET ju.`Archived` = 'Y' 
WHERE ju.`JobCode` IN (
     SELECT j.`JobCode` 
      FROM `Jobs` as j 
    LEFT JOIN `Cuts` AS c ON c.`JobCode` = j.`JobCode` 
     GROUP BY j.`JobCode` 
     HAVING MAX(IFNULL(c.`CutDate`, '2016-01-01')) < '2014-01-01' 
) 
+0

Спасибо. Я так запутался в LEFT vs RIGHT и просто ПРИСОЕДИНИТЕСЬ - и я совсем забыл о HAVING. Сейчас я запускаю тесты ... – gruvin

1

Вы пробовали JOIN вместо WHERE состояния?

UPDATE `Jobs` 
JOIN (SELECT `JobCode` FROM (
     SELECT m.`JobCode` 
     FROM `Jobs` as jx 
     JOIN (
      SELECT j.`JobCode`, MAX(c.`CutDate`) AS MaxCutDate 
      FROM `Cuts` AS c 
      JOIN `Jobs` AS j ON (j.`JobCode`=c.`JobCode`) 
      GROUP BY j.`JobCode` 
     ) AS m ON (m.`JobCode`=jx.`JobCode`) 
     WHERE m.MaxCutDate < '2014-01-01' 
    ) AS tmp_table 
) AS t ON t.`JobCode` = `Jobs`.`JobCode` 
SET `Archived` = 'Y' 

Также я заменяет LEFT JOIN в подзапрос с JOIN. Поскольку мы выбираем JobCode, который должен существовать в таблице Jobs - это необходимо для UPDATE. Если значение JobCode равно нулю, то я ничего не обновляю, как я понял.

+0

Сначала я попробовал этот тип JOIN. Но я просто не мог разглядеть синтаксис (int he UPDATE context) или что именно он будет делать. Сейчас я запускаю несколько тестов, чтобы попытаться прибить эту концепцию. – gruvin

0

Спасибо за отличный совет. Вы были ВСЕ ПОЛНЫ.

В конце концов, «случайные» результаты были на самом деле ошибкой в ​​окружающем PHP-коде. Случайные вакансии фактически не архивировались.

Кроме того, я использую phpMyAdmin, у которого, по-видимому, есть (другая: -P) ошибка. Когда я использую подзапросы, он будет сообщать, «затронуты строки Zero (0)», когда это просто неверно! Я верил в это и не проверял дальше фактическое содержимое таблицы после моего запроса. Я потратил много времени на это. : -/

Потеря LEFT в LEFT JOIN определенно помогает в случае, когда Job не имеет соответствующей записи Cuts.

В конце концов, я пошел с версией Эндрю, только потому, что я считаю, что это было бы самым эффективным из-за того, что не использовал HAVING ..., и это так, как я изначально пытался, поэтому он чувствует себя лучше, хе-хе .. .

<!-- language: SQL --> 
UPDATE `Jobs` 
JOIN (SELECT `JobCode` FROM (
     SELECT m.`JobCode` 
     FROM `Jobs` as jx 
     JOIN (
      SELECT j.`JobCode`, MAX(c.`CutDate`) AS MaxCutDate 
      FROM `Cuts` AS c 
      JOIN `Jobs` AS j ON (j.`JobCode`=c.`JobCode`) 
      GROUP BY j.`JobCode` 
     ) AS m ON (m.`JobCode`=jx.`JobCode`) 
     WHERE m.MaxCutDate < '2014-01-01' 
    ) AS tmp_table 
) AS t ON t.`JobCode` = `Jobs`.`JobCode` 
SET `Archived` = 'Y' 

Еще раз спасибо!

Смежные вопросы