2013-07-18 2 views
2

Мне нужно обновить таблицу в моей базе данных. Для простоты предположим, что имя таблицы tab и имеет 2 столбца: id (PRIMARY KEY, NOT NULL) и col (UNIQUE VARCHAR(300)). Мне нужно обновить таблицу следующим образом:Запрос на обновление PostgreSQL

id     col 
---------------------------------------------------- 
1      'One two three' 
2      'One twothree' 
3      'One two  three' 
4      'Remove white spaces' 
5      'Something' 
6      'Remove whitespaces ' 

к:

id     col 
---------------------------------------------------- 
1      'Onetwothree' 
2      'Removewhitespaces' 
3      'Something' 

Id числа и порядок строк после обновления не важно и может быть различным. Я использую PostgreSQL. Некоторые из столбцов - FOREIGN KEYs. Вот почему ограничение UNIQUE с col было бы хлопотно.

+0

На самом деле, вы хотите, чтобы удалить дубликаты, которые будут созданы после удаления пробелов, не так ли? –

+0

Как вы получили внешний ключ в столбце не первичного ключа? –

+0

@DavidLevel: Определяется 'UNIQUE', этого достаточно для ссылки на ограничение FK ** ing **' col'. Для ограничения FK, указывающего другим способом, вам также не нужно. К сожалению, Q неясен в отношении направления ограничения FK. –

ответ

2

Я думаю, что только использование replace в этом формате сделает то, что вы хотите.

update tab 
set col = replace(col, ' ', ''); 

Для этого используется SQLFiddle.

+0

Функция замены не является глобальной, она просто удалит первый пробел, не так ли? Но хорошая ссылка, которую я не знал –

+0

В postgres documnentation говорится: * Заменить все вхождения в строке подстроки с помощью подстроки на * .. http://www.postgresql.org/docs/9.1/static/functions-string .html –

+0

Когда я попробую вашу ссылку, я вижу что-то другое ... –

1

Вы не должны использовать имя описательного столбца id, даже если некоторые из них имеют оральные ORM. Вместо этого я использую tab_id.

Я интерпретирую ваше описание таким образом: у вас есть другие столы с колонками FK, указывающие наtab.col. Подобно таблице child1 в моем примере ниже.

Чтобы очистить беспорядок, сделайте все это за один сеанс, чтобы сохранить временную таблицу, которую я использую. Еще лучше, сделайте все это в одной транзакции .

  1. Обновить все ссылающихся таблицы иметь все ссылающихся строки указывают на «первый» (однозначно - как бы вы определить, что) в наборе происходит, чтобы быть дубликатами в tab.

    Создать таблицу up перевода, которые будет использоваться для всех обновлений:

    CREATE TEMP TABLE up AS 
    WITH t AS (
        SELECT tab_id, col, replace(col, ' ', '') AS col1 
         ,row_number() OVER (PARTITION BY replace(col, ' ', '') 
              ORDER BY tab_id) AS rn 
        FROM tab 
        ) 
    SELECT b.col AS old_col, a.col AS new_col 
    FROM (SELECT * FROM t WHERE rn = 1) a 
    JOIN (SELECT * FROM t WHERE rn > 1) b USING (col1); 
    

    Затем обновить все ссылающуюся таблицу.

    UPDATE child1 c 
    SET col = up.new_col 
    FROM up 
    WHERE c.col = up.old_col; 
    
    -- more tables? 
    

    -> SQLfiddle

    Теперь все ссылки указывают на «первый» в группе простофиль, и вы получили лицензию, чтобы убить остальных.

  2. Удалить дублирующиеся строки кроме первых tab.

    DELETE FROM tab t 
    USING up 
    WHERE t.col = up.old_col 
    
  3. Убедитесь, что все ссылающееся ограничение FK есть пункт о ON UPDATE CASCADE.

    ALTER TABLE child1 DROP CONSTRAINT child1_col_fkey; 
    
    ALTER TABLE child1 ADD CONSTRAINT child1_col_fkey FOREIGN KEY (col) 
    REFERENCES tab (col) 
    ON UPDATE CASCADE; 
    
    -- more tables? 
    
  4. Санируйте свои значения по удаления пустого пространства

    UPDATE tab 
    SET col = replace(col, ' ', ''); 
    

    Это только заботится о старых добрых пробельных символов (значение 32 ASCII, Unicode U + 0020). У вас есть другие?

Все ограничения FK должны указывать на tab.tab_id для начала. Ваши таблицы будут меньше и быстрее, и все это будет проще.

+0

Сценарий может не совпадать с OP, но я считаю важным, что вы четко заявили об этом, и это имеет смысл (для меня так или иначе), равно как и подход к решению проблемы. На вторичной ноте в вашем ответе, почему вы говорите, что назначение столбца просто как «id» - это то, что OP * не должно делать, а не то, что вы сами * не сделали бы? Я имею в виду, это не вопрос личного предпочтения (или самой корпоративной политики)? Для меня, например, 'customer.id' было бы так же ясно, как' customer.customer_id'. Последнее * может быть несколько полезным в будущем, но так же может быть и первым. –

+0

@AndriyM: Ну, в какой-то степени это всегда вопрос мнения, конечно. Но использование не описательных имен, таких как 'id', - это почти всегда * плохой стиль. Когда вы присоединяетесь к нескольким таблицам в запросе - это то, что вы делаете * много * в реляционных базах данных, вы получаете несколько столбцов с именем 'id'. Это не помогает и приводит к ошибкам. Я рассмотрел несколько таких случаев здесь, на SO. –

0

Я решил это намного проще, чем Erwin. Я не SQL на моем компьютере, чтобы проверить это, но что-то подобное, что работал для меня:

DELETE FROM tab WHERE id IN (
    SELECT id FROM (
     SELECT id, col, row_number() OVER (PARTITION BY regexp_replace(col, '[ \t\n]*', '')) AS c WHERE c > 1; 
    ) 
) 

UPDATE tab SET col = regexp_replace(col, '[ \t\n]*', ''); 
+1

Как он мог работать, если бы вы не смогли его протестировать? Что именно вы имели в виду, * работал на меня *? –

+0

Я смог проверить его на работе. –

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