SQLinfo.ru - Все о MySQL

Форум пользователей MySQL

Задавайте вопросы, мы ответим

Вы не зашли.

#1 17.05.2010 16:00:55

Артём Н.
Активист
Зарегистрирован: 03.11.2009
Сообщений: 156

Выборка последнего изменения

Есть таблица, хранящая обновления:

Код:

create table if not exists change_log
(
   CLID                          INT AUTO_INCREMENT,
   TABLE_NAME                    CHAR(64),
   RECORD_ID                     CHAR(255),
   ACTION_TYPE                   ENUM('i', 'u', 'd', 'a'),
   INSERT_TIME                   TIMESTAMP
                                 DEFAULT CURRENT_TIMESTAMP
                                 ON UPDATE CURRENT_TIMESTAMP,
   primary key(CLID)
)
ENGINE=MEMORY
#ROW_FORMAT=DYNAMIC
CHARACTER SET cp1251 COLLATE cp1251_general_ci;

Из неё требуется выбрать последнее обновление для каждой записи каждой таблицы.
Последнее, во-первых, по timestamp, во-вторых, если timestamp у строк одинаковый, то по порядку вставки.

Пусть есть данные:

Код:

+------+------------+-----------+-------------+---------------------+
| CLID | TABLE_NAME | RECORD_ID | ACTION_TYPE | INSERT_TIME         |
+------+------------+-----------+-------------+---------------------+
|    1 | t1         | r0        | a           | 2010-05-17 15:42:34 |
|    2 | t1         | r0        | d           | 2010-05-17 15:42:45 |
|    3 | t2         | r0        | a           | 2010-05-17 15:42:45 |
|    4 | t2         | r1        | i           | 2010-05-17 15:42:45 |
+------+------------+-----------+-------------+---------------------+

Такой запрос:

Код:

select * from change_log
group by TABLE_NAME, RECORD_ID, INSERT_TIME
order by INSERT_TIME asc;

Выдаёт:

Код:

+------+------------+-----------+-------------+---------------------+
| CLID | TABLE_NAME | RECORD_ID | ACTION_TYPE | INSERT_TIME         |
+------+------------+-----------+-------------+---------------------+
|    1 | t1         | r0        | a           | 2010-05-17 15:42:34 |
|    3 | t2         | r0        | a           | 2010-05-17 15:42:45 |
|    4 | t2         | r1        | i           | 2010-05-17 15:42:45 |
+------+------------+-----------+-------------+---------------------+

Хотя должен:  2 | t1         | r0        | d, вместо первой записи.

Какой запрос нужно сделать для вывода последних изменений из списка?


"И ни птица, ни ива слезы не прольет,
Если сгинет с земли человеческий род.
И весна, и весна встретит новый рассвет,
Не заметив, что нас уже нет..."

Неактивен

 

#2 17.05.2010 16:25:27

paulus
Администратор
MySQL Authorized Developer and DBA
Зарегистрирован: 22.01.2007
Сообщений: 6757

Re: Выборка последнего изменения

SELECT TABLE_NAME, MAX(INSERT_TIME) AS INSERT_TIME, MAX(RECORD_ID) AS RECORD_ID.
Но надо понимать, что этот RECORD_ID может не соответствовать INSERT_TIME,
если он был больший. С другой стороны, если там использовать автоинкремент-
ное поле, то будет работать (т.к. порядок вставки тогда будет учтен).

Неактивен

 

#3 17.05.2010 16:43:39

Артём Н.
Активист
Зарегистрирован: 03.11.2009
Сообщений: 156

Re: Выборка последнего изменения

SELECT TABLE_NAME, MAX(INSERT_TIME) AS INSERT_TIME, MAX(RECORD_ID) AS RECORD_ID.
Но надо понимать, что этот RECORD_ID может не соответствовать INSERT_TIME,
если он был больший.

Не, RECORD_ID не имеет отношения к этой таблице. Это ID записи, которая была изменена в таблице TABLE_NAME.
Т.е., например, RECORD_ID может быть "user@host", а может быть "123" для таблицы t1 и  "123" для таблицы t2.
Мне нужно "объединить" все записи с одинаковыми RECORD_ID и одинаковыми TABLE_NAME в одну, у которой будут значения полей последней вставленной записи.

К тому же, пока что не знаю как быть с delete... Ведь, в принципе, если запись была удалена, нельзя её "объединять"?

С другой стороны, если там использовать автоинкремент-
ное поле, то будет работать (т.к. порядок вставки тогда будет учтен).

Хм... По CLID... Да, в принципе... Но почему вместо CLID не использовать INSERT_TIME? Или вместе с CLID, когда INSERT_TIME одинаков для одинаковых ао TABLE_NAME и RECORD_ID записей?

Только вот как это всё в запрос запихнуть - ума не приложу.


"И ни птица, ни ива слезы не прольет,
Если сгинет с земли человеческий род.
И весна, и весна встретит новый рассвет,
Не заметив, что нас уже нет..."

Неактивен

 

#4 17.05.2010 17:09:28

paulus
Администратор
MySQL Authorized Developer and DBA
Зарегистрирован: 22.01.2007
Сообщений: 6757

Re: Выборка последнего изменения

эээ… Вы вот это хотите?
SELECT TABLE_NAME, RECORD_ID, MAX(INSERT_TIME) AS INSERT_TIME, MAX(CLID) AS CLID
FROM change_log
GROUP BY TABLE_NAME, RECORD_ID

Неактивен

 

#5 17.05.2010 17:48:24

Артём Н.
Активист
Зарегистрирован: 03.11.2009
Сообщений: 156

Re: Выборка последнего изменения

Не совсем. Все данные те, но как сюда добавить ACTION_TYPE?

Код:

SELECT
MAX(CLID) AS CLID, TABLE_NAME, RECORD_ID, MAX(INSERT_TIME) AS INSERT_TIME,
ACTION_TYPE
FROM change_log
GROUP BY TABLE_NAME, RECORD_ID;

+------+------------+-----------+---------------------+-------------+
| CLID | TABLE_NAME | RECORD_ID | INSERT_TIME         | ACTION_TYPE |
+------+------------+-----------+---------------------+-------------+
|    2 | t1         | r0        | 2010-05-17 15:42:45 | a           |
|    3 | t2         | r0        | 2010-05-17 15:42:45 | a           |
|    4 | t2         | r1        | 2010-05-17 15:42:45 | i           |
+------+------------+-----------+---------------------+-------------+

В первом - ACTION_TYPE == 'd'. Чего-то я не соображаю...

Отредактированно Артём Н. (17.05.2010 17:50:16)


"И ни птица, ни ива слезы не прольет,
Если сгинет с земли человеческий род.
И весна, и весна встретит новый рассвет,
Не заметив, что нас уже нет..."

Неактивен

 

#6 17.05.2010 18:55:58

paulus
Администратор
MySQL Authorized Developer and DBA
Зарегистрирован: 22.01.2007
Сообщений: 6757

Re: Выборка последнего изменения

Нее, выбирать нужно только те строки, по которым группируете, и максимальные
значения. Остальные значения надо добывать присоединением (JOIN) изначальной
таблицы по всем получившимся полям sad

Неактивен

 

#7 17.05.2010 19:38:43

Артём Н.
Активист
Зарегистрирован: 03.11.2009
Сообщений: 156

Re: Выборка последнего изменения

А как это сделать?
Понимаю, что у меня совсем с головой туго...

Код:

mysql> SELECT
    -> MAX(clm.CLID) AS CL_ID, clm.TABLE_NAME, clm.RECORD_ID,
    -> MAX(clm.INSERT_TIME) AS INSERT_TIME,
    -> clj.ACTION_TYPE
    -> FROM change_log clm join change_log clj on CL_ID = clj.CLID
    -> GROUP BY TABLE_NAME, RECORD_ID;;
ERROR 1054 (42S22): Unknown column 'CL_ID' in 'on clause'

mysql> SELECT
    -> MAX(clm.CLID) AS CL_ID, clm.TABLE_NAME, clm.RECORD_ID,
    -> MAX(clm.INSERT_TIME) AS INSERT_TIME,
    -> clj.ACTION_TYPE
    -> FROM change_log clm, change_log clj
    -> where MAX(clm.CLID) = clj.CLID
    -> GROUP BY TABLE_NAME, RECORD_ID;;
ERROR 1111 (HY000): Invalid use of group function

sad И почему плохо через JOIN?


"И ни птица, ни ива слезы не прольет,
Если сгинет с земли человеческий род.
И весна, и весна встретит новый рассвет,
Не заметив, что нас уже нет..."

Неактивен

 

#8 17.05.2010 19:39:43

Артём Н.
Активист
Зарегистрирован: 03.11.2009
Сообщений: 156

Re: Выборка последнего изменения

Единственный гарантированно уникальный ID - это CLID.


"И ни птица, ни ива слезы не прольет,
Если сгинет с земли человеческий род.
И весна, и весна встретит новый рассвет,
Не заметив, что нас уже нет..."

Неактивен

 

#9 17.05.2010 19:46:51

paulus
Администратор
MySQL Authorized Developer and DBA
Зарегистрирован: 22.01.2007
Сообщений: 6757

Re: Выборка последнего изменения

Аа, CLID уникальный? И, небось, автоинкрементальный. Давайте тогда упростим задачу:
В данном случае из «максимальный CLID» следует «максимальный INSERT_TIME», поэтому
второй можно вообще выкинуть. Тогда у нас остаётся очень простой подзапрос для вы-
борки нужных CLID и общий запрос какой-то такой:

SELECT *
FROM change_log
JOIN (SELECT MAX(CLID) AS CLID FROM change_log GROUP BY TABLE_NAME) subq USING (CLID);

Неактивен

 

#10 17.05.2010 19:52:26

Артём Н.
Активист
Зарегистрирован: 03.11.2009
Сообщений: 156

Re: Выборка последнего изменения

Аа, CLID уникальный? И, небось, автоинкрементальный.

Ну, как следует из вышеприведённого CREATE TABLE - да. smile

С небольшой поправкой работает:

Код:

SELECT *
FROM change_log
JOIN (SELECT MAX(CLID) AS CLID FROM change_log GROUP BY TABLE_NAME, RECORD_ID) subq USING (CLID);

smile Но, конечно, не фига себе запрос... o.O JOIN с подзапросом... Плюс какой-то USING.
И ещё:
1.) А почему плохо через JOIN делать?
2.) Что будет, когда CLID дойдёт до макс. значения?


"И ни птица, ни ива слезы не прольет,
Если сгинет с земли человеческий род.
И весна, и весна встретит новый рассвет,
Не заметив, что нас уже нет..."

Неактивен

 

#11 17.05.2010 20:16:35

paulus
Администратор
MySQL Authorized Developer and DBA
Зарегистрирован: 22.01.2007
Сообщений: 6757

Re: Выборка последнего изменения

Хм... надо читать сообщения с начала smile Я пытался врубиться в суть, и не заметил smile

1) В таком виде — очень даже не плохо. Плохо было в варианте, когда у нас был боль-
шой зверь с большим количеством полей. Для подзапроса надо ключик на (TABLE_NAME, RECORD_ID, CLID).

2) Перестанут вставляться строки smile

Неактивен

 

#12 17.05.2010 20:52:12

Артём Н.
Активист
Зарегистрирован: 03.11.2009
Сообщений: 156

Re: Выборка последнего изменения

Спасибо.
С запросом понятно.

Перестанут вставляться строки

А если имеется меньше строк, чем макс. значение для типа ID? Но ID считается не с 0? Не будет ли "сброса" в 0?


"И ни птица, ни ива слезы не прольет,
Если сгинет с земли человеческий род.
И весна, и весна встретит новый рассвет,
Не заметив, что нас уже нет..."

Неактивен

 

#13 18.05.2010 13:30:27

paulus
Администратор
MySQL Authorized Developer and DBA
Зарегистрирован: 22.01.2007
Сообщений: 6757

Re: Выборка последнего изменения

Нет, не будет, разумеется. Иначе бы в мире периодически начинались мистические
баги с пересекающимися ID smile

Неактивен

 

#14 19.05.2010 08:45:30

Артём Н.
Активист
Зарегистрирован: 03.11.2009
Сообщений: 156

Re: Выборка последнего изменения

Что же тогда с ним делать? Ну, т.е. таблица не будет слишком большой, поскольку ранние записи удаляются в триггере. Но что делать с ID?


"И ни птица, ни ива слезы не прольет,
Если сгинет с земли человеческий род.
И весна, и весна встретит новый рассвет,
Не заметив, что нас уже нет..."

Неактивен

 

#15 19.05.2010 11:52:55

paulus
Администратор
MySQL Authorized Developer and DBA
Зарегистрирован: 22.01.2007
Сообщений: 6757

Re: Выборка последнего изменения

Что Вас смущает? Что закончится INT? Поставьте BIGINT UNSIGNED, он не закончится.

Неактивен

 

#16 19.05.2010 17:06:46

Артём Н.
Активист
Зарегистрирован: 03.11.2009
Сообщений: 156

Re: Выборка последнего изменения

Ну, в теории, он может закончиться... Меня смущает то, что количество записей будет сильно несоответствовать значению идентификатора.
Вот если-бы его как-то сбрасывать... Только как это сделать не очень сложно?

Отредактированно Артём Н. (19.05.2010 17:07:01)


"И ни птица, ни ива слезы не прольет,
Если сгинет с земли человеческий род.
И весна, и весна встретит новый рассвет,
Не заметив, что нас уже нет..."

Неактивен

 

#17 19.05.2010 17:47:41

paulus
Администратор
MySQL Authorized Developer and DBA
Зарегистрирован: 22.01.2007
Сообщений: 6757

Re: Выборка последнего изменения

Ну, в теории, если Вы будете его инкрементировать, скажем, 1000 раз в секунду, то Вам
его хватит на 2^64 / 1000 / 86400 / 365 = 584942417 лет. Можете не беспокоиться, Ваши
правнуки тоже не будут нести ответственность за переполнение wink

Неактивен

 

#18 19.05.2010 21:42:06

Артём Н.
Активист
Зарегистрирован: 03.11.2009
Сообщений: 156

Re: Выборка последнего изменения

Ну, проблемы теоретических правнуков меня не очень-то волнуют. :-\
Меня больше интересует возможно ли сделать так, чтобы не переполнялось?


"И ни птица, ни ива слезы не прольет,
Если сгинет с земли человеческий род.
И весна, и весна встретит новый рассвет,
Не заметив, что нас уже нет..."

Неактивен

 

#19 20.05.2010 10:08:52

paulus
Администратор
MySQL Authorized Developer and DBA
Зарегистрирован: 22.01.2007
Сообщений: 6757

Re: Выборка последнего изменения

Можно скидывать счетчик. Но за возникшие проблемы сами будете нести ответственность wink

ALTER TABLE tablename AUTO_INCREMENT = 0;

Неактивен

 

#20 20.05.2010 11:32:16

Артём Н.
Активист
Зарегистрирован: 03.11.2009
Сообщений: 156

Re: Выборка последнего изменения

Проблема не в том, как скинуть счётчик, а в том, что при сбросе начнётся отсчёт с нуля.
Но в таблице ещё останутся записи со старыми значениями счётчика.
В итоге... sad

Выглядит процедурка, которая заносит в таблицу изменения примерно так:

Код:

#
# Tables updation fixer.
#

DROP PROCEDURE IF EXISTS FixTableChange;

CREATE
DEFINER = 'root'@'%'
PROCEDURE FixTableChange(
  IN v_table_name CHAR(64),
  IN v_record_key CHAR(255),
  IN v_action ENUM('i', 'u', 'd', 'a')
)
SQL SECURITY INVOKER
COMMENT 'Служебная. Фиксация обновлений таблиц. Вызывается в триггерах.'
sproc: BEGIN
  declare client_refresh_rate INT;

  insert into change_log(TABLE_NAME, RECORD_ID, ACTION_TYPE)
    values(v_table_name, v_record_key, v_action);

  select (REFRESH_TIME + 1) * 2 from self_info into client_refresh_rate;
  # Очистка старых значений.
  delete from change_log
    where (UNIX_TIMESTAMP() - UNIX_TIMESTAMP(INSERT_TIME) >= client_refresh_rate);
END;

/*==============================================================*/
/* Triggers : base_sum                                          */
/*==============================================================*/

DROP TRIGGER IF EXISTS `base_sum_ins`;
DROP TRIGGER IF EXISTS `base_sum_upd`;
DROP TRIGGER IF EXISTS `base_sum_del`;

CREATE
DEFINER = 'root'@'%'
TRIGGER `base_sum_ins` AFTER INSERT ON `base_sum`
FOR EACH ROW
BEGIN
  call FixTableChange('base_sum',
    CONCAT_WS(';', NEW.ID_CLIENT_TYPE_GROUP, NEW.ID_CAR_TYPE), 'i');
END;

И вопрос в том как сделать. Неужели придётся у всех менять ID?


"И ни птица, ни ива слезы не прольет,
Если сгинет с земли человеческий род.
И весна, и весна встретит новый рассвет,
Не заметив, что нас уже нет..."

Неактивен

 

#21 20.05.2010 12:56:11

paulus
Администратор
MySQL Authorized Developer and DBA
Зарегистрирован: 22.01.2007
Сообщений: 6757

Re: Выборка последнего изменения

Ненене, меня на это не разведете smile Есть правильный способ, а есть Ваш. Я не буду
Вам помогать раскручивать Ваш способ, потому что он не правильный. Правильный —
не менять ID никогда, а когда они закончатся через пол миллиона лет — поменять
один раз руками, перестроив всю таблицу.

Неактивен

 

#22 20.05.2010 21:15:40

Артём Н.
Активист
Зарегистрирован: 03.11.2009
Сообщений: 156

Re: Выборка последнего изменения

Зато, мой способ теоретически правильнее. wink
Ведь, по-идее, ID должен кончаться только тогда, когда число записей в таблице равно макс. значению BIGINT.
Сейчас мне пришло в голову только:
1.) Искать дельту между текущим мин. ID и 0.
2.) Обновлять, если требуется, ID всех записей.
3.) Устанавливать autoincrement в 0.

Просто, может, какое стандартное средство есть?

P.S.:
Ну, вообще-то, мне это не принципиально. Поскольку, без этого и так куча проблем.
Вот, например, с CURRENT_USER() фигня. sad


"И ни птица, ни ива слезы не прольет,
Если сгинет с земли человеческий род.
И весна, и весна встретит новый рассвет,
Не заметив, что нас уже нет..."

Неактивен

 

Board footer

Работает на PunBB
© Copyright 2002–2008 Rickard Andersson