在UPDATE语句子查询中将IN更改为JOIN

时间:2015-04-16 13:32:09

标签: mysql

我有这个查询,这对于更高版本的MySQL来说效果很好(我认为),但不幸的是,这不是一个选项。

我有一个彩票数据库,每个彩票都有用户的电子邮件地址。我需要找到这个彩票的赢家,但每个电子邮件地址只能有1张中奖彩票,即使他们有多张中奖彩票。我正在尝试使用多个子查询来完成此任务。

我只想将winner ='Y'设置为每封电子邮件的最高匹配数,因此用户每周只能获得1个“获胜者”。

我想为每封电子邮件更新1行,而不是整个表格中的1行。

我的目标是在单个查询中执行所有这些操作,或者至少尽可能减轻服务器上的压力。我每周要处理超过30,000行/票。

我认为最好的办法是使用JOIN而不是IN,但我无法弄明白。

UPDATE `tickets`
SET `winner`='Y'
WHERE `ticketid` IN
    (SELECT `ticketid`
     FROM `tickets`
     WHERE `email` IN
         (SELECT distinct(email)
          FROM `tickets`
          WHERE `date` BETWEEN '$weekstart' AND '$weekend'
            AND `matches`>='3')
     ORDER BY `matches` DESC LIMIT 1)

此查询给出了错误:“#1235 - 此版本的MySQL尚不支持'LIMIT& IN / ALL / ANY / SOME子查询'”

3 个答案:

答案 0 :(得分:1)

除了 LIMIT & IN/ALL/ANY/SOME subquery 限制之外,在WHERE语句的UPDATE子句中,自行加入表是不可能的 - 没有一些技巧 - 甚至不是在最新的MySQL版本中。

使用连接重写通常更好 - 尽管远非琐碎:

UPDATE 
    ( SELECT DISTINCT email
      FROM tickets
    ) AS e
  JOIN 
    tickets AS t
      ON  t.ticket_id = 
          ( SELECT ti.ticket_id
            FROM tickets AS ti
            WHERE ti.email = e.email
              AND ti.date BETWEEN '$weekstart' AND '$weekend' 
              AND ti.matches >= 3
            ORDER BY ti.matches DESC
            LIMIT 1
          ) 
SET 
    t.winner = 'Y' ;

这只会为表格中的ticket_id的每个不同值找到一个emails(并且只有在添加条件的行中,所需周中的日期以及3个或更多&#34时;匹配")然后更新。

修改:我错了,上面的工作,引发错误:

  

"#1093 - 您无法指定目标表' e'用于FROM子句中的更新"


但这(甚至更复杂)确实有效:

UPDATE 
    tickets AS u
  JOIN
    ( SELECT t.ticket_id
      FROM
          ( SELECT DISTINCT email
            FROM tickets
          ) AS e
        JOIN 
          tickets AS t
            ON  t.ticket_id = 
                ( SELECT ti.ticket_id
                  FROM tickets AS ti
                  WHERE ti.email = e.email
                    AND ti.date BETWEEN '$weekstart' AND '$weekend' 
                    AND ti.matches >= 3
                  ORDER BY ti.matches DESC
                  LIMIT 1
                ) 
    ) AS x
      ON  x.ticket_id = u.ticket_id
SET 
    u.winner = 'Y' ;

关于效率,我认为(email, date, matches, ticket_id)上的索引会改善它。

如果表中具有matches >= 3的行的百分比很小,则替代索引将位于(email, matches, date, ticket_id)上。

答案 1 :(得分:0)

您希望根据您的条件更新电子邮件具有最大匹配数的故障单中的一行。

我认为这是您想要的查询:

UPDATE `tickets` t JOIN
       (SELECT `email`, max(matches) as maxm
        FROM tickets t
        WHERE `date` BETWEEN '$weekstart' AND '$weekend' AND `matches` >= 3
        GROUP BY email
        ORDER BY maxm DESC
        LIMIT 1
       ) te
       ON t.email = t3.email
    SET t `winner` = 'Y'
    LIMIT 1;

您可能希望使用此order by

进行某种随机化
order by maxm DESC, rand()

答案 2 :(得分:0)

试试这个:

UPDATE tickets
INNER JOIN (
SELECT `ticketid`
FROM `tickets` 
WHERE `date` BETWEEN '$weekstart' 
  AND '$weekend' AND `matches`>='3'
ORDER BY `matches` DESC LIMIT 1) t
ON tickets.ticketid = t.ticketid
SET `winner`='Y'