MySQL - отметить все строки, кроме 1 - PullRequest
2 голосов
/ 28 октября 2008

Это похоже на этот вопрос , но кажется, что некоторые ответы там не совсем совместимы с MySQL (или я делаю это неправильно), и у меня есть черт времени выяснить изменения, которые мне нужны. Видимо мой SQL более грубый, чем я думал. Я также ищу, чтобы изменить значение столбца, а не удалить, но я думаю, по крайней мере , что часть проста ...

У меня есть таблица как:

rowid SERIAL
fingerprint TEXT
duplicate BOOLEAN
contents TEXT
created_date DATETIME

Я хочу установить duplicate = true для всех, кроме первого (by_date) каждой группы по отпечатку пальца. Легко пометить все строк с дублирующимися отпечатками пальцев как дубликаты. Часть, на которой я застреваю - это первая.

Одно из приложений, которое заполняет таблицу, выполняет массовую загрузку данных, когда несколько работников загружают данные из разных источников, а данные работников не обязательно разбиваются по дате, поэтому пытаться пометить их все как они входят (первый вставленный не обязательно первый по дате). Кроме того, у меня уже есть куча данных, которые мне нужно очистить в любом случае. Поэтому я предпочел бы просто создать относительно эффективный запрос, который можно выполнить после массовой загрузки, чтобы очистить его, чем пытаться встроить его в это приложение.

Спасибо!

Ответы [ 6 ]

2 голосов
/ 20 марта 2009

MySQL необходимо явно указать, если объем данных, по которым вы группируете, превышает 1024 байта (см. эту ссылку для получения подробной информации). Поэтому, если ваши данные в столбце отпечатка пальца больше 1024 байт, вам следует использовать переменную max_sort_length (подробности о допустимых значениях см. В этой ссылке , а в этой ссылке о том, как установите его) на большее число, чтобы группа не использовала молча только часть ваших данных для группировки.

Как только вы убедитесь, что MySQL сгруппирует ваши данные должным образом, следующий запрос установит флаг дублирования, так что для первой записи отпечатка пальца будет задано значение FALSE / 0, а для всех последующих записей отпечатка пальца будет задано значение TRUE / 1:

    UPDATE mytable m1
INNER JOIN (SELECT fingerprint
                 , MIN(rowid) AS minrow 
              FROM mytable m2 
          GROUP BY fingerprint) m3 
        ON m1.fingerprint = m3.fingerprint
       SET m1.duplicate = m3.minrow != m1.rowid;

Имейте в виду, что в этом решении не учитываются значения NULL, и если поле отпечатка может иметь значение NULL, вам потребуется дополнительная логика для обработки этого случая.

0 голосов
/ 09 мая 2009

Непроверено ...

UPDATE TheAnonymousTable
   SET duplicate = TRUE
 WHERE rowid NOT IN
       (SELECT rowid
          FROM (SELECT MIN(created_date) AS created_date, fingerprint
                  FROM TheAnonymousTable
                 GROUP BY fingerprint
               ) AS M,
               TheAnonymousTable AS T
         WHERE M.created_date = T.created_date
           AND M.fingerprint  = T.fingerprint
       );

Логика заключается в том, что самый внутренний запрос возвращает самый ранний created_date для каждого отдельного отпечатка пальца как псевдоним таблицы M. Средний запрос определяет значение rowid для каждой из этих строк; это неприятно (но необходимо), и код предполагает, что вы не получите две записи для одного и того же отпечатка пальца и отметки времени. Это дает вам rowid для записи списка вкладок для каждого отдельного отпечатка пальца. Затем внешний запрос (ОБНОВЛЕНИЕ) устанавливает флаг «дубликата» во всех тех строках, где идентификатор строки не является одной из самых ранних строк.

Некоторые СУБД могут быть недовольны выполнением (вложенных) подзапросов в обновляемой таблице.

0 голосов
/ 31 октября 2008

Вот еще один способ сделать это, используя многотабличный синтаксис MySQL UPDATE:

UPDATE mytable m1
  JOIN mytable m2 ON (m1.rowid = m2.rowid AND m1.created_date < m2.created_date)
SET m2.duplicate = 1;
0 голосов
/ 29 октября 2008

Я не знаю синтаксис MySQL, но в PLSQL вы просто делаете:

UPDATE t1
SET duplicate = 1
FROM MyTable t1
WHERE rowid != (
  SELECT TOP 1 rowid FROM MyTable t2
  WHERE t2.fingerprint = t1.fingerprint ORDER BY created_date DESC
)

Это может иметь некоторые синтаксические ошибки, так как я просто печатаю манжету / не могу проверить это, но в этом суть.


Версия MySQL (не тестировалась):

UPDATE t1
  SET duplicate = 1
FROM MyTable t1
WHERE rowid != (
  SELECT rowid FROM MyTable t2
  WHERE t2.fingerprint = t1.fingerprint
  ORDER BY created_date DESC
  LIMIT 1
)
0 голосов
/ 28 октября 2008

Вот забавный способ сделать это:

SET @rowid := 0;

UPDATE mytable
SET duplicate = (rowid = @rowid), 
    rowid = (@rowid:=rowid)
ORDER BY rowid, created_date;
  • Сначала установите пользовательскую переменную на ноль, предполагая, что она меньше, чем любая строка в вашей таблице.
  • Затем используйте функцию MySQL UPDATE...ORDER BY, чтобы убедиться, что строки обновляются по порядку на rowid, а затем на created_date.
  • Для каждой строки, если текущий rowid не равен пользовательской переменной @rowid, установите duplicate в 0 (false). Это будет верно только для первой строки, с которой встречается данное значение для rowid.
  • Затем добавьте фиктивный набор rowid к своему собственному значению, установив @rowid для этого значения в качестве побочного эффекта.
  • Как и UPDATE следующей строки, если она является дубликатом предыдущей строки, rowid будет равна пользовательской переменной @rowid, и поэтому duplicate будет установлено в 1 (true).

Редактировать: Теперь я проверил это и исправил ошибку в строке, которая устанавливает duplicate.

0 голосов
/ 28 октября 2008

Как насчет двухэтапного подхода, предполагающего, что вы можете отключиться во время загрузки данных:

  • Отметить каждый элемент как дубликат.
  • Выберите самую раннюю строку из каждой группы и снимите флажок дублирования.

Не элегантно, но выполняет свою работу.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...