Солодянкин С.
8 сообщений
#16 лет назад
Подскажите, пожалуйста, как решить проблему.
Есть первичный ключ из двух полей: row_id типа int и row_date типа date.
row_date должен содержать текущую дату.
Нужно, чтобы row_id при добавлении записи увеличивался на 1 до тех пор, пока row_date не изменится (наступит следующий день).
Если row_date изменился, то счетчик row_id должен сброситься в 1 и при следующих добавлениях снова увеличиваться на 1.
Болатов А.
1090 сообщений
#16 лет назад
Когда разработчик пытается управлять значениями первичного ключа, это свидетельствует только о том, что структура разработанного им проекта не продумана и его надо переработать.
Новик Роман
697 сообщений
#16 лет назад
Создайте ещё одно поле с нужной Вам зависимостью. :blink:
One Choosen
183 сообщения
#16 лет назад
Насколько я знаю, вам не удасться положить на майскл работу по вашему хитрому инкрементированию поля row_id.
Я вижу выход следующий:
делаете выборку максимальной даты;
если выбранная дата равна сегодняшней, отгда выбераете еще максимальное row_id для этой даты, иначе вставляете запись с айди 1 и сегодняшней датой.

п.с.: ну думаю мой вариант -- это самое первое, простое и очевидное что могло прийти в голову... но думаю другого варинта нет... хотя может есть гуру майскл здесь ... ждемс.
Солодянкин С.
8 сообщений
#16 лет назад
2alibek
Это не свидетельствует ни о чем. Если стоит такая задача, тут хоть сколько проектируй.

2greenya
Спасибо большое за ответ! Мне тоже самое пришло в голову, но этот вариант кривоват. Может все таки кто-нибудь подскажет способ покрасивей?

2Novick
Можно поподробней.
One Choosen
183 сообщения
#16 лет назад
Но если честно, то я не совсем понимаю зачем вам может понадобится такой расклад в базе данных. изначально АйДи задумывалось как уникальный идетнификатор записи в пределах данной таблицы. возможно вам нужно такое для того чтобы потом при необходимости просто выводить данные и после выборки не делать никаких изменнения кроме сортировки типа ORDER BY row_date, row_id ? если "да", то я бы на вашем месте зделал так:
в таблице будет row_date куда вы будете при вставле писать row_date=NOW();
а когда нужно выбрать -- там уже нужно будет "руками" пронумеровать выбранные записи пере выводом (на экран например) .... тоесть на БД нумерация row_id не ложится.
-- имхо этот вариант правильнее.
Болатов А.
1090 сообщений
#16 лет назад
Цитата ("gray-82"):
2alibek
Это не свидетельствует ни о чем. Если стоит такая задача, тут хоть сколько проектируй.

Если эту задачу ставил клиент, то задачей разработчика (или менеджера) было при составлении ТЗ объяснить неправильность данного требования.
Новик Роман
697 сообщений
#16 лет назад
Цитата ("gray-82"):
Можно поподробней.

Создаёте дополнительное поле, в которое соответствующим запросом к интерфейсу БД заносите необходимое значение, или при выборке формируете нужное значение. По сути, то что описали Вы или greenya.
А что Вы ещё хотите от MySQL?
Вадим Т.
3240 сообщений
#16 лет назад
1. Нижеописанное решение будет работать только в MySQL 5.0.10+
2. Чтобы исключить конфликты при конкурентном доступе, инициировать вставки нужно используя транзакции (предпочтительно, если используете InnoD или MySQL блокировки (если используете MyISAM).
3. Это решение с таким ненормальным первичным ключем хоть и работает, но идеологически некорректно с точки зрения практической теории построения баз данных. gray-82, советую прислушаться к вышестоящим рекомендациям фрилансеров.

CREATE TABLE IF NOT EXISTS gray82test (
row_id INT(10) UNSIGNED NOT NULL DEFAULT 0,
row_date DATE NOT NULL DEFAULT '0000-00-00',
name VARCHAR(64),
PRIMARY KEY (row_id,row_date)
);

DELIMITER |

CREATE TRIGGER gray82test_bi BEFORE INSERT ON gray82test FOR EACH ROW
BEGIN
SET NEW.row_date = CURRENT_DATE;
SET NEW.row_id = (SELECT COALESCE(MAX(row_id) + 1, 1) FROM gray82test WHERE row_date = CURRENT_DATE);
END;
|

DELIMITER ;

INSERT INTO gray82test SET name = 'test1';
INSERT INTO gray82test SET name = 'test2';
INSERT INTO gray82test SET name = 'test3';
Новик Роман
697 сообщений
#16 лет назад
tvv, круто.
Вадим Т.
3240 сообщений
#16 лет назад
А если не хотите связываться с триггерами, или если имеете лишь MySQL 4 на хостинге, тогда значения ключа придется формировать вручную. Например так:

CREATE TABLE IF NOT EXISTS gray82test (
row_id INT(10) UNSIGNED NOT NULL DEFAULT 0,
row_date DATE NOT NULL DEFAULT '0000-00-00',
name VARCHAR(64),
PRIMARY KEY (row_id,row_date)
);


LOCK TABLES gray82test;
SET @id = (SELECT COALESCE(MAX(row_id) + 1, 1) FROM gray82test WHERE row_date = CURRENT_DATE);
INSERT INTO gray82test SET row_id = @id, row_date = CURRENT_DATE, name = 'your_value';
UNLOCK TABLES;


В этом примере 'your_value' передается Вами извне, а row_id и row_date формируется налету.

Если хотите обойтись без LOCK TABLES, и если используете InnoDB, можно использовать транзакции с уровнем изоляции SERIALIZABLE. Без этого никуда, или то или другое придется использовать, чтобы не было дублирования ключей при конкурентном доступе. Но увы, что LOCK TABLES, что SERIALIZABLE - убьет напрочь перфоменс при доступе к этой таблице. Поэтому советую отказаться от такого подхода с таким ключом.
Солодянкин С.
8 сообщений
#16 лет назад
Цитата ("alibek"):
Цитата ("gray-82"):
2alibek
Это не свидетельствует ни о чем. Если стоит такая задача, тут хоть сколько проектируй.

Если эту задачу ставил клиент, то задачей разработчика (или менеджера) было при составлении ТЗ объяснить неправильность данного требования.

Вообще-то задача разработчика - сделать все возможное, чтобы удовлетворить желания клиента, а не говорить ему: это нельзя, то нельзя.

Я нашел более простое и эффективное решение этой проблемы. Может кому пригодится.
Нужно просто создать первичный ключ, чтобы данные поля в нем шли в таком порядке: row_date, row_id.
alter table table1 drop PRIMARY key, add PRIMARY key ( `row_date`, `row_id` )
Поле row_id сделать auto_increment.
И все.
Болатов А.
1090 сообщений
#16 лет назад
Цитата ("gray-82"):
Вообще-то задача разработчика - сделать все возможное, чтобы удовлетворить желания клиента, а не говорить ему: это нельзя, то нельзя.

Это от неопытности.
Со временем придет понимание того, что необходим также постановщик задач. Если проект небольшой или бюджет маленький, то функции постановщика задач обычно берет на себя разработчик.
Вадим Т.
3240 сообщений
#16 лет назад
Цитата ("gray-82"):
Я нашел более простое и эффективное решение этой проблемы. Может кому пригодится.
Нужно просто создать первичный ключ, чтобы данные поля в нем шли в таком порядке: row_date, row_id.
alter table table1 drop PRIMARY key, add PRIMARY key ( `row_date`, `row_id` )
Поле row_id сделать auto_increment.
И все.


gray-82, У Вашего решения есть недостаток. Оно может работать работать только на таблицах типа MyISAM. Если используете InnoDB - работать не будет (то есть Вам тогда к сожалению придется забыть про транзакции и прочие прелести).

CREATE TABLE gray82test2 (
row_id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
row_date DATETIME NOT NULL DEFAULT '0000-00-00',
name VARCHAR(32) ,
PRIMARY KEY (row_date,row_id)
) TYPE=MyISAM;

ALTER TABLE gray82test2 TYPE = InnoDB;
Error: 1075 - Incorrect table definition; there can be only one auto column and it must be defined as a key
Андрей К.
35 сообщений
#16 лет назад
Автоприращение для ID следует отменить
Всё таки необходима таблица Tab(CDate,MaxID) и вот за чем
Insert tabTable set row_id = (Select case MaxID when isnull(MaxID) then 1 else MaxID+1 end case From Tab Where CDate = SamDate )
--прочие присвоения вставки
при этом возникает другая проблема: необходимо контролировать MaxID, чтобы он , был максимальным для своей даты.
к сожалению показанную вставку нельзя произвести в таблицу из тойже таблицы(или у меня не получается)
Вадим Т.
3240 сообщений
#16 лет назад
Цитата ("AndreyK"):
Автоприращение для ID следует отменить
Всё таки необходима таблица Tab(CDate,MaxID) и вот за чем
Insert tabTable set row_id = (Select case MaxID when isnull(MaxID) then 1 else MaxID+1 end case From Tab Where CDate = SamDate )
--прочие присвоения вставки
при этом возникает другая проблема: необходимо контролировать MaxID, чтобы он , был максимальным для своей даты.
к сожалению показанную вставку нельзя произвести в таблицу из той же таблицы(или у меня не получается)


Делать вставку из той же таблицы, из которой в этой же операции идет выборка - не получится, это досадное ограничение MySQL, кстати один из больших недостатков. А если использовать другую таблицу для счетчиков, тогда все равно потребуется использовать блокировки или транзакции чтобы гарантировать целостность при параллельных запросах. Так что, думаю, лучше использовать одно из моих решений, изложенных несколькими постами выше - или с триггером, или с переменной.

Хотя, если на транзакции наплевать, конечно можно и использовать предложенный gray-82 вариант с автоинкрементом. Хотя я себе слабо представляю проект, который может обойтись без транзакций и при этом хоть что-то гарантировать пользователям относительно целостности данных.
 A.
120 сообщений
#16 лет назад
Если стоит такая задача, тут хоть сколько проектируй.
ой, а можно задачу почитать. полностью, а не в напевках рабиновича?
Андрей К.
35 сообщений
#16 лет назад
Автора вопрооса беспокоит ключ на двух полях целочисленном и даты. Причем целочисленное поле никакой семантики не несёт(счетчик всегда можно вычислить). Если в поле даты всегда будет вставляться текущая дата, то тип этому полю следует задать DateTime - уникальность будет обеспечена(если не теоретически, то практически), а целочисленное поле можно сделать с автоприращением.
Но возникает вопрос как изменить существующие в таблице даты для сохранения уникальности.
Солодянкин С.
8 сообщений
#16 лет назад
Ключ записи создается один раз при вставке записи и однозначно идентифицирует запись. Во всяком случае для этих целей обычно используется ключ. Дата является частью ключа, поэтому ее нельзя менять. Если нужно поменять дату, то либо вынести ее из состава ключа, либо создать дополнительное поле с датой.
Солодянкин С.
8 сообщений
#16 лет назад
Цитата ("AndreyK"):
Автоприращение для ID следует отменить
Всё таки необходима таблица Tab(CDate,MaxID) и вот за чем
Insert tabTable set row_id = (Select case MaxID when isnull(MaxID) then 1 else MaxID+1 end case From Tab Where CDate = SamDate )
--прочие присвоения вставки
при этом возникает другая проблема: необходимо контролировать MaxID, чтобы он , был максимальным для своей даты.
к сожалению показанную вставку нельзя произвести в таблицу из тойже таблицы(или у меня не получается)

Не понял, почему следует отменить автоприращение.
Цитата ("tvv"):
gray-82, У Вашего решения есть недостаток. Оно может работать работать только на таблицах типа MyISAM. Если используете InnoDB - работать не будет (то есть Вам тогда к сожалению придется забыть про транзакции и прочие прелести).

Вообще, мне нужно было решение именно для MyIsam, так как с InnoDB все понятно. Триггеры, транзакции и хранимые процедуры могут все.