| 
			
			 | 
		#1 | 
| 
			
			 Участник 
		
			
	 | 
	
	
	
		
		
			
			
			D365, SysDatabaseLog, поле NewData
			 
			
			В ах2009 и ах2012 детали каждой записи лога хранятся в контейнере в поле SysDatabaseLog.Data. 
		
		
		
		
		
		
			Например, мы настроили логирование изменений в таблице CustTable, обновили в записи таблицы CustTable какое-то поле. Тогда в SysDatabaseLog появится запись, у которой в поле Data будут храниться значения измененных полей. Помимо изменённого нами поля там будут ещё поля типа modifiedDateTime, modifiedBy, recVersion. В D365 в SysDatabaseLog для хранения деталей лога используется новое поле NewData. Это строковое поле типа Memo. Заполняется оно с помощью триггеров на уровне БД. У этих триггеров вскрылись следующие неприятные моменты: 1. Если мы хотим логировать update только отдельных полей в CustTable и думаем что в SysDatabaseLog будут появляться записи только при изменении этих полей, то мы ошибаемся. Записи будут появляться при изменении любого поля. 2. В поле NewData сохраняется значение всех полей, на которые мы настроили ведение логов. Даже если эти поля не изменялись - всё равно туда записывается значение этих полей. Два раза, типа старое и новое. Что ожидаемо ведёт к чрезмерному росту таблицы SysDatabaseLog . 3. Значения полей типа utcdatetime сохраняются в newdata без секунд. Может конечно у нас каких-то настроек не хватает, чтоб секунды сохранялись. Вот такая подлянка от микрософта. 
				__________________ 
		
		
		
		
	Дмитрий  | 
| 
	
 | 
|
| За это сообщение автора поблагодарили: trud (2), sukhanchik (4). | |
| 
			
			 | 
		#2 | 
| 
			
			 Участник 
		
			
	 | 
	
	
	
		
		
		
		 Цитата: 
	
Есть какие-то случаи, когда запись в журнале базы данных создается даже тогда изменились поля, которые не настроены для отслеживания. До Ax3.0 включительно настройка по полям работала. Начиная с обновления Ax3.0, в котором появилось поле RecVersion этот механизм перестал учитывать настройки полей. Возможно это из-за RecVersion, возможно что-то там переделали, но факт на лицо. Причем, игнорирование настроек изменений по полям отслеживается не на всех таблицах (какая закономерность тут работает так и не понял). Мы, в свое время, наткнулись на это еще в DAX4 при настройке журнала базы данных по InventTableModule.  | 
| 
	
 | 
|
| За это сообщение автора поблагодарили: sukhanchik (4). | |
| 
			
			 | 
		#3 | 
| 
			
			 Участник 
		
			
	 | 
	
	
	
		
		
		
		 
			
			Если посмотреть на код триггера  
		
		
		
		
		
		
		
	X++: AND ( UPDATE(KnownAs) OR UPDATE(Name) OR UPDATE(NV_ParentCompany) ) X++: IF (@spLogType = 3 AND ( UPDATE(KnownAs) OR UPDATE(Name) OR UPDATE(NV_ParentCompany) ) ) --Update BEGIN INSERT INTO SYSDATABASELOG (NEWDATA, LOGRECID, LOGTYPE, TABLE_, DESCRIPTION, USERNAME, CREATEDBY, CREATEDTRANSACTIONID, DATAAREAID, PARTITION, SEQUENCENUMBER) SELECT '03::' + + 'KnownAs' + ', ÿþ ' + I.KNOWNAS + ' ÿþ ' + D.KNOWNAS + ' ÿþ ' + 'Name' + ', ÿþ ' + I.NAME + ' ÿþ ' + D.NAME + ' ÿþ ' + 'NV_ParentCompany' + ', ÿþ ' + CONVERT(nvarchar(max), I.NV_PARENTCOMPANY) + ' ÿþ ' + CONVERT(nvarchar(max), D.NV_PARENTCOMPANY) + ' ÿþ ' + 'CreatedBy' + ', ÿþ ' + I.CREATEDBY + ' ÿþ ' + D.CREATEDBY + ' ÿþ ' + 'CreatedDateTime' + ', ÿþ ' + CONVERT(nvarchar(max), I.CREATEDDATETIME) + ' ÿþ ' + CONVERT(nvarchar(max), D.CREATEDDATETIME) + ' ÿþ ' + 'ModifiedBy' + ', ÿþ ' + I.MODIFIEDBY + ' ÿþ ' + D.MODIFIEDBY + ' ÿþ ' + 'ModifiedDateTime' + ', ÿþ ' + CONVERT(nvarchar(max), I.MODIFIEDDATETIME) + ' ÿþ ' + CONVERT(nvarchar(max), D.MODIFIEDDATETIME) + ' ÿþ ' + 'Partition' + ', ÿþ ' + CONVERT(nvarchar(max), I.PARTITION) + ' ÿþ ' + CONVERT(nvarchar(max), D.PARTITION) + ' ÿþ ' + 'RecId' + ', ÿþ ' + CONVERT(nvarchar(max), I.RECID) + ' ÿþ ' + CONVERT(nvarchar(max), D.RECID) + ' ÿþ ' + 'RecVersion' + ', ÿþ ' + CONVERT(nvarchar(max), I.RECVERSION) + ' ÿþ ' + CONVERT(nvarchar(max), D.RECVERSION) + ' ÿþ ' , I.RECID, 2, 1268,I.PARTYNUMBER + ',' + I.NAME,@userId, @userId, 0, 'dat', I.PARTITION, @seqNo FROM INSERTED I LEFT OUTER JOIN DELETED D ON I.RECID = D.RECID; END; Я в свое время словил в этом триггере ошибку, когда строка логирования обрезалась 1000-ю символами, предложил вариант с добавлением CONVERT(nvarchar(max) ..) - пофиксили оперативно.  | 
| 
	
 | 
|
| За это сообщение автора поблагодарили: sukhanchik (8). | |
| 
			
			 | 
		#4 | 
| 
			
			 Участник 
		
			
	 | 
	
	
	
		
		
		
		 Цитата: 
	
Более детальный анализ показал что ошибка из пункта 1 возникает только в тех таблицах, которые участвуют в наследовании (касается это и "родителей" и "детей"). Конкретно я столкнулся с такой проблемой в таблице DirPerson, которая отнаследована от DirPartyTable. У таблицы DirPerson (фактически в БД у таблицы DIRPARTYTABLE) код в триггере выглядит так : X++: ...
    IF (@spLogType = 3 ) --Update
    BEGIN
        INSERT INTO SYSDATABASELOG (NEWDATA, LOGRECID, LOGTYPE, TABLE_, DESCRIPTION, USERNAME, CREATEDBY, CREATEDTRANSACTIONID, DATAAREAID, PARTITION, SEQUENCENUMBER)  
            SELECT '03::' + 					 + 'BirthDay' + ', ÿþ ' +  CONVERT(nvarchar(max), I.BIRTHDAY) + ' ÿþ ' + CONVERT(nvarchar(max), D.BIRTHDAY) + ' ÿþ '
...X++: ...
    IF (@spLogType = 3 AND ( UPDATE(ConnectorName) OR UPDATE(ConnectorProperties) OR UPDATE(IsTest) ) ) --Update
    BEGIN
        INSERT INTO SYSDATABASELOG (NEWDATA, LOGRECID, LOGTYPE, TABLE_, DESCRIPTION, USERNAME, CREATEDBY, CREATEDTRANSACTIONID, DATAAREAID, PARTITION, SEQUENCENUMBER)  
            SELECT '03::' + 					 + 'ConnectorName' + ', ÿþ ' +  I.CONNECTORNAME + ' ÿþ ' + D.CONNECTORNAME + ' ÿþ '
...
				__________________ 
		
		
		
		
	Дмитрий  | 
| 
	
 | 
|
| За это сообщение автора поблагодарили: Raven Melancholic (5), S.Kuskov (5). | |
| 
			
			 | 
		#5 | 
| 
			
			 Участник 
		
			
	 | 
	
	
	
		
		
		
		 
			
			И опять иерархия наследования таблиц, грустно. 
		
		
		
		
		
		
		
	Я считал, что те проблемы, которые наследование DAX2012 создало в ранее существующих механизмах, это просто проблемы роста. Но вот уже D365 идет по планете, а воз и ныне там. Впечатление, что команда предложившая и реализовавшая наследование таблиц, появилась на проекте, что-то сделала и слиняла в другие проекты.  | 
| 
	
 | 
| 
			
			 | 
		#6 | 
| 
			
			 Участник 
		
			
	 | 
	
	
	
		
		
		
		 Цитата: 
	
Для преобразования полей типа datetime в строку в триггере используется функция CONVERT(nvarchar(max), ...) https://docs.microsoft.com/ru-Ru/sql...l-server-ver15 А в MS SQL Server формат преобразования datetime по умолчанию - mon dd yyyy hh:miAM (or PM) . Если мы хотим секунды - необходимо в функцию CONVERT добавлять параметр style. Например, style 120 - это "yyyy-mm-dd hh:mi:ss (24h)". 
				__________________ 
		
		
		
		
	Дмитрий  | 
| 
	
 | 
| 
			
			 | 
		#7 | 
| 
			
			 Administrator 
		
			
	 | 
	
	
	
		
		
		
		 Цитата: 
	
		
			Сообщение от Raven Melancholic
			 
 
			И опять иерархия наследования таблиц, грустно. 
		
	Я считал, что те проблемы, которые наследование DAX2012 создало в ранее существующих механизмах, это просто проблемы роста. Но вот уже D365 идет по планете, а воз и ныне там. Впечатление, что команда предложившая и реализовавшая наследование таблиц, появилась на проекте, что-то сделала и слиняла в другие проекты. 1. Реализация наследования в AX2012RTM. Когда реально каждая таблица, участвующая в наследовании была реальной таблицей в БД. 2. Реализация наследования в AX2012R2. Когда всю иерархию таблиц слили по структуре данных в одну корневую родительскую таблицу. Т.е. теперь физически с т.з. БД любой запрос к подчиненной таблице в иерархии (в АХ) превращается (в БД) в запрос к корневой родительской таблице с фильтром по полю InstanceRelationType, в котором сидит TableId подчиненной таблицы. При этом в такой реализации использовать наследование таблиц абсолютно бессмысленно - проще сделать одну большую таблицу и уже с ней работать. Как минимум с индексами проблем не будет (в АХ нельзя сделать индекс с участием поля из подчиненной таблицы и родительской таблицы одновременно. А это напрямую влияет на производительность в том случае, когда этот индекс сильно нужен) Ну т.е. (как вариант развития событий) сначала сделали "по уму", затем видимо "не взлетело" по производительности - сделали костыль, "проштрафившуюся" команду уволили и продолжили с этим жить. 
				__________________ 
		
		
		
		
	Возможно сделать все. Вопрос времени  | 
| 
	
 | 
|
| За это сообщение автора поблагодарили: twilight (1). | |
| 
			
			 | 
		#8 | 
| 
			
			 MCTS 
		
			
	 | 
	
	
	
		
		
		
		 
			
			Так в первом варианте реализации даже теоретически нельзя индекс такой сделать. Т. е. получается концепция наследования таблиц в целом нежизнеспособна?
		 
		
		
		
		
		
		
			
				__________________ 
		
		
		
		
	I could tell you, but then I would have to bill you.  | 
| 
	
 | 
| 
			
			 | 
		#9 | 
| 
			
			 Участник 
		
			
	 | 
	
	
	
		
		
		
		 Цитата: 
	
Концепция из пункта 1 могла бы нормально жить. Когда в БД каждая таблица - отдельно. Непонятно почему этот вариант отвергли. 
				__________________ 
		
		
		
		
	Дмитрий  | 
| 
	
 | 
| 
			
			 | 
		#10 | 
| 
			
			 Administrator 
		
			
	 | 
	
	
	
		
		
		
		 Цитата: 
	
Цитата: 
	
Собственно - изменение архитектуры БД, частичная нормализация таблиц и привели к тому, что AX 2012 стала требовать (на демо-виртуалке) минимум 32 Гб, в то время, как для AX 2009 4 Гб хватало за глаза. В D365FO в демо-виртуалке такого "взрывного роста" к требованиям уже не случилось, но надо понимать, что: 1. Структуру БД радикально уже не меняли 2. Убрали клиента, который использовал определенные ресурсы системы. 3. Скорость работы в системе при этом не увеличилась 
				__________________ 
		
		
		
		
	Возможно сделать все. Вопрос времени  | 
| 
	
 | 
| 
			
			 | 
		#11 | 
| 
			
			 Участник 
		
			
	 | 
	
	
	
		
		
		
		 Цитата: 
	
Выяснилось что строковые поля обрезаются до 1000 символов. Пришлось писать SQL-запросы с помощью классов .NET. 
				__________________ 
		
		
		
		
	Дмитрий  | 
| 
	
 | 
| 
			
			 | 
		#12 | 
| 
			
			 Участник 
		
			
	 | 
	
	
	
		
		
		
		 
			
			А в связи с тем, что заполнение переехало на уровень триггеров указание common.skipDataBaseLog(true) теперь работает?
		 
		
		
		
		
		
		
		
	 | 
| 
	
 | 
| 
			
			 | 
		#13 | 
| 
			
			 Участник 
		
			
	 | 
	
	
	
		
		
		
		 Цитата: 
	
Но в триггере есть такой участок кода : X++: SELECT @userId = DBO.SysGetUserIdFromContextInfo(); SELECT @sessionId = DBO.SysGetSessionIdFromContextInfo(); IF EXISTS(SELECT TOP 1 TABLEID FROM SYSSKIPDATABASELOG WHERE USERID = @userId AND SESSIONID = @sessionId AND TABLEID = 13271) BEGIN RETURN; END 
				__________________ 
		
		
		
		
	Дмитрий  | 
| 
	
 | 
| 
			
			 | 
		#14 | 
| 
			
			 Участник 
		
			
	 | 
	
	
	
		
		
		
		 
			
			Обнаружилось ещё одно "новшество" в форме ЖБД в D365. 
		
		
		
		
		
		
			На вкладке "История" теперь нельзя увидеть изменения регистра в строковых полях. Имеются в виду случаи когда вы в поле поменяли значение с "test" на "Test". В форме при отображении эти изменения игнорируются. А в журнал базы данных они попадают. X++: if (fieldId != 0 && (!System.String::IsNullOrEmpty(newValue) || !System.String::IsNullOrEmpty(oldValue))) { // If field was saved as array field, we use the field index to get the field id fieldId = this.getExtendedFieldIdByIdx(useExtendedFieldId, fieldId, SysDatabaseLogDataParser::IsArrayFieldName(fieldNameDb) ? idx : 1); if (newValue != oldValue && (this.LogType != DatabaseLogType::Update || fieldName != 'RecVersion')) { list.addStart([fieldId, newValue, oldValue]); } } Если в этом участке кода переменные newValue и oldValue сравнивать с помощью newValue.Equals(oldValue), то всё нормализуется и мы в форме ЖБД увидим изменения, связанные с регистром текста. 
				__________________ 
		
		
		
		
	Дмитрий  | 
| 
	
 | 
|
| За это сообщение автора поблагодарили: S.Kuskov (2). | |
| 
	
	 | 
	
		
  |