基于行计数的SQL更新

时间:2015-02-12 18:37:48

标签: mysql sql

我最近在大约15年不活动后开始重新学习SQL语法。我熟悉旧的ANSI-89 JOIN语法,新语法正在习惯。

无论如何,我要做的是计算两个查询中的元素数量,如果它们相等,则为行设置一个标志。

这是我的表结构:

PERSON
 ID        -- unique ID for the person
HOUSE   
 ID        -- unique ID for the house
 PersonID  -- person who owns the house
BATHROOM
 HouseID   -- House that the room is in
 Type      -- The type of bathroom - ensuite, powder, etc.
 Pieces    -- The number of pieces in the bathroom.  

行。所以我想在PERSON表中添加一个新字段,用于跟踪所有房屋的所有浴室是否为3件套房。 (不要问为什么 - 这显然是制造场景,但它很好地映射到我试图解决的现实世界场景。)

所以,我从以下开始:

ALTER TABLE person ADD All_Three tinyint(1) NOT NULL DEFAULT 1 AFTER ID;

现在我有了这个字段,它默认为1

所以,我需要运行一个填充它的查询。这是我的尝试:

UPDATE IGNORE person,
     (SELECT COUNT(*) AS count 
      FROM  house, bathroom
      WHERE house.PersonID=person.ID
        AND bathroom.HouseID=house.ID
        AND bathroom.Type='ensuite'
        AND bathroom.Pieces=3) num_three,
     (SELECT COUNT(*) AS count 
      FROM  house, bathroom
      WHERE house.PersonID=person.ID
        AND bathroom.HouseID=house.ID) all_bathrooms
SET person.All_Three=2  
WHERE num_three.count=all_bathrooms.count;

这对我来说似乎很合乎逻辑; 第一个子查询计算具有三个部分的套间数,第二个子查询计算浴室总数。如果他们是平等的(即该人拥有的所有浴室都是3件套房),那么我们可以设置All_Three字段。

此操作失败并显示错误

ERROR 1054 (42S22): Unknown column 'person.ID' in 'where clause'

那么,如何在子查询中引用person?我试过别名,但也失败了同样的错误。

编辑: 事实证明我的目标是错误的。我需要做的是设置all_three字段,如果该人拥有的每个房屋仅包含3件套房,并且每个房屋至少有一个套房(显然必须是3件)

2 个答案:

答案 0 :(得分:1)

为此,我们将生成符合条件PERSON.ID的{​​{1}}的包含列表,BATHROOM.Type = 'ensuite' and BATHROOM.Pieces = 3的排除列表不符合条件PERSON.ID }),并使用BATHROOM.Type != 'ensuite' or BATHROOM.Pieces != 3 / in过滤更新:

not in

SQL FIDDLE

答案 1 :(得分:1)

您想要更新表格中的记录。单个表中更新的语法是:

UPDATE tablename SET ... WHERE ...

相反,您尝试使用两个所谓的派生表(select语句)加入表:

UPDATE tablename, (query 1), (query 2) SET ... WHERE ...

这将交叉加入这三个部分。这是旧的以逗号分隔的连接语法总是会发生的事情;您将所有记录相互组合。然后,您通常会在WHERE子句中命名您的连接条件,以从结果中删除记录。所以使用新的sytax,这将是:

UPDATE tablename CROSS JOIN (query 1) CROSS JOIN (query 2) SET ... WHERE ...

您为两个派生查询的连接提供了条件,但这会将其连接转换为内连接:

UPDATE tablename CROSS JOIN (query 1) INNER JOIN (query 2) ON ... SET ...

您还尝试将表人与派生表相关联。但是在两个派生表查询中,不知道外部的其他表或派生表。您无法在派生表查询中引用表人员。

不允许:

UPDATE tablename CROSS JOIN (select something from othertable where othertable.colx = tablename.coly) SET ...

相反:

UPDATE tablename INNER JOIN (select something, colx from othertable where ...) AS query1 ON query1.colx = tablename.coly) SET ...

但如上所述,您无论如何都不想更新多个表,因此无需加入。与派生表不同,普通子查询可以与外表不相关。您可以在WHERE子句中使用此类子查询:

UPDATE person
SET ...
WHERE 
  (select count(*) from ... where ... = person.id) = 
  (select count(*) from ... where ... = person.id)

关于你在编辑中澄清的问题:你想找到只有三件套浴室的房子的人。换句话说:所有拥有房屋的人(简单的EXISTS或IN条款),但没有其他浴室类型的房子或另一个浴室的批发房间(更多的'更多)复杂的NOT EXISTS条款)。

在你的NOT EXIST条款中,对于一个人不合适的房屋你再次有一个NOT EXISTS条款(如果这个房子没有房间,如果可能的话)和一个EXISTS条款(对于一个浴室的情况)那个房子里有错误的类型或件号。)

UPDATE person p
SET all_three = 2
WHERE id IN (select personid from house)
AND NOT EXISTS -- an unsuitable house
( 
  select *
  from house h
  where not exists -- a bathroom for that house
  ( 
    select * 
    from bathroom b 
    where b.houseid = h.id
  )
  or exists -- bad bathroom for that house
  (
    select * 
    from bathroom b 
    where b.houseid = h.id 
    and (b.type <> 'ensuite' or b.pieces <> 3)
  )
  where h.personid = p.id
);

顺便说一句,是否使用(NOT)EXISTS或(NOT)IN条款主要是个人品味问题。