powered by simpleCommunicator - 2.0.60     © 2026 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / FoxPro, Visual FoxPro [игнор отключен] [закрыт для гостей] / Вставка в таблицу записи с уникальным id
46 сообщений из 46, показаны все 2 страниц
Вставка в таблицу записи с уникальным id
    #32364284
Т.е. надо автоматически генерировать id для записей в таблице (по сути аналог счетчика в Access). Как это лучше сделать? Заранее благодарен!
...
Рейтинг: 0 / 0
Вставка в таблицу записи с уникальным id
    #32364451
Фотография AngelOKES
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Делаешь поле типа int
и когда запись добовляешь, то выбирай максимальный id и +1
Вот и все!!!
...
Рейтинг: 0 / 0
Вставка в таблицу записи с уникальным id
    #32364502
Igor Korolyov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
авторкогда запись добовляешь, то выбирай максимальный id и +1
Очень даже вредный совет!

Посмотри как сделано в сэмплах и в Tastrade - суть в наличии специальной системной таблицы, где хранятся "последние использованные" ключи для каждого такого автоинкрементного поля. И хранимая процедура, которая возвращает очередной ключик и наращивает соответствующий счётчик в системной таблице.
Кстати в VFP8 есть AUTOINC модификатор для Integer полей, но по ряду причин я его не использую (в частности проблемы с представлениями на основе таблиц с AUTOINC).
WBR, Igor
...
Рейтинг: 0 / 0
Вставка в таблицу записи с уникальным id
    #32364575
Фотография FM32YO aka KID
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
FUNCTION NewId
PARAMETERS cTableName

select ids.next_id AS Next_n FROM ids ;
WHERE ALLTRIM(UPPER(ids.table_name)) = ALLTRIM(UPPER(cTablename));
INTO CURSOR nexter

nNewId = nexter.Next_n + 1

UPDATE ids SET next_id = nNewId WHERE ALLTRIM(UPPER(ids.table_name)) = ALLTRIM(UPPER(cTablename))

RETURN nNewId
*ENDFUNC
...
Рейтинг: 0 / 0
Вставка в таблицу записи с уникальным id
    #32364686
Igor Korolyov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Тоже плохой пример - нужно явное управление блокировками - RLOCK() или уж на крайний случай FLOCK() для систенмной таблицы... В общем чего мучится то, всё уже есть, давно написано...
WBR, Igor
...
Рейтинг: 0 / 0
Вставка в таблицу записи с уникальным id
    #32364762
FM32YO__
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
это ids блокировать надо?
а зачем? что-то до сих пор не было проблем с доступом...
Хотя может у Вас опыта больше...
...
Рейтинг: 0 / 0
Вставка в таблицу записи с уникальным id
    #32364810
Igor Korolyov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
FM32YO__ это ids блокировать надо?Да. Его родимого и надо.
автора зачем? что-то до сих пор не было проблем с доступом...
Потому как при сетевой работе вполне возможна ситуация, когда в промежутке между SELECT и UPDATE с другой машины будет в свою очередь запрошен счётчик, и получится что 2 разные сессии получили один и тот-же ключ. Блокировка как раз гарантирует, что этого не произойдёт. Т.е. пока первая сессия не отлочит запись/таблицу а вторая сессия её не залочит - ничего читать/писать нельзя.
То что это не слишком вероятно (ну компы нонче быстрые, юзеров много не сидит, да и интенсивность вставок новых записей как правило не очень большая) не означает, что это нужно игнорировать...
WBR, Igor
...
Рейтинг: 0 / 0
Вставка в таблицу записи с уникальным id
    #32364827
Фотография FM32YO aka KID
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
понял СПАСИБО!
а не подскажете КАК малой кровью в моей функции єто проследить???
...
Рейтинг: 0 / 0
Вставка в таблицу записи с уникальным id
    #32364845
Igor Korolyov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Ну посмотри наконец на те хорошие примеры что я в самом начале заслал :(
Tastrade и NewID из Solution-ов. Там хоть и не идеально но есть блокировка.

WBR, Igor
...
Рейтинг: 0 / 0
Вставка в таблицу записи с уникальным id
    #32364846
Фотография FM32YO aka KID
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
я тут подума - а с какой скоростью операторы должны наяривать данные чтобы свалить такой счетчик???
промежуток времени между
select ids.next_id

и

UPDATE
ИМХО настолько мал!!!
Или Вы имеете в виду 500 операторов одновременно и в один момент.. не знаю как в РОССИИ но ИМХО нигде так не наяривают... = собственный опыт
...
Рейтинг: 0 / 0
Вставка в таблицу записи с уникальным id
    #32364857
Igor Korolyov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Део не в том наяривают или нет, а в том, что писать надо надёжно! Как говорится из ошибок встречаются чаще всего непредусмотренные :)
WBR, Igor
...
Рейтинг: 0 / 0
Вставка в таблицу записи с уникальным id
    #32364858
bdv9
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Большой скорости и не надо.
При работе в сети нескольких (до 20 и более) пользователей у меня до того как явно стал применять RLOCK() такие ситуации были по разу в неделю точно.
Сейчас работает вот это и проблем нет (кстати взял с TasTrade Solution)
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
*******************************************
*** Создание уникального идентификатора ***
*******************************************
*!*	Вх.  д.: tcTab_Name - значение поля tab_name
*!*	         tcField_Name - значение поля key_name
*!*	Вых. д.: уникальный идентификатор (из чисел)
*******************************************
*!*	Вызов функции: ... = NewIDOld('значение поля tab_name', 'значение поля key_name')
*******************************************
*!*	Для работы функции необходимо иметь файл gep_setu
*!*	gep_setu.dbf
*!*		tab_name	C( 25 )	- имя файла, для которого создается ключ
*!*		key_name	C( 10 )	- имя поля, для которого создается ключ
*!*		key_len		N( 2 , 0 )	- длина ключа
*!*		value		C( 10 )	- текущее значение ключа
*!*		индекс		tab_name = UPPER(tab_name) + UPPER(key_name)
FUNCTION NewIDOld
PARAMETERS tcTable_Name, tcField_Name

Local lcOldAlias, lcFileName, lcTagName, lcOldReprocess, lnKeyLen, lnValue

lcOldAlias = ALIAS()
lcFileName = 'gep_setu'
lcTagName = 'tab_name'
lcOldReprocess = SET('REPROCESS')

SET REPROCESS TO AUTOMATIC

= OpenTable(gcPathToCurrData+'KOD\gep_setu.dbf', lcFileName, lcTagName)
SELECT (lcFileName)

tcTable_Name = PADR(UPPER(ALLTRIM(tcTable_Name)), 25 ,' ')
tcField_Name = PADR(UPPER(ALLTRIM(tcField_Name)), 10 ,' ')

IF SEEK(tcTable_Name+tcField_Name, lcFileName, lcTagName)
	IF RLOCK()
		lnKeyLen = Key_Len
		REPLACE Value WITH PADR(PADL(ALLTRIM(STR(VAL(Value)+ 1 )),lnKeyLen,'0'), 10 ,' ')
		lnValue = VAL(Value)
		UNLOCK
	ENDIF
ELSE
	= MESSAGEBOX('Невозможно создать уникальное имя по ключу' + Chr( 13 ) + ;
		'Ключ: ' + AllTrim(tcTable_Name) + ' + ' + AllTrim(tcField_Name) + Chr( 13 ) + Chr( 13 ) + ;
		'Приложение будет закрыто', 0 + 16 ,'Ошибка')
	DO PQuit IN Main.PRG
ENDIF

IF NOT EMPTY(lcOldAlias)
	SELECT (lcOldAlias)
ENDIF

SET REPROCESS TO lcOldReprocess

RETURN PADL(ALLTRIM(STR(lnValue)),lnKeyLen,'0')
...
Рейтинг: 0 / 0
Вставка в таблицу записи с уникальным id
    #32365137
Фотография NNN
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
2Igor Korolyov

Покритикуй, давно я не занимался подобными вещами..
Не требуется ни обработки блокировок, ни дополнительных таблиц, но необходим индекс типа primary или candidate и имя тэга должно совпадать с именем поля, которое должно иметь тип Integer. Идея - в обработке ошибки 1884 (Uniqueness of index "name" is violated). Пример перегружен проверками, в работе я использовал более простую функцию (как давно это было!)
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
FUNCTION NewId
LPARAMETERS tcAlias, tnRetryLimit
IF EMPTY(tcAlias)
	tcAlias=ALIAS()
ENDIF 
IF TYPE('tcAlias')#'C' OR EMPTY(tcAlias) OR !USED(tcAlias)
	RETURN  0 
ENDIF 
LOCAL ARRAY laTags[ 1 ]
LOCAL lnTagCount, i
lnTagCount=ATAGINFO(laTags,'',tcAlias)
IF lnTagCount= 0 
	RETURN  0 
ENDIF
LOCAL lcTag 
lcTag=''
FOR i= 1  TO lnTagCount
	IF laTags[i, 2 ]='PRIMARY' OR laTags[i, 2 ]='CANDIDATE'
		IF TYPE(tcAlias+'.'+laTags[i, 1 ])='N'
			lcTag=laTags[i, 1 ]
			EXIT 
		ENDIF 
	ENDIF 
ENDFOR 
IF EMPTY(lcTag)
	RETURN  0 
ENDIF 
#define MAX_INT  2147483647 
#define MAX_TRY  10 
IF TYPE('tnRetryLimit')#'N' OR EMPTY(tnRetryLimit)
	tnRetryLimit=MAX_TRY
ENDIF 
LOCAL lnMaxId
LOCAL lnRetryCount
LOCAL lnRetryCount2
LOCAL lcOnError
LOCAL lcSetNear
LOCAL lnRecno
lnMaxId= 0 
lnRetryCount= 0 
lnRetryCount2= 0 
lcOnError=ON('Error')
lcSetNear=SET('Near')
lnRecno=RECNO(tcAlias)
DO WHILE lnRetryCount<tnRetryLimit
	SET NEAR ON
	SEEK MAX_INT ORDER TAG &lcTag DESCENDING IN &tcAlias
	lnMaxId=&tcAlias..&lcTag+ 1 
	ON ERROR lnRetryCount=lnRetryCount+ 1 
	INSERT INTO &tcAlias (&lcTag) VALUES (lnMaxId)
	ON ERROR &lcOnError
	SET NEAR &lcSetNear
	IF lnRetryCount2=lnRetryCount
		EXIT 
	ENDIF 
	lnRetryCount2=lnRetryCount
	GOTO lnRecno IN &tcAlias
	lnMaxId= 0 
ENDDO 
RETURN lnMaxId
ENDFUNC 

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
CREATE CURSOR test (id I UNIQUE, field1 C( 10 ), field2 C( 10 ))
LOCAL lnId
lnId=NewID()
IF lnId> 0 
	UPDATE test SET field1=SYS( 2015 ), field2=SYS( 2015 ) ;
	WHERE id=lnId
ELSE
	=MESSAGEBOX('Ошибка при добавлении новой записи')
ENDIF 
lnId=NewID('test')
IF lnId> 0 
	UPDATE test SET field1=SYS( 2015 ), field2=SYS( 2015 ) ;
	WHERE id=lnId
ELSE
	=MESSAGEBOX('Ошибка при добавлении новой записи')
ENDIF 
lnId=NewID('test',  20 )
IF lnId> 0 
	UPDATE test SET field1=SYS( 2015 ), field2=SYS( 2015 ) ;
	WHERE id=lnId
ELSE
	=MESSAGEBOX('Ошибка при добавлении новой записи')
ENDIF 
BROWSE 
USE 
...
Рейтинг: 0 / 0
Вставка в таблицу записи с уникальным id
    #32365171
iwa
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
ставится фокс-8 и проблема решается сама собой, так как там есть поле integer(autoincrement)
...
Рейтинг: 0 / 0
Вставка в таблицу записи с уникальным id
    #32365250
Фотография FM32YO aka KID
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Коллуги или я неверно пояснил.. или Вы неверно поняли....
в моем примере есть заранее созданная таблица
ids [Next_id N(10,0), Table_name C(15)]
0 table1
0 table2

и так далее... в итоге при добавлении в таблицу1 или 2 строки Next_id меняется на 1, 2... 100 и так далее...
или же все равно такой подход чреват проблемами?
Верю у Вас опыта по-более чем у меня но НЕУЖТО при работе 2-5-10-20 юзеров одновременно возможен вариант, когда 3-е ОДНОВРЕМЕННО сделали Next_id = 5....???
...
Рейтинг: 0 / 0
Вставка в таблицу записи с уникальным id
    #32365296
акм
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Запростец! Еще как бывает.
Поэтому делать надо все по правилам. Как учит нас товарищ bdv9 или NNN.

Cам делаю так как bdv9.
...
Рейтинг: 0 / 0
Вставка в таблицу записи с уникальным id
    #32365360
Sergey Ch
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Ну мы тут и накрутили

На практике я просто использую таблицу с последним номером.
Далее пишу цикл, в котором пытаюсь ее открыть (EXCLUSIVE) Если не открывает, то выдается сообщение на 1 секунду - что пытается получить новый номер документа. И так 100 раз. После удачной попытки - это спец -таблица благополучно молча закрывается, а мы продолжаем работать...

Может быть не так красиво и без обработки ошибок, но в сети при 30 пользователях работает вполне корректно... Но у каждого свой собственный путь и каждый должен в своей жизни написать подобный метод :)
...
Рейтинг: 0 / 0
Вставка в таблицу записи с уникальным id
    #32366551
Фотография AngelOKES
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Сколько тут уже написали ужас прям.

Есть ещё вариант, уж если вы все налегаете на сетевую версию, то почему бы
в качестве сервера не использовать SQL Server. Там все это продумано до мелочей.

Если таблица для индивидуального пользования, то max(id)+1 - самый легкий
вариант и ошибок здесь никаких быть не может.
...
Рейтинг: 0 / 0
Вставка в таблицу записи с уникальным id
    #32367069
XAndy1
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
> Если таблица для индивидуального пользования, то max(id)+1 - самый легкий
вариант и ошибок здесь никаких быть не может.

Не совсем. Даже совсем не так, если таблица будет более менее приличного размера :)
Если уж таблица для индивидуального пользования, быстрее и проще всего старое доброе go (reccount()), затем id = id + 1

Ну а для многопользовательской среды, как уже отмечали выше, вариантов нет, только отдельная таблица идентификаторов (ключей) с блокировкой записей перед приращением идентификатора. Хотя и тут бывают глюки при большой нагрузке
...
Рейтинг: 0 / 0
Вставка в таблицу записи с уникальным id
    #32367254
Фотография ВладимирМ
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
NNN

Можно я за Игоря напоследок покритикую :)). Сомневаюсь я, что после нового года у меня будет время сюда заглядывать.

Собственно по коду я особых ошибок не вижу (ну разве что стиль написания, но к делу это не относится). Меня смущает сам подход.

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

Как следствие, это налагает определенный стиль написания процедуры вставки, что не есть хорошо. Например, при таком подходе организовать APPEND FROM невозможно. Придется писать циклы.

Т.е. просто взять команду FoxPro и использовать ее уже не получится. Надо будет учитывать, а как эта команда будет увязана с принятой идеологией создания новых записей.

Использование же служебной таблицы и вызов функции из Default-поля не налагает никаких ограницений на использование всех прочих команд FoxPro.

XAndy1
Если уж таблица для индивидуального пользования, быстрее и проще всего старое доброе go (reccount()), затем id = id + 1

Далеко не факт, что последняя запись таблицы (GO (Reccount())) будет содержать и максимальное значение ключа записи. Это зависит от идеологии программы. Более надежным в этом случай будет все-таки работа с индексом (SET DELETED OFF + SET ORDER ... DESC + GO TOP) или определение MAX()+1

FM32YO aka KID
"Если неприятность может случится, то она обязательно случится" (с)

Если ты разрабатываешь процедуру для многопользовательского приложения (пусть даже для 2 клиентов), то почти наверняка возникнет ситуация, когда они одновременно обратятся к одной записи. Или в твоем случае влезут между SELECT и UPDATE

Простейшая модификация твоего кода - это заменить SELECT на LOCATE и сразу после нее сделать попытку блокировки (RLOCK()). Соответсвенно заменить UPDATE на REPLACE (хотя это уже не обязательно).

Ну, вот мой код для критики :)
Здесь используется служебная таблица TabLists из базы данных Leasing следующей структуры

TableName С(10) - имя таблицы (файла DBF) большими буквами
LastID I - последнее использованное значение ключевого поля этой таблицы

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
*************************************************************
*формирование нового кода записи в указанном или в текущем alias
*************************************************************
*Параметры
*tcAlias - имя таблицы без расширения для которой следует сформировать новый ID
*************************************************************
*Возвращаемое значение
*В случае успешного формирования, возвращается сформированный ID
*В случае неудачи, возвращается значение NULL
*************************************************************
*Замечание
*Рекомендуется установить для ключевых полей таблиц признак NOT NULL, тогда
*в случае неудачного формирования ID возникнет сообщение об ошибке при попытке
*записать это ID в ключевое поле таблицы
*************************************************************
FUNCTION NewID(tcAlias)
  LOCAL lcAlias
  IF TYPE( "tcAlias" )<> "C"  OR EMPTY(m.tcAlias)=.T.
    lcAlias = UPPER(ALIAS())
  ELSE
    lcAlias = UPPER(AllTrim(m.tcAlias))
  ENDIF

  LOCAL lnNewID
  lnNewID=NULL
  
  * Некоторые обязательные предварительные настройки среды
  LOCAL lcReprocess, lnArea, lcError, llIsUsedTabLists
  lnArea = SELECT()
  lcReprocess = SET('REPROCESS')
  SET REPROCESS TO  3 
  lcError=ON( "ERROR" )
  ON ERROR *	&& подавляю выдачу сообщений об ошибках
  llIsUsedTabLists=USED( "TabLists" )
  
  * Если служебная таблица не открыта, то открываю ее
  IF m.llIsUsedTabLists=.F.
    USE Leasing!TabLists IN  0 
  ENDIF
  
  * Дополняю имя таблицы пробелами справа до длины поля TabLists.TableName
  * чтобы исключить поиск по первым символам
  IF USED( "TabLists" )=.T. AND LEN(m.tcAlias)<LEN(TabLists.TableName)
  	m.tcAlias=PADR(m.tcAlias,LEN(TabLists.TableName))
  ENDIF

  * Вариант, когда указанный алиас не найден в служебной таблице
  * рассматривается как ошибка и новый номер ID не присваивается
  * Проверка на блокировку и собственно блокировка найденной записи 
  * предотвращают ряд специфических ошибок связанных с работой 
  * невизуальных ActiveX компонент
  IF USED( "TabLists" )=.T. AND SEEK(m.lcAlias, "TabLists" , "TableName" )=.T. AND ;
  		ISRLOCKED(RECNO( "TabLists" ), "TabLists" )=.F. AND RLOCK( "TabLists" )=.T.
    SELECT TabLists
    REPLACE TabLists.LastID WITH TabLists.LastID+ 1 
	IF CursorGetProp('buffering','TabLists')> 1  AND TableUpdate(.F.,.T.,'TabLists')=.F.
	  * Если на служебную таблицу была установлена буферизация и сброс буфера
	  * не удался, то делаю откат буфера, причем результат этого отката уже
	  * не имеет значения
      TableRevert(.F.,'TabLists')
    ELSE
	  lnNewID=TabLists.LastID
	ENDIF
	UNLOCK RECORD RECNO( "TabLists" ) IN TabLists
  ENDIF
  
  * Восстанавливаю исходные настройки среды
  IF m.llIsUsedTabLists=.F.
  	USE IN TabLists
  ENDIF
  SELECT (m.lnArea)
  SET REPROCESS TO m.lcReprocess
  ON ERROR &lcError
  
  RETURN m.lnNewID
ENDFUNC
*************************************************************
*Завершение формирования нового кода записи в указанном или в текущем alias
*************************************************************
...
Рейтинг: 0 / 0
Вставка в таблицу записи с уникальным id
    #32367344
Фотография FM32YO aka KID
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Есть вопрос!!!
использую Ваш код в ХП.. на дефолт навесил функцию NewID - пока добавляю записи в режиме BROWSE все идет на УРА!!!
А теперь... табличка из 2-х полей
ID и name
ворма в которой добавление записи делается так:
Insert into MyTable (name) VALUES ("bla bla bla")
итог - сообщение - Record is not locked
и все...
вот не пойму - я торможу или все же код?
Еще раз повторюсь в режиме БРОУЗ в таблицу строки добавляются на ура и ИД номер аботает!
...
Рейтинг: 0 / 0
Вставка в таблицу записи с уникальным id
    #32367379
Фотография NNN
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
2ВладимирМ

Спасибо за критику :)
С Наступающим! И надеюсь, что после нового года у меня будет время сюда заглядывать - либо шутка, либо временное явление :)
...
Рейтинг: 0 / 0
Вставка в таблицу записи с уникальным id
    #32367410
Фотография ВладимирМ
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
FM32YO aka KID

Это вопрос по моему коду?

Посмотри в отладчике, где именно происходит ошибка № 130 ("Record is not locked"). Сомневаюсь я, что в служебной таблице (т.е. в моем коде), поскольку у меня нет прямых команды REPLACE предварительно не обрамленных RLOCK().

Вероятно все-таки в той таблице, куда ты пытаешься сделать вставку (MyTable). А это может быть, если заголовок таблицы заблокирован другим процессом (при вставке автоматически блокируется заголовок таблицы, т.е. запись с номером 0).

Для проверки выйди из FoxPro, снова войди, и дай команду INSERT, когда еще ничего не успел сделать

NNN

Мне тут придется разбираться с Axapt-ой, так что на пару месяцев я точно из FoxPro выпаду. Ну а по весне станет ясно насколько это затянется.
...
Рейтинг: 0 / 0
Вставка в таблицу записи с уникальным id
    #32367971
Фотография FM32YO aka KID
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
2 ВладимирМ

Это я проверил в ПЕРВУЮ очередь!!!
Ошибка происходит именно в строке
INSERT INTO MyTable....
и НИЧЕМ таблица не может быть занята.. так как тестирую на простенькой форма, в окружении которой только ДВЕ таблицы - та, в которую вставляю запись и та, в которой для 1-й хранится последнеее значение ИД-номера....
так что если у ВАС это работает = чего-то я недоглядел.. вот пока не знаю чего именно....
...
Рейтинг: 0 / 0
Вставка в таблицу записи с уникальным id
    #32367973
Фотография FM32YO aka KID
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
прикольно сделал как Вы сказали - вышел - зашел и в коммандном окне
INSERT INTO statusb (opis) VALUES ("t1")
запись прошла....
неужто причина в том, что в форме у меня листбокс, RowSource для которого есть statusb_q.opis, где statusb_q есть

SELECT * FROM statusb WHERE statusb.del_no = 1;
INTO CURSOR statusb_q ORDER BY opis
(код лежит в активэйте формы.....)
...
Рейтинг: 0 / 0
Вставка в таблицу записи с уникальным id
    #32369024
Фотография sparrow
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Модератор форума
А никто не пробовал написать простой COM сервер? И только у него получать всякие иды, мне кажется это самое надежное решение.
...
Рейтинг: 0 / 0
Вставка в таблицу записи с уникальным id
    #32370148
Фотография FM32YO aka KID
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
вернулся снова к собственному коду - свое самое родное...
принимаю во внимание Ваши замечания по поводу того, что при работе нескольких юзеров возможно дублирование уникального номера..
ВОПРОС!!!
А если я буду делать вставку новой записи так:
Begin Transaction
INSERT INTO......
END TRANSACTION

по идее, пока один юзер делает добавление записи - другой не сможет...
Может кто поделится соображениями ?
И, какое сообщение по идее должен получить 2-й юзер, если 1-й еще не завершил транзакцию?
Если сообщение системное - как его заменить на свое - Русское?
...
Рейтинг: 0 / 0
Вставка в таблицу записи с уникальным id
    #32370172
bdv9
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Думаю транзакция здесь не поможет.
Насколько я знаю транзакция действует только для данного пользователя, а другой может делать что угодно.
Советую все-таки перейти на создание уникального идентификатора для таблицы, используя дополнительный файл - это проверенная технология: работает давно и без сбоев.
...
Рейтинг: 0 / 0
Вставка в таблицу записи с уникальным id
    #32370217
Фотография FM32YO aka KID
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Да мой метод и использует дополнительный файл-таблица с полями
Next_id N(10), TableName C(10)
то есть для каждой таблицы в БД есть строка в этой дополнительной таблице, и данные оттуда берутся

FUNCTION NewId
PARAMETERS cTableName
select ids.next_id AS Next_n FROM ids ;
WHERE ALLTRIM(UPPER(ids.table_name)) = ALLTRIM(UPPER(cTablename));
INTO CURSOR nexter
nNewId = nexter.Next_n + 1
UPDATE ids SET next_id = nNewId WHERE ALLTRIM(UPPER(ids.table_name)) = ALLTRIM(UPPER(cTablename))
RETURN nNewId
*ENDFUNC

Но Гуру данного вфорума указали на возможность 2-х юзеров ОДНОВРЕМЕННО вызвать ХП NewId....
Или Ва имели в виду нечто иное?
Дайте пример работающего кода если можно, а то все, что тут перечислялось так или иначе НЕ всегда работает...
...
Рейтинг: 0 / 0
Вставка в таблицу записи с уникальным id
    #32370245
bdv9
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Собственно пример рабочего кода я уже приводил в этом топике.
И у меня он работает везде, давно и без сбоев.
В некоторых проектах (правда в тестовых) применял его в связке ХП+Default value.
...
Рейтинг: 0 / 0
Вставка в таблицу записи с уникальным id
    #32371097
Фотография FM32YO aka KID
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
попробовал так:
Форма1 и форма2
абсолютно одинаковые только в форма1 на СОХРАНИТЬ
Begin Trans
Insert...
.
.
Wait window "1111"
END Trans
thisform.release()
В Форма2 на сохранить
Begin Trans
Insert...
.
.

END Trans
в итоге обе формs висят на єкране и "ждут " пока с 1-й не погашу Wait window
Иными словами утверждение о том, что каждая транзакция касается только того пользователя, который ее начал - неверно...
вот только посоветуйте КАК сделать при этом в форме2 системное сообщение "Подождите... ля ля ля..."
...
Рейтинг: 0 / 0
Вставка в таблицу записи с уникальным id
    #32371158
bdv9
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Сегодня проверил.
Действительно ты прав.
Может можно и так.

<вот только посоветуйте КАК сделать при этом в форме2 системное сообщение "Подождите... ля ля ля..."
Не знаю, но попробуй пойти не через транзакции, а через RLOCK()

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
set repr to  5 

sele table
go <record>

i =  0 
lnKol =  100    && Кол-во попыток можно задать
llOk = .F.
do while Not llOk And lnKol >  0 
if not rlock()
i = i +  1 
lnKol = lnKol -  1 
wait wind nowa [Подождите файл временно заблокирован. Попытка№ ] + AllTrim(Str(i))
else
llOk = .T.
<Твои действия>
unlock
endif
enddo

If Not llOk
   Wait Wind NoWa [Сообщение о неудачной попытке]
Else
   <Твои действия>
EndIf
...
Рейтинг: 0 / 0
Вставка в таблицу записи с уникальным id
    #32371201
Фотография FM32YO aka KID
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
огромное спасибо!
Дома опробую...
хотя... заранее знаю, что проджект мой будет юзать только 1 юзер....
просто на будущее интересуюсь....
...
Рейтинг: 0 / 0
Вставка в таблицу записи с уникальным id
    #32371266
bdv9
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Это правильно.
Понравилась фраза с foxclub: "Стели солому направо и налево" .
...
Рейтинг: 0 / 0
Вставка в таблицу записи с уникальным id
    #32371340
Фотография FM32YO aka KID
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
LOCAL t1, t2, t3, t4, Co1, Co2

t1 = ALLTRIM(UPPER(thisform.t1.value))
t2 = ALLTRIM(UPPER(thisform.t2.value))
t3 = thisform.t3.value
t4 = thisform.t4.value
Co1 = thisform.Co1.value
Co2 = thisform.Co2.value

private lError
lError = .f.
SET REPROCESS TO automatic &&20

begin transaction
on error lError = .T.

INSERT INTO Tovar (name, code, price, idgroup, idunits, Quantity, del_no) ;
VALUES (t1, t2, t3, Groups_q.id, Units_q.id, t4, 1)

wait window " 1111 "

on error
if lError
ROLLBACK
=messagebox("Повторите еще раз")
else

END TRANSACTION
endif
thisform.release()
*******************************
такой Вариант порекомендовал Сергей Титов...
все четко работает НО
если 1-й юзер "держит" транзакцию...
посредством wait window " 1111 " в моем примере...
(сам понимаешь в реальной задаче wait window " 1111 "
не будет присутствовать.. я его добавил исключительно в целях
эксперимента... как же мне иначе симулировать "занятость" записи..
если я тест провожу один..)
2-й юзер при таком раскладе - SET REPROCESS TO automatic
получает "видимость" того, что форма попросту "зависла"
лично я никак на глаз не мог определить... то ли винда висит.. то ли
прога...
ну иллюзия зависания полная.... вот интересно КАК бы это обойти????
Если поставить SET REPROCESS TO 1
то есть одна и только одна попытка
то 2-й юзер естественно сразу получит в лоб сообщением
"Повторите еще раз" и форма закроется....

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

разве что НЕ закрівать форму в случае неудачного доступа к залоченной таблице...
...
Рейтинг: 0 / 0
Вставка в таблицу записи с уникальным id
    #32374978
Фотография ВладимирМ
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Очень кратко, пока не началось... :)

Ошибка №130 (также, как и 108 и 109) связана с конфликтом совместного доступа. Наиболее вероятная причина - это некорректное использование PrivateDataSession.

Помню, когда-то давно у меня такие ошибки были. Но как не пытался, но добиться того же эффекта с моим NewID() у меня не получилось.

Транзакция.

Необходимо стремиться к тому, чтобы время открытой транзакции было сведено к минимуму. Дело в том, что открытая транзакция блокирует все области, которые были изменены пользователем до завершения транзакции. А это много чего. Так сразу все и не перечислешь. Например, может быть заблокирован ВЕСЬ структурный индексный файл.

Общая логика модификации и записи данных примерно следующая:

-) Накладывается буферизация на таблицы источники
-) Выполняется модификация в буферах
-) А вот сам процесс сброса данных из буфера в исходные таблицы обрамляется транзакцией.

При такое идеологии в случае отката транзакции (по разным причинам) система возвращается в то состояние, в котором она была до момента открытия транзакции. Т.е. к моменту уже заполненного, но еще не сброшенного буфера. Соответсвенно пользователь может повторить попытку сброса буфера без повторного ввода данных.

Ну что-то вроде:

=CURSORSETPROP('buffering',3,'MyTable')
INSERT INTO MyTable (Field1) VALUES (1)

BEGIN TRANSACTION
IF TableUpdate(.T.,.T.,'MyTable')
END TRANSACTION
ThisForm.Release()
ELSE
ROOLBACK
MessageBox('Сохранить не удалось. Повторите попытку')
ENDIF
...
Рейтинг: 0 / 0
Вставка в таблицу записи с уникальным id
    #32392948
This is the function I use to generate the Integer type Primary Keys for my tables.Requires a NextKey.dbf table. Enjoy:

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
LPARAMETERS tcTable
** tcTable - Name of the table where a new record is added

LOCAL lnCurrSelect, ;
   lcTableName, ;
   llUsed, ;
   lnCurrReprocess, ;
   luKey, ;
   lcKey, ;
   lcField, ;
   lcOldTalk, ;
   liOldidKey, ;
   lnDay, ;
   lnHour

lcOldTalk=SYS( 103 )
SET TALK OFF

lcAsserts=SET( "ASSERTS" )

SET ASSERTS ON

ASSERT TYPE( "tcTable" ) =  "C"  ;
   AND ! EMPTY(tcTable) MESSAGE PROGRAM()+ ": Parameter must be character type and not empty..." 

* Save the current work area, open the NEXTID table
* (if necessary), and find the row with the desired table name. If it
* doesn't exist, create a record for it.

lnCurrSelect = SELECT()
llUsed       = USED('NextKey')
lcTableName  = UPPER(ALLTRIM(tcTable))
IF llUsed
   SELECT "NextKey"
   SET ORDER TO "cTableName"
ELSE
   SELECT 0
   USE NextKey ORDER cTableName AGAIN SHARED
ENDIF llUsed
SEEK lcTableName
IF NOT FOUND()
   LOCATE
   CALCULATE MAX(NextKey.iidkey) TO liOldidKey
   INSERT INTO NextKey (iidkey, iNextKey, cTableName, iIncrement) ;
      VALUES (liOldidKey+1, 0,lcTableName, 1)

ENDIF NOT FOUND()

* Increment the next available ID.

lnCurrReprocess = SET('REPROCESS')
SET REPROCESS TO AUTOMATIC

IF RLOCK()
 
   REPLACE iNextKey WITH iNextKey + iIncrement
 		   
   luKey = iNextKey
   UNLOCK
ENDIF RLOCK()


* Cleanup and return.

SET REPROCESS TO lnCurrReprocess
IF NOT llUsed
   USE
ENDIF NOT llUsed

SELECT (lnCurrSelect)
SET TALK &lcOldTalk
SET ASSERTS &lcAsserts

RETURN luKey
...
Рейтинг: 0 / 0
Вставка в таблицу записи с уникальным id
    #32393296
Por
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Por
Гость
2 sparrow

Я делал COM для раздачи "инвентарных" номеров (реестровых). Но там проблема несколько отличалась. Дело в том, что требуемый номер должен был быть уникальным в пределах базы данных, а не только одной таблицы, как в случае с PK.
...
Рейтинг: 0 / 0
Вставка в таблицу записи с уникальным id
    #32393846
Фотография ВладимирМ
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Rosty Vygovsky, Lynbrook, NY
Видимо Вам тяжело читать по русски, но Ваш код повторяет целый ряд типичных ошибок. Он, конечно, работоспособный, но при очень определенных условиях.

Посмотрите мой код страницей раньше. Там в комментариях я написал почему и зачем делал кое-какие дополнительные команды, которых нет в Вашем коде.
...
Рейтинг: 0 / 0
Вставка в таблицу записи с уникальным id
    #32395659
dasistgut
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Господа, объясните мне, как работает foxpro в случае, когда индексы беруться из специальной таблицы, как предлагает, например, Igor Korolyov.
Насколько я понимаю, алгоритм следующий:
1. Сеанс пользователя НЕ в режиме транзакции.
2. Сеанс выполняет оператор Insert Into...
3. СУБД открывает транзакцию.
4. Срабатывает триггер Insert Row, который берёт из специальной таблицы значение ключа. Значение ключа в специальной таблице увеличивается на 1.
5. Полученное значение ключа записывается туда, куда надо.
6. Транзакция закрывается.

Далее:
Если в этот же момент другой пользователь выполняет те же действия с небольшим запозданием, т.е. если его п.4 приходится на п.5 первого пользователя, он получит то же значение ключа, что и первый. В результате возникнет та же коллизия дублирования ключей.
Или foxpro работает как-то по-другому?
Спасибо.
...
Рейтинг: 0 / 0
Вставка в таблицу записи с уникальным id
    #32395832
andrew_Pr
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
to dasistgut
п.4 второго юзера никогда не пересечется с п.5 первого,
потому что, когда второй юзер дойдет до п.3, его программа
"зависнет" и будет ждать, пока первый юзер не закончит п.6
Или в других БД транзакции работают как-то по-другому? ;)
...
Рейтинг: 0 / 0
Вставка в таблицу записи с уникальным id
    #32395843
Фотография ВладимирМ
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
dasistgut
Все несколько не так. Значение ключа генерится не триггером, а несколько раньше, при генерации Default значения поля. Хотя для разбора полетов это не столь принципаильно.

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

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

Т.е. другой пользователь не сможет сгенерить ключ, пока первый не разберется с транзакцией.
...
Рейтинг: 0 / 0
Вставка в таблицу записи с уникальным id
    #32396010
dasistgut
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
andrew_Pr
Похоже, что в других БД транзакции действительно работают по-другому.
Делаем простой проект: Delphi+IB. На любую таблицу вешаем примерно такой триггер:
CREATE TRIGGER SET_EMP_NO FOR EMPLOYEE
ACTIVE BEFORE INSERT POSITION 0
AS
declare variable i integer;
BEGIN
i = 1;
if (new.first_name = '1') then
While (i>0) do begin
i = i + 1;
end
END
т.е. если поле first_name инициировано значением '1', то триггер зациклится.
В программе одна форма, на которой DBGrid, с этой таблицей.
Запускаем две копии программы. В первой вставляем запись с first_name = '1' и делаем Post. Прога "виснет", как и следовало ожидать.
Во второй копии вставляем запись с first_name <> '1'. Запись вставляется и эта (вторая) прога продолжает работать нормально.
Т.е. возвращаясь к моему вопросу, юзер на п.3 (по крайней мере в IB, Oracle) не зависнет. Но учитывая объяснение Владимира М становится всё понятно.
Спасибо.
...
Рейтинг: 0 / 0
Вставка в таблицу записи с уникальным id
    #32506998
lesha_spb
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Очень полезный топик, хотя и несколько разросшийся:)
Но по моему надо упоминуть и про такую вещь, как проверка неиспользованных ключей. вот что я имею ввиду: У меня, как и у большинства(я думаю), есть таблица с номером(integer) последнего ключа, так вот рано или поздно его значение станет огромным и при этом многие из уже использованных ключей освободились(например соответсвующая запись удалена). Тогда можно спокойно, например один раз в несколько месяцев, запускать процедуру проверки ключей, которая будет все положительные целые числа меньшие значения в таблице загонять во временную таблицу. Т.о. получаем дополнительные ключи, дальше можно например брать ключи из этой временной таблицы пока она не пустая. Если я в чем не прав поправьте меня.
...
Рейтинг: 0 / 0
Вставка в таблицу записи с уникальным id
    #32507005
lesha_spb
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
>Тогда можно спокойно, например один раз в несколько месяцев, запускать >процедуру проверки ключей, которая будет все положительные целые числа >меньшие значения в таблице загонять во временную таблицу.
Естественно имелись ввиду те значения которые ни где не используются.
Это будет долгая процедура, не спорю.
...
Рейтинг: 0 / 0
Вставка в таблицу записи с уникальным id
    #32507154
Фотография ВладимирМ
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Что значит "станет огромным"?

Предельно допустимое количество записей в DBF-таблице - это 1 billion (единица и девять нулей)

В типе Integer как правило используются только положительные значения, т.е. используется диапазон от 0 до 2,147,483,647

Значит, не боясь переполнения, можно совершенно спокойно удалить 2,147,483,647 - 1,000,000,000 = 1,147,483,647 записей

Если каждый день без перерывов на выходные и праздники удалять по 10,000 записей, то для переполнения нам понадобиться:

1,147,483,647/(365*10,000) = 314 лет

Ваша программа столько проживет?

Так что, не обращайте внимание на "дыры" в нумерации. Не стоящее это занятие.
...
Рейтинг: 0 / 0
46 сообщений из 46, показаны все 2 страниц
Форумы / FoxPro, Visual FoxPro [игнор отключен] [закрыт для гостей] / Вставка в таблицу записи с уникальным id
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]