pechkin: (Default)
pechkin ([personal profile] pechkin) wrote2012-09-05 11:22 am
Entry tags:

SQL - вопрос

Есть такая таблица, для связей объектов, и в ней два поля, куда вписываются id объектов. Нужны такие ограничения:

1. Нельзя привязать объект сам к себе: ALTER TABLE [dbo].[Relations] ADD CHECK (GUID1 <> GUID2)
2. Нельзя привязать один объект к другому больше, чем один раз: CREATE UNIQUE INDEX [GUIDsUnique] ON [dbo].[Relations]
([GUID1] ASC, [GUID2] ASC) 
3. Нельзя привязать один объект к другому, если другой уже привязан к первому. Как это реализовать на уровне базы данных?
4. Не забыл ли я чего?
UPD: Видимо, я сделаю так: 

-- проверка привязки объекта к себе самому
ALTER TABLE [dbo].[Relations] ADD CHECK (GUID1 <> GUID2)
-- проверка повторной привязки 
CREATE UNIQUE INDEX [GUIDsUnique] ON [dbo].[Relations] ([GUID1] ASC, [GUID2] ASC) 
-- триггер на добавление/изменение, который просто уберет из таблицы все (максимум одну) перевернутые пары. Они ведь по смыслу совершенно идентичны той, которая только что появилась

Я не ошибся нигде?

UPD: еще чуть более элегантное решение: не надо что-то делать, если не надо это делать:

ALTER TRIGGER [dbo].[CheckReversedPairs_Insert]
ON [dbo].[Call_Related]
INSTEAD OF INSERT
AS
BEGIN
if 0 = (select count(*)
    from call_related, inserted 
    where call_related.callguid1=inserted.callguid2 and call_related.callguid2=inserted.callguid1)
insert into call_related 
    select * from inserted
END

Осталось только сочинить запрос, который проверит существующую уже таблицу на перевернутые пары. Делов-то. И обедать.

UPD: Сытое брюхо подсказало выход:

SELECT Call_Related.*
FROM     Call_Related INNER JOIN
                  Call_Related AS Call_Related_1 ON Call_Related.CallGUID1 = Call_Related_1.CallGUID2 AND Call_Related.CallGUID2 = Call_Related_1.CallGUID1 AND 
                  Call_Related.CallGUID1 < Call_Related.CallGUID2

[identity profile] skvoznik.livejournal.com 2012-09-05 09:54 am (UTC)(link)
Эта структура допускает закольцовывание связей, в курсе?

[identity profile] skvoznik.livejournal.com 2012-09-05 09:55 am (UTC)(link)
т.е. этот подход

[identity profile] pechkin.livejournal.com 2012-09-05 10:18 am (UTC)(link)
Не-не, это совсем другое, это не дерево, а просто табличка связей, пар.

[identity profile] skvoznik.livejournal.com 2012-09-05 10:33 am (UTC)(link)
Ясно. В смысле. ясно, что недостаточно инфы, ибо есть разные варианты по всем пунктам...

[identity profile] skvoznik.livejournal.com 2012-09-05 10:00 am (UTC)(link)
по п. 3 как вариант:
создать вычисляемое поле, в котором будет хеш или любое другое однозначное соединение Guid1 и Guid2 и повесить на поле уникальный индекс. Чтобы для пары Guid1 и Guid2 хеш не зависел от порядка их следования, хеш можно строить, предварительно отсортировав guid-ы по значению. Щас еще подумаю..

[identity profile] pechkin.livejournal.com 2012-09-05 10:17 am (UTC)(link)
я придумал (почти сам, кстати!) кое-что получше. Ну, с некоторых точек зрения. Конечно, не идеально, потому что даже когда триггер отвергает добавление записи, он сообщает, что один ряд аффектед, и какого-нибудь клиент-программера это может сбить с толку.

[identity profile] skvoznik.livejournal.com 2012-09-05 10:36 am (UTC)(link)
фигово, т.к. клиент не будет знать, была ли вставлена строка.
Кроме того, я лично вообще триггеры почти не юзаю. Можешь сделать SP для вставки, а еще можешь рулить процессом в бизнес-слое - если за _эту_ целостность отвечает у тебя он, а не БД; при этом, в таблице оставляешь только жесткие констрейнты, без логики.

[identity profile] pechkin.livejournal.com 2012-09-05 11:11 am (UTC)(link)
У нас ситуация несколько сложнее, есть три не связанных друг с другом ни исторически, ни концептуально среды, сидящих на одной базе данных. Я даже не знаю, как они устроены, их пишут совершенно другие люди в других местах. Поэтому есть смысл засовывать такие вещи как можно ниже.

Можно сделать так, чтобы триггер, если не может сохранить, поднимал еггог. Не знаю, или это намного лучше.

[identity profile] skvoznik.livejournal.com 2012-09-05 11:15 am (UTC)(link)
Ну, исходя из первого абзаца как постановки задачи, трудно придумать решение :)

И ваще, пойду-ка я побегаю.

[identity profile] pechkin.livejournal.com 2012-09-05 12:04 pm (UTC)(link)
Хитрый!

[identity profile] pechkin.livejournal.com 2012-09-05 12:46 pm (UTC)(link)
А вообще такая функция, которая могла бы складывать во что-то два гуида, а потом бы еще и вычитать один гуид из этого чего-то и получать другой - мне бы очень помогла в работе с этой таблицей потом. Что бы это могло быть? В голове вертится только, забыл, как называется, когда первая цифра умножается на один, вторая на два, третья на четыре и так далее, и все это складывается.

[identity profile] pechkin.livejournal.com 2012-09-07 05:48 pm (UTC)(link)
Оба философа стояли, раскрыв рты.

– Дьявол меня раздери, – сказал Тфауматий. – Вот это я называю мышлением. Эхиоп, проклятье, почему мы сами никогда до такого не додумываемся?

– Хрен его знает, Тфауматий, – ответил Эхиоп благоговейным шепотом. – Наверно, у нас слишком хорошо натренированные мозги.

Сказав это, они развернулись на каблуках и вышли за дверь навстречу жизни, которая не снилась им в их самых удивительных снах.
(http://pechkin.rinet.ru/x/smp/xlat/Adams_D/H2G2G/1/da_h2g2g1_25.html)

[identity profile] skvoznik.livejournal.com 2012-09-05 11:27 pm (UTC)(link)
и кто после этого извращенец? ))

[identity profile] pechkin.livejournal.com 2012-09-07 05:46 pm (UTC)(link)
Тот, кто придумал гуидами пользоваться вместо интегеров. То есть, у него были, наверняка, свои причины, но у каждого извращенца хватит причин.