Имя: Пароль:
1C
 
Самоотчет об ошибке "..terminated because a duplicate key was found for index..."
0 Serg_1960
 
24.09.08
12:45
Кратко: в SQL-базе ТиИ (реиндексация) завершается по ошибке. По тексту ошибки ясно "в чем" дело, - но не понятно "где"

Попытка вставки неуникального значения в уникальный индекс:
Microsoft OLE DB Provider for SQL Server: CREATE UNIQUE INDEX terminated because a duplicate key was found for index ID 1. Most significant primary key is 'type ad, len 16'.
HRESULT=80040E2F, SQLSrvr: Error state=1, Severity=10, native=1505, line=1

Понятно что SQL "отвергает" попытку вставки неуникального значения в уникальный индекс. Не понятно только "где" искать проблему. В каком индексе, и главное, - в какой таблице...
1 Serg_1960
 
24.09.08
12:58
Первое что пришло в голову: выгрузить базу, загрузить в файловую версию и там проверить. Так и сделал... ТиИ (по полной программе) прошло без ошибок. Довольный, выгрузил базу из файловой версии и "попытался" загрузить в SQL-версию... и "пролетел, как фанера над Парижем". Загрузка выдала ошибку:
Ошибка загрузки информационной базы. В информационную базу загружены не все данные
по причине:
Попытка вставки неуникального значения в уникальный индекс:... далее см. текст выше...

Итак, что имеем:
Есть РИБ-база на SQL (с ошибками) и файловая база (якобы без ошибок). Все данные вроде-бы "на месте" (проверил "визуально" с помощью формирования на пробу отчетов и повторения различных расчетов)

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

Решил искать ошибку не там где она есть (в рабочей) а там "где светло" - в файловой копии. Ведь "проблема" сидит там и "передается" в SQL-базу...
2 Serg_1960
 
24.09.08
12:59
Сообщить("Справочники: контроль на уникальность кода записи");
   Для Каждого Объект ИЗ Метаданные.Справочники Цикл
       Если Объект.КонтрольУникальности > 0 Тогда
           Имя = Объект.Имя;
           Запрос = Новый Запрос;
           Запрос.Текст = "ВЫБРАТЬ
                          |    "+Имя+".Код КАК Код,
                          |    КОЛИЧЕСТВО("+Имя+".Ссылка) КАК Количество
                          |ИЗ
                          |    Справочник."+Имя+" КАК "+Имя+"
                          |
                          |СГРУППИРОВАТЬ ПО
                          |    "+Имя+".Код
                          |
                          |УПОРЯДОЧИТЬ ПО
                          |    Количество УБЫВ";
           Результат = Запрос.Выполнить();
           Если Не Результат.Пустой() Тогда
               Выборка = Результат.Выбрать();
               Пока Выборка.Следующий() И Выборка.Количество > 1 Цикл
                   Сообщить("Справочник(код) "+Объект.Синоним + "(" + Выборка.Код + ")");
               КонецЦикла;
           КонецЕсли;
       КонецЕсли;
   КонецЦикла;
3 Immortal
 
24.09.08
13:02
При ТИИ пишутся имена таблиц, разве не так?
4 mikecool
 
24.09.08
13:04
(3) пишется..
у меня такая хрень была с итогами при загрузке базы в скуль, решением служило - рассчитать итоги на начало времен, загрузить, рассчитать итоги на тек период.
причем - база выгрузилась с постгри и загружалась в мс скуль
5 Serg_1960
 
24.09.08
13:06
(3) Так-то оно так, только попробуй успей прочитать. Идет ТиИ - бегут строки. Потом следует большая пауза (уже без сообщений в статусной строке)... и "вылетает" ошибка не понятно уже "к чему" она
6 Serg_1960
 
24.09.08
13:09
С Вашего разрешения, я продолжу...
Проверил коды справочников, а потом только "вспомнил" - они здесь "не причем" наличие одинаковых кодов справочников SQL-базе "не помеха"...

Решил "проверяться" дальше...
   
Сообщить("Документы: контроль на уникальность номера");
   
   ПериодГод = Метаданные.СвойстваОбъектов.ПериодичностьНомераДокумента.Год;
   ПериодКвартал = Метаданные.СвойстваОбъектов.ПериодичностьНомераДокумента.Квартал;
   ПериодМесяц = Метаданные.СвойстваОбъектов.ПериодичностьНомераДокумента.Месяц;
   ПериодДень = Метаданные.СвойстваОбъектов.ПериодичностьНомераДокумента.День;
   БезПериода = Метаданные.СвойстваОбъектов.ПериодичностьНомераДокумента.Непериодический;
   
   Для Каждого Объект ИЗ Метаданные.Документы Цикл
       Если Объект.КонтрольУникальности > 0 Тогда
           Имя = Объект.Имя;
           Период = Объект.ПериодичностьНомера;
           Если Период = ПериодГод Тогда
               Диапазон = "ГОД";
           ИначеЕсли Период = ПериодКвартал Тогда
               Диапазон = "КВАРТАЛ";
           ИначеЕсли Период = ПериодМесяц Тогда
               Диапазон = "МЕСЯЦ";
           ИначеЕсли Период = ПериодДень Тогда
               Диапазон = "ДЕНЬ";
           Иначе
               Диапазон = "";
           КонецЕсли;
           Запрос = Новый Запрос;
           Запрос.Текст = "ВЫБРАТЬ
                          |    "+Имя+".Номер КАК Номер,
                          |    КОЛИЧЕСТВО("+Имя+".Ссылка) КАК Количество
                          |ИЗ
                          |    Документ."+Имя+" КАК "+Имя+"
                          |
                          |СГРУППИРОВАТЬ ПО
                          |    "+Имя+".Номер"+?(ЗначениеЗаполнено(Диапазон),", НАЧАЛОПЕРИОДА("+Имя+".Дата,"+Диапазон+")", "")+"
                          |
                          |УПОРЯДОЧИТЬ ПО
                          |    Количество УБЫВ";
           Результат = Запрос.Выполнить();
           Если Не Результат.Пустой() Тогда
               Выборка = Результат.Выбрать();
               Пока Выборка.Следующий() И Выборка.Количество > 1 Цикл
                   Сообщить("Документ(номер) "+Объект.Синоним + "(" + Выборка.Номер + ")");
               КонецЦикла;
           КонецЕсли;
       КонецЕсли;
       Запрос = Новый Запрос;
       Запрос.УстановитьПараметр("ДатаС",        НачалоГода(Дата("19800101")));
       Запрос.УстановитьПараметр("ДатаПо",        КонецГода(ТекущаяДата()));
       Запрос.УстановитьПараметр("БезДаты",    Дата("00010101"));
       Запрос.Текст = "
       |ВЫБРАТЬ
       |    "+Имя+".Ссылка КАК Документ
       |ИЗ
       |    Документ."+Имя+" КАК "+Имя+"
       |
       |ГДЕ
       |    (НЕ("+Имя+".Дата МЕЖДУ &ДатаС И &ДатаПо) Или "+Имя+".Дата = &БезДаты)";
       Результат = Запрос.Выполнить();
       Если Не Результат.Пустой() Тогда
           Выборка = Результат.Выбрать();
           Пока Выборка.Следующий() И Выборка.Количество > 1 Цикл
               Сообщить("Документ(дата) "+Объект.Синоним + " : " + Выборка.Документ);
           КонецЦикла;
       КонецЕсли;
       
   КонецЦикла;
7 mikecool
 
24.09.08
13:09
(5) можно поломать голову и написать процедурку по поиску неуникальных значений индексов.. типа пробегать по таблицам, из сис брать имена индексов, и дальше select ... group by ... having count(1)>1
8 mikecool
 
24.09.08
13:10
(6) не парься насчет кодов, поскольку ругаться может на кокой-нить составной индекс
9 Serg_1960
 
24.09.08
13:15
Все правильно. И я остановился и задумался. "Трудно искать черную кошку в темной комнате - особенно когда ее там нет". Надо было проверять SQL-базу, а не файловую версию (платформа эти ошибки не фиксирует и "в упор не видит")...

А c SQL у меня "проблема": последний раз я с ним работал лет двадцать назад :( Пришлось начинать с азов...
10 Serg_1960
 
24.09.08
13:22
Большое спасибо авторам Lock1C.epf, StrBaseSQL.epf и (разумеется) сообществу волшебного форума. В течение недели "заново освоил" SQL :о)

Начал с "классического" вопроса новичков...

   Connection = Новый COMОбъект("ADODB.Connection");
   String = "driver={SQL Server};server=SqlSrvr;uid=sa;pwd=PasWord;Database=Base";
   String = "Provider=SQLOLEDB.1;Password=PasWord;Persist Security Info=True;User ID=sa;Initial Catalog=Base;Data Source=SQLSRVR";
   Connection.ConnectionTimeOut=20;
   Connection.CommandTimeOut=600;
   Connection.CursorLocation=3;
   
   Сообщить("Установить соединение с базой данных.");
   Попытка
       Connection.Open(String);
   Исключение
       Сообщить("Ошибка во время установки соединения с базой данных!");
       Сообщить(ОписаниеОшибки());
       Возврат;
   КонецПопытки;

На всякий случай ДВА варианты строки соединения сейчас указал
11 mikecool
 
24.09.08
13:26
(10) зачем это все писать в восьмерке?
для дб2 не знаю, а вот для мс скуля есть Qwery Analizer, для постгри и мс есть среда(забыл как производитель называется) наподобие PL\SQL
12 Immortal
 
24.09.08
13:32
(11) дежавю
13 Serg_1960
 
24.09.08
13:35
(11) Ситуация "обострилась" тем, что админ ушел в отпуск "закрыв" SQL-сервер на пароль, и свой телефон "забросил" на #цензура#.
Эх! "Знал бы прикуп - жил бы в Сочи"...

Продолжим ?

... в принципе возможны были разные варианты проверки. Но мне они "не помогли":(

   Сообщить("Проверить структуру SQL-базы командой DBCC CHECKDB...");
   Попытка
       Command = Новый COMОбъект("ADODB.Command");
       Command.ActiveConnection=Connection;
       Command.CommandText="DBCC CHECKDB";
       RecordSet = Новый COMОбъект("ADODB.RecordSet");
       RecordSet=Command.Execute();
   Исключение
       Сообщить("Ошибка во время проверки базы данных!");
       Сообщить(ОписаниеОшибки());
   КонецПопытки;


   Сообщить("Переиндексировать SQL-базу");
   Попытка
       Command = Новый COMОбъект("ADODB.Command");
       Command.ActiveConnection=Connection;
       Command.CommandText="SET NOCOUNT ON
                           |DECLARE @TableName char(32)
                           |DECLARE SysCur CURSOR FOR SELECT name FROM sysobjects WHERE type='U'
                           |OPEN SysCur
                           |FETCH NEXT FROM SysCur INTO @TableName
                           |WHILE @@FETCH_STATUS=0 BEGIN
                           | DBCC DBREINDEX(@TableName)
                           | FETCH NEXT FROM SysCur INTO @TableName
                           |END
                           |CLOSE SysCur
                           |DEALLOCATE SysCur";
       RecordSet = Новый COMОбъект("ADODB.RecordSet");
       RecordSet=Command.Execute();
   Исключение
       Сообщить("Ошибка во время индексирования базы данных!");
       Сообщить(ОписаниеОшибки());
   КонецПопытки;
14 mikecool
 
24.09.08
13:39
(13) раз коннект освоил :) в (7) я описал принцип, по которому сам бы действовал
через systables или syscolumns берем имена таблиц
там же в сисах есть и список индексов, надо посмотреть по каждой таблице список индексов, по составу индекса выбрать поля с группировкой и счетчиком, там где больше 1 - дубликат...
поточнее написать не могу, поскольку нет доступного скуля...
15 Serg_1960
 
24.09.08
13:50
(14) Правильные вещи говоришь. Только мне (после баааальшого перерыва) сложно все это было, не только сделать, - но и "понять" что мне советовали...

По мере "усвоения" я написал вот такую "болванку", в которой ставил точки останова и "изучал" что это "такое" я из SQL-я "вытащил" :о)

   Catalog = Новый COMОбъект("ADOX.Catalog");
   Catalog.ActiveConnection=Connection;
   
   ВсегоТаблиц = Catalog.Tables.Count-1;
   Для НомерТаблицы = 0 По ВсегоТаблиц Цикл
       Таблица = Catalog.Tables.Item(НомерТаблицы);
       Сообщить("Таблица: " + Таблица.Name + " тип: " + Таблица.Type);
       Если Таблица.Indexes.Count > 0 Тогда
           ВсегоИндексов =  Таблица.Indexes.Count-1;
           Для НомерИндекса = 0 По ВсегоИндексов Цикл
               Индекс = Таблица.Indexes.Item(НомерИндекса);
               Сообщить("   Индекс: " + Индекс.Name);
           КонецЦикла;
       КонецЕсли;
       Если Таблица.Type = "TABLE" Тогда
           Попытка
               Command = Новый COMОбъект("ADODB.Command");
               Command.ActiveConnection=Connection;
               Text = "DBCC DBREINDEX("+СокрЛП(Таблица.Name)+")";
               Command.CommandText=Text;
               RecordSet = Новый COMОбъект("ADODB.RecordSet");
               RecordSet=Command.Execute();
           Исключение
               Сообщить("Ошибка во время " + Text);
               Сообщить(ОписаниеОшибки());
           КонецПопытки;
       КонецЕсли;
   КонецЦикла;
16 Serg_1960
 
24.09.08
13:58
Алгорит НеПинатьНогамиСильно. Другие "начинают" писать с фразы "Hello Word" *:о)

Кстати: можно Com-ы сократить и писать Вывод = Connection.Execute("текст команды")... Дальше свои "иследования" приводить не буду. И так ясно что можно выполнять любые алгоритмы на T-SQL... Тем более что ошибка после переиндексации "определилась"
17 Serg_1960
 
24.09.08
14:02
Ошибка, указанная в (0) была спровоцирована "задвоением" записей в журналах документов. Когда знаешь где искать, - легко найдешь:

   Сообщить("Журналы: контроль на уникальность ссылки");
   Для Каждого Объект ИЗ Метаданные.ЖурналыДокументов Цикл
       Имя = Объект.Имя;
       Запрос = Новый Запрос;
       Запрос.Текст = "ВЫБРАТЬ
                      |    "+Имя+".Ссылка КАК Ссылка,
                      |    КОЛИЧЕСТВО("+Имя+".Ссылка) КАК Количество
                      |ИЗ
                      |    ЖурналДокументов."+Имя+" КАК "+Имя+"
                      |
                      |СГРУППИРОВАТЬ ПО
                      |    "+Имя+".Ссылка
                      |
                      |УПОРЯДОЧИТЬ ПО
                      |    Количество УБЫВ";
       Результат = Запрос.Выполнить();
       Если Не Результат.Пустой() Тогда
           Выборка = Результат.Выбрать();
           Пока Выборка.Следующий() И Выборка.Количество > 1 Цикл
               Сообщить("Журнал(ссылки) "+Объект.Синоним + " : " + Выборка.Ссылка);
               ЗапросСсылки = Новый Запрос;
               ЗапросСсылки.УстановитьПараметр("Дубль", Выборка.Ссылка);
               ЗапросСсылки.Текст = "ВЫБРАТЬ
                             |    "+Имя+".Дата КАК Дата,
                             |    "+Имя+".Номер КАК Номер,
                             |    "+Имя+".Ответственный КАК Ответственный
                             |ИЗ
                             |    ЖурналДокументов."+Имя+" КАК "+Имя+"
                             |
                             |ГДЕ
                             |    "+Имя+".Ссылка = &Дубль
                             |
                             |УПОРЯДОЧИТЬ ПО
                             |    Дата, Номер";
               ВыборкаСсылки = ЗапросСсылки.Выполнить().Выбрать();
               Пока ВыборкаСсылки.Следующий() Цикл
                   Сообщить("   Записи " + ВыборкаСсылки.Дата + " : " + ВыборкаСсылки.Номер + " : " + ВыборкаСсылки.Ответственный);
               КонецЦикла;
           КонецЦикла;
       КонецЕсли;
   КонецЦикла;
18 Serg_1960
 
24.09.08
14:26
Осталось сообщить только "разбор полетов":
Ошибка "появилась" после миграции дочернего узла из файлового варианта в SQL под Линукс - ни для меня, ни для моего админа "целина неосвоеная"... Наверное где-то "напартачили" при переходе... Но выбора у нас особо не было. Филиал уже давно "задыхался" - а босам денег было жалко. Вот "в горячах" и перешли "на халяву"...

Да совсем забыл :о)
В журналах я не стал "копаться" (ни времени, ни желания не было). Я их очистил и с "помощью" ТиИ заново заполнил...

В вышеуказанном алгоритме вставил:

Если Лев(Таблица.NAME,16) = "_DocumentJournal" Тогда
  Command.CommandText="TRUNCATE TABLE "+СокрЛП(Таблица.Name)+ " | "+UPDATE STATISTICS "+СокрЛП(Таблица.Name);
КонецЕсли;


Вот так... теперь можете меня ругать или хвалить...
19 Serg_1960
 
24.09.08
19:40
Добавлю ссылку http://www.holden.ru/node/19
20 Serg_1960
 
25.09.08
14:56
Добавлю алгорит поиска duplicate key для табличных частей справочников. С небольшими переделками можно использовать для табличных частей документов...

   Для Каждого Объект ИЗ Метаданные.Справочники Цикл
       ИмяОбъекта = Объект.Имя;
       Для Каждого Таблица Из Объект.ТабличныеЧасти Цикл
           ИмяТаблицы = Таблица.Имя;
           Состояние(ИмяОбъекта+"."+ИмяТаблицы);
           Запрос = Новый Запрос;
           Запрос.Текст = "
           |ВЫБРАТЬ
           |    " + ИмяТаблицы + ".Ссылка КАК Ссылка,
           |    " + ИмяТаблицы + ".НомерСтроки КАК НомерСтроки,
           |    КОЛИЧЕСТВО(" + ИмяТаблицы + ".НомерСтроки) КАК Количество
           |ИЗ
           |    Справочник." + ИмяОбъекта + "." + ИмяТаблицы + " КАК " + ИмяТаблицы + "
           |
           |СГРУППИРОВАТЬ ПО
           |    " + ИмяТаблицы + ".Ссылка,
           |    " + ИмяТаблицы + ".НомерСтроки
           |
           |УПОРЯДОЧИТЬ ПО
           |    Количество УБЫВ";
           Выборка = Запрос.Выполнить().Выбрать();
           Пока Выборка.Следующий() И Выборка.Количество > 1 Цикл
               Сообщить(ИмяОбъекта + "." + ИмяТаблицы + "/" + СокрЛП(Выборка.Ссылка) + "/поз." + Выборка.НомерСтроки + "/");
           КонецЦикла;
       КонецЦикла;
   КонецЦикла;
Здесь можно обсудить любую тему при этом оставаясь на форуме для 1Сников, который нужен для работы. Ymryn