Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1] 2  Все   Вниз
  Печать  
Автор Тема: повтор в уникальном ключе  (Прочитано 33037 раз)
0 Пользователей и 8 Гостей смотрят эту тему.
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« : 21-09-2007 03:31 » 

задача такова: в некой базе B имеется таблица T, в таблице несколько простых полей и одно поле - уникальный ключ. Таблица, возможно, содержит ошибку - повторы значений в ключе. Записей в таблице много, перебор отпадает )  Как быстро выяснить, есть ли в таблице T записи с повторными значениями ключа ?
« Последнее редактирование: 21-09-2007 05:53 от Алексей1153++ » Записан

Sla
Команда клуба

ua
Offline Offline
Пол: Мужской

WWW
« Ответ #1 : 21-09-2007 05:57 » 

select count(key),key from table where Count(key)>1
Записан

Мы все учились понемногу... Чему-нибудь и как-нибудь.
RXL
Технический
Администратор

Offline Offline
Пол: Мужской

WWW
« Ответ #2 : 21-09-2007 06:02 » 

Код: (SQL)
SELECT id, COUNT(*)
FROM mytable
GROUP BY id
HAVING COUNT(*) > 1

id - столбец, нуждающийся в проверке.
На выходе: id и число повторов.

Конечно, проверка имеет смысл пока не создан уникальный ключ - после повторных значений быть не может в принципе.
Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #3 : 21-09-2007 06:10 » 

RXL, Ром, ключ уже создан...

Sla, а твой вариант уже для созданого ключа сработает ? Проверить то не могу по техническим причинам  - база не даёт просто так создать дубли

----
параллельный вопрос, о транзакциях

CommitTrans и Rollback - это "взаимозаменяемые" функции, или надо сначала обязательно Commit, а только потом откат ? (если откат потребовался) . Или же можно откатить сразу без подтверждения?


Записан

RXL
Технический
Администратор

Offline Offline
Пол: Мужской

WWW
« Ответ #4 : 21-09-2007 06:19 » 

Леш, тогда и проверять нечего. Просто обрабатывай ошибки вставки: если будет ошибка дублирования, то удали запись и повтори вставку.

-------
COMMIT - принять транзакцию
ROLLBACK - откатить транзакцию (отменить)

Действия прямо противоположные. После любой из этих команд транзакция закрывается.
« Последнее редактирование: 21-09-2007 06:21 от RXL » Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #5 : 21-09-2007 06:21 » 

RXL, вставок ещё нету, просто хочется проанализировать на наличие ошибки

----
значения процедур я понимяу, последовательность не совсем понимаю тогда, когда откат требуется. И ещё вот этот момент

begintrans();
if(!committrans)
{
  //вызывать rollback ?
}
Записан

RXL
Технический
Администратор

Offline Offline
Пол: Мужской

WWW
« Ответ #6 : 21-09-2007 06:48 » 

Леш, "вставка" - это действие, а не сущность. Ты, видимо, путаешь с понятием "запись" (или "строка"). Это так или я ошибаюсь?

Ошибка происходит при выполнении вставки (INSERT) или при модификации записи (UPDATE), но не существует сама по себе. При возникновении ошибки база не меняется, а транзакцию следует откатить.

Код:
try
{
  do INSERT // <----- ошибка возникает тут
  do COMMIT
}
catch (....)
{
  do ROLLBACK
}
Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #7 : 21-09-2007 06:53 » 

RXL, нет, я не путаю. Просто сбило мну с толку то, что у CDataBase::CommitTrans() тип функции BOOL , так вот - стоит ли проверять возвращаемое значение, или это всегда неважно ?
Записан

Sla
Команда клуба

ua
Offline Offline
Пол: Мужской

WWW
« Ответ #8 : 21-09-2007 07:22 » 

Алексей1153++, что мой, что RXL вариант , это одно и тоже
даже, мне кажется что RXL будет правильнее

кста, при использовании агрегатной функции, не обязательно делать group by

если таблица пустая, то тебе субд просто-напросто не даст что-либо вставить, хотя варианты бывают


Записан

Мы все учились понемногу... Чему-нибудь и как-нибудь.
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #9 : 21-09-2007 07:43 » 

если таблица пустая, то тебе субд просто-напросто не даст что-либо вставить, хотя варианты бывают

как это не даст ? Ещё как даёт )
Записан

RXL
Технический
Администратор

Offline Offline
Пол: Мужской

WWW
« Ответ #10 : 21-09-2007 07:45 » 

кста, при использовании агрегатной функции, не обязательно делать group by
Обязательно. Стандарт требует. Если конкретно, то на MySQL и Oracle без GROUP BY не работает.

если таблица пустая, то тебе субд просто-напросто не даст что-либо вставить, хотя варианты бывают
Ээээ.... Не логично что-то...
Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #11 : 21-09-2007 07:52 » 

а COUNT(*) - считает количество возвёрнутых записей что ли ?
Записан

Sla
Команда клуба

ua
Offline Offline
Пол: Мужской

WWW
« Ответ #12 : 21-09-2007 08:20 » 

поправлюсь Улыбаюсь
в приведенных примерах group by можно не использовать, но по стандарту НУЖНО!

поэтому... Нужно ипользовать group by

count(*) считает все записи
Записан

Мы все учились понемногу... Чему-нибудь и как-нибудь.
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #13 : 21-09-2007 08:30 » 

Sla, я имею в виду когда вот тут
SELECT id, COUNT(*)
FROM mytable
GROUP BY id
HAVING COUNT(*) > 1


------------------
всё же хотелось бы узнать, имеет ли смысл проверять значения,возвращаемые функциями

Код:
BOOL CDataBase::BeginTrans();
BOOL CDataBase::CommitTrans();
BOOL CDataBase::RollBack();
« Последнее редактирование: 21-09-2007 08:40 от Алексей1153++ » Записан

Sla
Команда клуба

ua
Offline Offline
Пол: Мужской

WWW
« Ответ #14 : 21-09-2007 08:58 » 

данная конструкция вернет количество ID

RXL тебе привел код

commit - грубо, зафиксирует транзакцию в базе
rollback - откатит

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

есть такое autocommit
если тру, то после закрытия приложения или коннекта  commit произойдет автоматически.

вот для примера,
Код:
select * from table;
insert into table values (.....);
select * from table;
// увидишь что значения добавлены
rollback;
select * from table;
// увидишь что значений нету
Код:
select * from table;
insert into table values (.....);
select * from table;
// увидишь что значения добавлены
commit;
select * from table;
// увидишь что значения есть

Дальше тоже интересно, но я не очень в этом ....

например:
пользователь 1:пользователь 2:
select * from table;
insert into table values (.....);
select * from table;
// увидишь что значения добавлены, но увидишь только свои
rollback;
select * from table;
// увидишь что значения есть, но от второго пользователя
select * from table;
insert into table values (.....);
select * from table;
// увидит только свои значения
commit;
select * from table;
// увидишь что значения есть
Записан

Мы все учились понемногу... Чему-нибудь и как-нибудь.
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #15 : 21-09-2007 18:18 » 

Цитата: Sla
кста, при использовании агрегатной функции, не обязательно делать group by
Обязательно.
Цитата: RXL
Обязательно. Стандарт требует. Если конкретно, то на MySQL и Oracle без GROUP BY не работает.
Не поэтому.
Цитата: Sla
в приведенных примерах group by можно не использовать, но по стандарту НУЖНО!

поэтому... Нужно ипользовать group by
В приведённых примерах нужно использовать group by по условию задачи.

Запрос вида
Код:
SELECT COUNT(id)
FROM mytable
Вернёт просто количество записей.

Запрос вида
Код:
SELECT COUNT(DISTINCT id)
FROM mytable
Вернёт количество уникальных записей.

Следовательно, запрос вида
Код:
SELECT COUNT(id) - COUNT(DISTINCT id) AS repcnt
FROM mytable
Вернёт количество неуникальных записей в таблице - можно установить факт наличия дубликатов, но мы не будем знать, какие записи содержат дубликаты.

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

Группировка применяется к запросу после первичного горизонтального фильтра, поэтому в первичном горизонтальном фильтре (секция WHERE) использование агрегатных функций либо недопустимо, либо они будут возвращать результат по всем записям, а не для групп записей. Первый запрос Sla будет возвращать 1 запись с количеством строк в таблице, если в таблице более одной записи - это решение совсем не той задачи, что задана в начале.

Именно поэтому необходимо использовать группировку и секцию вторичного горизонтального фильтра (HAVING), как раз предназначенного для фильтрации результатов группировок.

Запрос вида
Код:
SELECT
    id,
    COUNT(id) AS idcnt
FROM mytable
GROUP BY id
Вернёт список уникальных идентификаторов и количество таких записей, которые содержат соответствующий идентификатор. Нас интересуют только те записи, которые содержат неуникальный идентификатор, т.е. их количество в группе больше 1.
Код:
SELECT
    id,
    COUNT(id) AS idcnt
FROM mytable
GROUP BY id
HAVING COUNT(id) > 1

Если нужно получить собственно записи с повторяющимися идентификаторами, то можно использовать подзапрос:
Код:
SELECT
    *
FROM mytable
WHERE id IN
    (SELECT id
     FROM mytable
     GROUP BY id
     HAVING COUNT(id) > 1)
« Последнее редактирование: 21-09-2007 18:30 от dimka » Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #16 : 21-09-2007 18:32 » 

вот теперь всё вроде понятно ) Пасиба
« Последнее редактирование: 21-09-2007 18:34 от Алексей1153++ » Записан

Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #17 : 24-09-2007 08:01 » 

ещё попутные вопросы:

1)
добавляю в таблицу новое поле . Как инициализировать поле неповторяющимися значениями ? Только перебором ? Или есть какой нибудь специальный запрос ?

2)
Код:
BeginTrans;

//создаём новое поле
ALTER TABLE T ADD Fnew BIGINT NOT NULL;

//заполнить поле копированием из старого поля INTEGER NOT NULL
UPDATE T SET Fnew=Fold
 ---> происходит ошибка преобразования типов

 ...

CommitTrans

Когда же без BeginTrans, то всё исправно работает. В чём причина и как победить ?
« Последнее редактирование: 24-09-2007 08:45 от Алексей1153++ » Записан

Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #18 : 24-09-2007 12:57 » 

Цитата: Алексей1153++
ещё попутные вопросы:

1)
добавляю в таблицу новое поле . Как инициализировать поле неповторяющимися значениями ? Только перебором ? Или есть какой нибудь специальный запрос ?
Смотря какое значение. Улыбаюсь Если оно может быть биективно отображено из первичного ключа, то можно
Код:
UPDATE mytable
SET newcol = <некое выражение от>id
Иначе только перебором.

Цитата: Алексей1153++
//заполнить поле копированием из старого поля INTEGER NOT NULL
UPDATE T SET Fnew=Fold
 ---> происходит ошибка преобразования типов
А типы какие?
« Последнее редактирование: 24-09-2007 12:58 от dimka » Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #19 : 24-09-2007 18:37 » new

типы - новое BIGINT , старое - INTEGER.

кстати, ошибка не вылетает, если делать так
Код:

BeginTrans;

ALTER TABLE T ADD Fnew BIGINT NOT NULL;

CommitTrans;

BeginTrans;
UPDATE T SET Fnew=Fold

 ...

CommitTrans;

но это не совсем удобно
Записан

Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #20 : 24-09-2007 21:57 » 

А преобразование типов не работает? Что-то вроде:
Код:
UPDATE mytable
SET newField = CAST(oldField AS BIGINT)

Кстати, дело, похоже, в последовательности действий. FB поддерживает вложенные транзакции?
Код:
BEGIN TRANSACTION OutTran
BEGIN TRANSACTION FirstTran
ALTER TABLE ...
COMMIT TRANSACTION FirstTran
BEGIN TRANSACTION SecondTran
UPDATE ...
COMMIT TRANSACTION SecondTran
COMMIT TRANSACTION OutTran

Поддерживает ли FB или его клиент инструкцию типа "GO", которая предписывает безотлагательно выполнить все ранее переданные команды до начала выполенния следующих команд?
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #21 : 25-09-2007 03:37 » 

вложенные не получается делать, влетает. И в мсдн:
Цитата
CDatabase::BeginTrans
Call this member function to begin a transaction with the connected data source.
...
Caution   Calling BeginTrans again without calling CommitTrans or Rollback is an error.

GO не поможет в любом случае - многострочные запросы из программы делать не получается.

с CAST попробовал на всякий случай - тоже не работает.

Пока вижу только такой выход - завести некую таблицу с "системными" полями, когда первый раз вся операция пройдёт удачно - выставлю флажок
Записан

Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #22 : 25-09-2007 08:08 » 

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

Меня гложут сомнения: правильно ли вообще понимается проблема, точнее её причина?

Скажем, новое поле отмечено как NOT NULL. Чем оно заполняется по умолчанию, если не задано никакого DEFAULT-выражения?
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #23 : 25-09-2007 08:28 » 

dimka, по умолчанию поле заполнено null... Я сделал сейчас

ALTER TABLE T ADD Fnew BIGINT default 0 NOT NULL

всё равно то же самое. Без транзакции же исправно заполняется '0' . Раньше хоть и заполнялось NULL , но отличий не наблюдается )
« Последнее редактирование: 25-09-2007 08:30 от Алексей1153++ » Записан

Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #24 : 25-09-2007 09:28 » 

dimka, ты меня натолкнул на МЫСЛЬ Улыбаюсь

было бы здорово, если бы можно было просто тупо изменить тип поля F_old с INTEGER на BIGINT . Все перечисленные выше проблемы бы сразу отпали )
Такое возможно ?
Записан

Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #25 : 25-09-2007 10:09 » 

собственно вот, нашёл

ALTER TABLE mess_log alter MESSAGE_ID TYPE BIGINT

но есть одно но. Имя поля используется в триггере, база не даёт менять тип поля, когда есть ссылки на поле. Без триггера всё нормально. Как тут поступить ?
Записан

Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #26 : 25-09-2007 10:33 » 

в qsl-editor в IBExpert попробовал так

Код:
/*выносим триггер (но хотелось бы не так жёстко :( )*/
drop trigger MESS_LOG_BI ;

/*меняем тип ключевого поля*/
ALTER TABLE mess_log alter MESSAGE_ID TYPE BIGINT  ;

/*создаём триггер обратно*/
create trigger MESS_LOG_BI for mess_log before insert as
 BEGIN
  IF (NEW.MESSAGE_ID IS NULL)
   THEN NEW.MESSAGE_ID = GEN_ID(new_mess_id,1);
 END

это получилось, но как-то всё это кажется коряво ...
Записан

Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #27 : 25-09-2007 14:38 » 

Имхо, не коряво, а логично. Всё равно тебе пришлось бы потом переключать триггер на новое поле.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #28 : 25-09-2007 18:09 » 

тогда так и оставим )

ещё как-то надо сделать флажок о том, что я эту коррекцию вносил, в отсутствие опыта вижу два варианта:
1) вышеописанное некое поле-флаг в некой "системной" таблице
2) проверить тип поля (как ?)
Записан

Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #29 : 26-09-2007 04:31 » 

программно проверить тип поля получилось так
Код:
CString sql;
CDBVariant var;
CRecordset rs(pbase);

try
{
sql="SELECT FIRST(1) message_id FROM T";
rs.Open(CRecordset::snapshot,sql,CRecordset::readOnly);

CODBCFieldInfo FI;
rs.GetODBCFieldInfo((short)0,FI);
rs.Close();

switch(FI.m_nSQLType)
{
default:
{
//какой то левый тип
}
break;

case SQL_INTEGER:
{
//
}
break;

case SQL_BIGINT:
{
//
}
break;
}

}
catch(CException* e)
{
}
« Последнее редактирование: 26-09-2007 05:54 от Алексей1153++ » Записан

Страниц: [1] 2  Все   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines