为什么这个带子查询的MySQL UPDATE表现得很奇怪?

时间:2016-05-16 03:39:09

标签: mysql sql syntax

我想我失去了它! : - /

所以,我有这个MySQL 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
)

CustomersJobsCuts是简单关系组的一部分。 Customers有多个JobsJobs有多个Cuts我在这里提出了这个问题的复杂性,因为我不确定它是否是问题的一部分,并且不想过度简化问题。

查询的目的是为所有Jobs设置Archived'Y'Jobs 根据{{​​1}}。Cut字段,自2014-01-01 OK之前未创建Cuts条记录的记录。当时看起来很简单。

所以问题是这个;查询正确的以下部分生成单个列结果,仅包含CutDateJobCode个文件,其中Cut个记录没有CutCutDate2014-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表中也恰好有Jobs的931到934。这些包含在上述结果中,因为它们没有CutCutDate的记录比2014-01-01更新。这就是我想要的和我期望的。

HOWEVER ...当我运行完整查询时,包括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记录也可以!我说,&#34;随机&#34;,因为a)我找不到他们的押韵或理由。

所以,这就是问题所在。请哦,非常好,非常好 ...我想念WTF吗?

非常感谢!

3 个答案:

答案 0 :(得分:1)

我认为左连接的顺序不正确。您希望包含Jobs表中的所有JobCodes,然后如果Cuts表中有记录,那么您希望包含它们(如果存在)。

在此情况下,如果裁员记录不存在,CutDate将为空,您也无法处理会发生的事情。

    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年之后或之前,具体取决于您是否要存档记录,如果不存在记录的Cuts。您可以使用having语句来处理聚合函数,以便使用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'
 )

答案 1 :(得分: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为空,那么根据我的理解,没有任何内容可以更新。

答案 2 :(得分:0)

感谢大家的好建议。你是完全正确的。

最后,“随机”结果实际上是周围PHP代码中的一个错误。随机工作实际上并未存档。

最重要的是,我正在使用phpMyAdmin,它显然有一个(另一个:-P)错误。当我使用子查询时,它会报告“零(0)行受影响”,当时这不是真的!我相信这一点,并且在查询后没有进一步检查实际的表格内容。我浪费了很多这样的时间。 : - /

在LEFT JOIN中丢失LEFT肯定有助于Job没有相关的Cuts记录。

最后,我选择了@Andrew的版本,因为我认为它是最有效的,因为没有使用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'

再次感谢!