Нужно добавить или обновить запись при определенных условиях

evshi

Новичок
Мне надо проверить, если в таблице user_history у uid (user id) более 10 записей, то найти самую старую по dt, и на место ее записать новую. Если же записей у uid < 10, то сделать insert.

Вот структура таблицы:
CREATE TABLE `user_history` (
`id` int(11) NOT NULL auto_increment,
`uid` int(11) default NULL,
`objId` int(11) default NULL,
`objType` int(11) default NULL,
`dt` datetime default NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=cp1251;


Пытаюсь сделать через IF:
SELECT
IF(
((SELECT COUNT(*) FROM `user_history` WHERE `uid`=3)>10),
1,
(INSERT INTO `user_history` (`uid`) VALUES(99))
)
Но уже на этом шаге вываливается ошибка синтаксиса. Как правильно решить данную задачу?
 

Gas

может по одной?
одном запросом никак, делай insert, а потом удаляй лишние или по крону раз в день, например.
 

evshi

Новичок
Чтобы удалить лишние, сначала надо посчитать кол-во записей. И если их >10, удалить, все равно IF напрашивается.
 

Dovg

Продвинутый новичок
>Можно без подзапросов:
>Эээ, а разве MyISAM поддерживает транзакции?
Извините, привычка.

В альтернативной субд транзакции есть всегда и нельзя сделать delete с лимитом. :)
 

evshi

Новичок
Хотя секундочку...., мне же надо хранить последние 10 добавленных записей. Если я буду удалять, не проверяя кол-ва, то у меня все время будет одна запись.

И кстати, обнаружил сложность №2: мне надо у каждого uid хранить только уникальные сочетания objId + objType, т.е. для одного uid связка objId + objType повторяться не должна. Я сделал уникальный ключ на uid + objId + objType. Но в случае, если insert пытается добавить уже существующую связку, запись не добавляется, но mysql ругается. Как можно избежать появления ошибок?
 

evshi

Новичок
А если нужно в стеке оставить только N записей после добавления, то без подзапроса не обойтись. Что-то вроде:
delete t1 from t1
INNER JOIN (select id from t1 order by id desc limit 10, 100) as t
where t1.id = t.id;
Спасибо, заработало, а с условием удаления старой и добавления новой ошибки, видимо, можно действительно избежать, но все же решение получилось не очень красивым по двум причинам:
1. При таком решении значения столбца id все время растут. При большом количестве запросов получится нехорошо:(
2. С точки зрения логики все же странно сначала добавить, а потом удалить. Правильнее было бы сделать update.

Но все равно спасибо - это лучше, чем ничего.
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
внутри базы update реализован как insert+delete

какие нафиг подзапросы, просто
1. insert ...
2. select count(*) where user_id=...
3. if ($count>10) select id ... order by dt asc limit $count-10
4. $ids = implode(',',$result);
5. "delete ... where id in (".$ids.")"

все эти хаки под mysql, подзапросы и т.п. надо юзать когда знаешь, что так надо
 

evshi

Новичок
Какой-то mysql негибкий получается:( В итоге сделал кучу запросов но работает:
PHP:
function updateUserHistory($objId, $objType, $uid){
	if ($objId AND $objType AND $uid) {
		mysql_query("SELECT `id` FROM `user_history` WHERE `uid`=$uid");
		$rowQnt=mysql_affected_rows();
		if ($rowQnt>9) {
			if ($rowQnt<11) {
				//надо проверить, встречается ли ключ $uid-$objId-$objType?
				$dblKeyId=mysql_fetch_array(mysql_query("SELECT `id` FROM `user_history` WHERE `uid`=$uid AND `objId`=$objId AND `objType`=$objType LIMIT 1"));
				if ($dblKeyId[id]) {//если нашел, то делаю update `dt`=NOW() у этой записи
					mysql_query("UPDATE `user_history` SET `dt`=NOW() WHERE `id`=".$dblKeyId[id]." LIMIT 1");
				}else{//если не нашел, делаю update самой старой записи
					$oldestId=mysql_fetch_array(mysql_query("SELECT `id` FROM `user_history` ORDER BY `dt` LIMIT 1"));
					mysql_query("UPDATE `user_history` SET `uid`=$uid, `objId`=$objId, `objType`=$objType, `dt`=NOW() WHERE `id`=".$oldestId[id]." LIMIT 1");
				}
			}else alertMe("udateUserHistory: rowQnt >10");
		}else{
			mysql_query("INSERT INTO `user_history`	(`uid`, `objId`, `objType`, `dt`) VALUES ($uid, $objId, $objType, NOW()) ON DUPLICATE KEY UPDATE `dt`=NOW()");
		}
	}
}
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
фигню ты написал, читай доку по mysql_affected_rows и mysql_num_rows

алгоритм у тебя хреновый, но 2й раз объяснять не буду
 

Вурдалак

Продвинутый новичок
Chusha, сам алгоритм уже расписал grigori.

Хотел бы донести до пионера в MySQL, что там есть INSERT IGNORE. Через «собаку» подавляют ошибку только мудаки.
 

antson

Новичок
Партнер клуба
Честно не вижу смысла так сложно делать инсерт ?
Какой в этом глубокий смысл ? Экономия дискового пространства ?

В предложенной постановке операция логирования становиться неоправданно тяжелой.
Я бы сделал простые инсерты и при просмотре просто добавил лимит 10 записей.
А чистку базы вынес на крон раз в сутки в 3 часа ночи сразу после бекапов .
 
Сверху