当操作影响集合

时间:2016-01-13 18:00:49

标签: mysql sql

背景信息:我们有一个社区会议室,可以通过窗帘分成两半。在过去,当一个团队需要整个房间时,我们放入两个条目,每半个一个...但是我们已经修改了软件(MRBS),因此现在有3个房间(Full {1},Closet Side {2}和Kitchen Side {3})和软件检查当已经预订满员时您不能预留部分房间,反之亦然。但是我们有很多旧的"完整的房间"保留双方的预订。所以2& 3是相同的我需要将其中一个预订移至1并删除另一个。

所以我有一张表如:

id  room_id  start_time   name
1   2        13:00        Meeting
2   2        15:00        Meeting
3   3        15:00        Meeting
4   3        13:00        Storytime

我想要通过桌子,当房间2& 3两者同时具有条目并且具有相同的名称我想将房间2的room_id更改为1并删除房间3的条目。因此在上面的示例中,条目2将被修改并且条目3将被删除

我相当确定这需要两个单独的查询; EG首先匹配将所有room_id更改为2到1,然后作为单独的查询,比较房间1和3并删除3上的条目。

认为这对于更改房间2到1很接近:

UPDATE `mrbs_entry`
JOIN `mrbs_entry` AS `other_side` ON `other_side.room_id` = '3'
    AND `other_side.name` = `mrbs_entry.name`
    AND `other_side.start_time` = `mrbs_entry.start_time`
    AND `other_side.id` != `mrbs_entry.id`
SET `mrbs_entry.room_id` = '1'
WHERE (`mrbs_entry.room_id` = '2' AND `mrbs_entery.id` IN(92437,92438,92442,92443,92470,92471,92477,92478,92489,89462,92496,90873))

然而我收到#1054 - Unknown column 'mrbs_entry.room_id' in 'field list'错误

注意:IN(*)位将其限制为几个测试条目,以确保它实际上按预期工作。

8 个答案:

答案 0 :(得分:1)

您无法在SET行中使用表名进行更新。 您可能可以将该行更改为

SET `room_id` = '1'

但是从确保查询的工作方式可能更加安全:

UPDATE 
`mrbs_entry`
set `room_id` = '1'
WHERE `id` IN
(
    SELECT `mrbs_entry.id` FROM
    `mrbs_entry`
    JOIN `mrbs_entry` AS `other_side` ON `other_side.room_id` = '3'
        AND `other_side.name` = `mrbs_entry.name`
        AND `other_side.start_time` = `mrbs_entry.start_time`
        AND `other_side.id` != `mrbs_entry.id`
    WHERE (`mrbs_entry.room_id` = '2' AND `mrbs_entry.id` IN(92437,92438,92442,92443,92470,92471,92477,92478,92489,89462,92496,90873))
) AS T

运行内部查询,直到它拉出正确的id组,然后运行整个内容来更改room_id s

答案 1 :(得分:1)

方法1 - 使用临时表的存储过程

如果您准备使用存储过程和临时表,这似乎是最简单的方法:

CREATE PROCEDURE sp_sanitize_mrbs()
BEGIN
    DROP TEMPORARY TABLE IF EXISTS mrbs_to_sanitize;
    CREATE TEMPORARY TABLE mrbs_to_sanitize (
      id int auto_increment primary key,
      room2_id int,
      room3_id int);

    -- "I want to go through the table, and when room 2 & 3 both have
    -- entries at the same time and with the same name I want to..."
    INSERT INTO mrbs_to_sanitize (room2_id, room3_id)
    SELECT m1.id, m2.id
    FROM mrbs_entry m1
    CROSS JOIN mrbs_entry m2
    WHERE m1.start_time = m2.start_time
      AND m1.name = m2.name
      AND m1.room_id = 2
      AND m2.room_id = 3;

    -- ...change room 2's room_id to 1
    UPDATE mrbs_entry me
    JOIN mrbs_to_sanitize mts
    ON me.id = mts.room2_id
    SET me.room_id = 1;

    -- "...and delete the entry for room 3."
    DELETE me
    FROM mrbs_entry me
    JOIN mrbs_to_sanitize mts
    ON me.id = mts.room3_id;
END//

-- ...
-- The Stored Procedure can now be called any time you like:
CALL sp_sanitize_mrbs();

请参阅SQL Fiddle Demo - using a Stored Procedure

方法2 - 不带存储过程

以下“技巧”稍微复杂一点,但应该在不使用存储过程,临时表或变量的情况下执行此操作:

-- "I want to go through the table, and when room 2 & 3 both have
-- entries at the same time and with the same name I want to..."

-- "...change room 2's room_id to 1"
UPDATE mrbs_entry m1
CROSS JOIN mrbs_entry m2
-- temporarily mark this row as having been updated
SET m1.room_id = 1, m1.name = CONCAT(m1.name, ' UPDATED')
WHERE m1.start_time = m2.start_time
  AND m1.name = m2.name
  AND m1.room_id = 2
  AND m2.room_id = 3;

-- "...and delete the entry for room 3."
DELETE m2 FROM mrbs_entry m1
CROSS JOIN mrbs_entry m2
WHERE m1.start_time = m2.start_time
  AND m1.name = CONCAT(m2.name, ' UPDATED')
  AND m1.room_id = 1
  AND m2.room_id = 3;

-- now remove the temporary marker to restore previous value
UPDATE mrbs_entry
SET name = LEFT(name, CHAR_LENGTH(name) - CHAR_LENGTH(' UPDATED'))
WHERE name LIKE '% UPDATED';

方法2的解释

第一个查询更新房间号码。但是,正如您所提到的,我们需要在单独的查询中执行删除操作。由于我没有对您的数据进行任何假设,因此一旦修改后获得相同结果的安全方法是引入“标记”以临时指示更新更改了哪一行。 在上面的示例中,此标记为'UPDATED ',但您可能希望选择更有可能永远不会用于任何其他目的的内容,例如一个随机的字符序列。如果需要,它也可以移动到不同的字段。然后可以执行删除,最后需要删除标记以恢复原始数据。

请参阅SQL Fiddle demo - without Stored Procedure

答案 2 :(得分:0)

我认为你需要实现一个触发器或者更确切地说是一个返回触发器的存储过程。

1)检查条目是否具有相同的标题。 2)更新房间到2到1号房间。 3)删除3号房间的入口。

像这样。

答案 3 :(得分:0)

我会分2步完成。第一步是非破坏性的。通过这种方式,我可以看到第一步看起来是否正确,然后继续对mrbs_entry进行实际修改。

首先使用自联接创建一个临时表,以发现房间2和3的完全匹配。这个额外的表将有效地列出需要进入房间1的预订,并且它列出了2和3的预订。需要删除。

CREATE TABLE tmp
    SELECT a.*
        FROM mrbs_entry AS a
        JOIN mrbs_entry AS b  ON a... = b...
        WHERE a.room_id = 2
          AND b.room_id = 3;

ONstart_time和/或date和/或name或其中的一些组合。请注意,只捕获了一组列(a.*)。

该表故意不是CREATE TEMPORARY TABLE,而是永久性的表格。表。检查内容以查看是否正确创建。

第二步分为2个查询,但在单个事务中为了一些额外的安全性:

BEGIN;
DELETE mrbs_entry
    FROM mrbs_entry
    JOIN tmp ON mrbs_entry... = tmp...
    WHERE mrbs_entry.room_id IN (2,3);   -- delete both reservations
INSERT INTO mrbs_entry
     SELECT 1 as room_id, ...
         FROM tmp;     -- Insert the "full" assignments
COMMIT;

最后清理 - 在你进一步确认改变是好的之后,

DROP TABLE tmp;

答案 4 :(得分:0)

这对我有用,我认为它非常自我解释并遵循您使用两个查询的逻辑。

-- update query
update mrbs_entry 
join 
(
    select t1.* 
    from mrbs_entry as t1 
    join mrbs_entry as t2 
        on 
        t2.room_id <> t1.room_id -- get different rooms
    and t2.start_time = t1.start_time  -- with same start time
    and t2.name = t1.name -- and same name
    where 
        t1.room_id = 2 -- where the left side has room id 2
    and t2.room_id = 3 -- and the right side has room id 3
) as t 
    on 
        t.id = mrbs_entry.id -- set those room ids (2) to
    set mrbs_entry.room_id = 1 ; -- the new id (1)

-- delete query
delete mrbs_entry 
from mrbs_entry 
join mrbs_entry as t1 
    on 
        t1.room_id <> mrbs_entry.room_id -- same as above, different room ids
    and t1.start_time = mrbs_entry.start_time -- same start time
    and t1.name = mrbs_entry.name -- same name
    where 
        mrbs_entry.room_id = 3 -- get the stuff that has room id 3 (to get rid of it)
    and t1.room_id = 1 -- and new duplicate room id 1
;

答案 5 :(得分:0)

我已经修改了你的陈述并正确worked(至少乍一看)。

powerShell = PowerShell.openSession();

//Print results    
System.out.println(powerShell.executeScript("\"C:\\testscript.ps1\"").getCommandOutput());

powerShell.close();

答案 6 :(得分:0)

我对这里的一些答案感到困惑,因为实际问题不是那么难恕我自己。

  • 首先,您需要确定所有子房间的所有预订。换句话说,您需要一个具有相同名称和相同start_time的预订列表,但一个用于room_id 2,另一个用于room_id 3.(GROUP BY&amp; HAVING
  • 然后,您可以在room_id 1条目(INSERT
  • 中转换此列表
  • 最后,当还有一个(刚插入的)room_id 1条目时,你会删除所有room_id 2和3条目。 (DELETE

    -- setup
    CREATE TABLE mrbs_entry (id int auto_increment primary key, room_id int, start_time varchar(255), name varchar(255));
    INSERT INTO mrbs_entry (room_id, start_time, name) VALUES (2, '13:00', 'Meeting');
    INSERT INTO mrbs_entry (room_id, start_time, name) VALUES (2, '15:00', 'Meeting');
    INSERT INTO mrbs_entry (room_id, start_time, name) VALUES (3, '15:00', 'Meeting');
    INSERT INTO mrbs_entry (room_id, start_time, name) VALUES (3, '13:00', 'Storytime');
    
    INSERT INTO mrbs_entry (room_id, start_time, name) VALUES (2, '18:00', 'Test');
    INSERT INTO mrbs_entry (room_id, start_time, name) VALUES (3, '18:00', 'Test');
    
    -- step 1: "convert" reservations that book all 'sub-rooms' into a 'full-room' reservation
    INSERT `mrbs_entry` (room_id, start_time, name)
    SELECT 1, -- full room
            start_time, 
            name
        FROM `mrbs_entry` new
        WHERE room_id IN (2, 3) -- reserved left and/or right side
    
        -- safety check, avoid inserting the value if it already exists!
        AND NOT EXISTS ( SELECT *
                            FROM `mrbs_entry` old
                            WHERE old.room_id    = 1
                            AND old.start_time = new.start_time
                            AND old.name       = new.name )
    
    -- must have reserved both sides of the room
        GROUP BY start_time,
                name
    HAVING COUNT(*) = 2;
    
    -- step 2: get rid of all 'sub-room' reservations in case there already is a 'full-room' reservation
    DELETE del
        FROM `mrbs_entry` del
        JOIN `mrbs_entry` fr -- WHERE exists a room_id 1 for same time & name
        ON fr.room_id    = 1
        AND fr.start_time = del.start_time
        AND fr.name       = del.name
    WHERE del.room_id IN (2, 3); -- get rid of half-rooms
    

PS:我更喜欢在步骤2中使用WHERE EXISTS语法,但它无法立即开始工作..将会看到我能否在今天晚些时候找到时间(我通常在TSQL中工作,这有点不同)

答案 7 :(得分:0)

按照Steve Chambers解决方案方法2的说法,我会在一次交易的三个步骤中完成。

测试用例:

CREATE TABLE mrbs_entry (id int auto_increment primary key, room_id int, start_time varchar(255), name varchar(255));
INSERT INTO mrbs_entry
    (room_id, start_time, name)
VALUES
    (2, '13:00', 'Meeting'),
    (2, '15:00', 'Meeting'),
    (3, '15:00', 'Meeting'),
    (3, '13:00', 'Storytime');

解决方案:

START TRANSACTION;

    -- 1. Mark one "side" of the pair with temporary artificial room_id = 0.
    UPDATE mrbs_entry side2
    INNER JOIN mrbs_entry side3 ON side3.room_id = 3 AND
      side2.start_time = side3.start_time AND
      side2.name = side3.name
    SET side2.room_id = 0
    WHERE side2.room_id = 2;

    -- 2. Delete the other "side" of the pair.
    DELETE side3 FROM mrbs_entry side3
    INNER JOIN mrbs_entry side0 ON side0.room_id = 0 AND
      side0.start_time = side3.start_time AND
      side0.name = side3.name
    WHERE side3.room_id = 3;

    -- 3. Reset the mark to a valid value.
    UPDATE mrbs_entry
    SET room_id = 1
    WHERE room_id = 0;

COMMIT;
如果room_id存在约束,

这将无法正常工作,但如果没有,这将是最安全和最有效的解决方案。即使存在约束,也可以在事务开始时将room_id = 0临时添加到可能值列表中,并在提交之前将其删除。