Ошибка внешнего ключа при рекурсивном отношении [дубликат]

Ваш вопрос хорош, речь идет не только о методах. Функции и объекты в модулях обычно имеют префикс с одним символом подчеркивания и могут быть префиксными двумя.

Но имена __double_underscore, например, не обрабатываются именами в модулях. Случается, что имена, начинающиеся с одного (или более) подчеркивания, не импортируются, если вы импортируете все из модуля (из импорта модуля *), а также имена, указанные в справке (модуль).

148
задан Ricardo Altamirano 9 July 2012 в 21:41
поделиться

9 ответов

SQL Server делает простой подсчет каскадных путей и вместо того, чтобы пытаться выяснить, существуют ли какие-либо циклы, он принимает худшее и отказывается создавать ссылочные действия (CASCADE): вы можете и должны создавать ограничения без референтные действия. Если вы не можете изменить свой дизайн (или это может привести к компрометации вещей), вам следует рассмотреть возможность использования триггеров в качестве последнего средства.

Решение каскадных путей FWIW является сложной задачей. Другие SQL-продукты просто проигнорируют проблему и позволят вам создавать циклы, и в этом случае это будет гонка, которая увидит, что будет перезаписывать последнее значение, возможно, незнание дизайнера (например, ACE / Jet делает это). Я понимаю, что некоторые SQL-продукты попытаются разрешить простые случаи. Факт остается фактом, SQL Server даже не пытается, играет его сверхбезопасно, отказываясь от нескольких путей, и, по крайней мере, это говорит вам об этом.

153
ответ дан onedaywhen 21 August 2018 в 18:20
поделиться
  • 1
    В конце концов я пришел к одному и тому же заключению, и в итоге решил его использовать с помощью триггеров. Приятно знать, что есть разница между СУБД и что на самом деле это дизайнерское решение, за которое я застрял. Большое спасибо :) – user 12 May 2009 в 11:37
  • 2
    одна вещь, которую я до сих пор не могу понять, заключается в том, что если эта «проблема» может быть решена с помощью триггера, то как получилось, что триггер не будет «вызывать циклы или несколько каскадных путей ...»? ? – armen 24 July 2014 в 11:17
  • 3
    @armen: потому что ваш триггер явно предоставит логику, которую система не могла бы косвенно определить самостоятельно, например, если есть несколько путей для ссылочного действия удаления, тогда ваш код запуска определит, какие таблицы будут удалены и в каком порядке. – onedaywhen 5 August 2014 в 16:22
  • 4
    А также запускается триггер после завершения первой операции, поэтому гонка не продолжается. – Bon 4 August 2015 в 16:50
  • 5
    @dumbledad: Я имею в виду, что только триггеры используют, когда ограничения (возможно, на комбинации) не могут выполнить задание. Ограничения являются декларативными, и их реализация несет ответственность за систему. Триггеры - это процедурный код, и вы должны кодировать (и отлаживать) реализацию и терпеть их недостатки (худшая производительность и т. Д.). – onedaywhen 22 February 2016 в 16:14

Это ошибка политики триггеров базы данных. Триггер - это код и может добавлять некоторые отношения или условия к каскадному отношению, например Cascade Deletion. Вам может потребоваться специализировать опции связанных таблиц вокруг этого, например Отключение CascadeOnDelete :

protected override void OnModelCreating( DbModelBuilder modelBuilder )
{
    modelBuilder.Entity<TableName>().HasMany(i => i.Member).WithRequired().WillCascadeOnDelete(false);
}

или Полностью отключить эту функцию:

modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
0
ответ дан Amirhossein Mehrvarzi 21 August 2018 в 18:20
поделиться

Я хотел бы указать, что (функционально) существует большая разница между циклами и / или несколькими путями в SCHEMA и DATA. В то время как циклы и, возможно, многолучевые передачи в DATA, безусловно, могут усложнить обработку и вызвать проблемы с производительностью (стоимость «правильной» обработки), стоимость этих характеристик в схеме должна быть близка к нулю.

Поскольку большинство кажущихся циклов в RDB происходят в иерархических структурах (организационная диаграмма, часть, подчасти и т. д.), к сожалению, SQL Server принимает худшее; т.е. цикл цикла цикла ==. На самом деле, если вы используете ограничения RI, вы не можете построить цикл в данных!

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

Конечно, если SQL Server сделал , разрешить циклы он все равно будет подвержен глубине 32, но это, вероятно, подходит для большинства случаев. (Слишком плохо, что это не настройки базы данных, однако!) [/ ​​G4]

«Вместо удаления» триггеры тоже не работают. Во второй раз, когда таблица посещается, триггер игнорируется. Итак, если вы действительно хотите имитировать каскад, вам придется использовать хранимые процедуры в присутствии циклов. Тем не менее, вместо триггера вместо удаления будет работать для многолучевых случаев.

Celko предлагает «лучший» способ представления иерархии, которая не вводит циклы, но есть компромиссы.

11
ответ дан Bill Cohagan 21 August 2018 в 18:20
поделиться
  • 1
    & quot;, если вы используете ограничения RI, вы не можете фактически построить цикл в данных! & quot; -- хорошая точка зрения! – onedaywhen 23 March 2016 в 18:11

Типичная ситуация с несколькими каскадинными путями будет такова: мастер-таблица с двумя деталями, скажем, «Мастер» и «Детали1» и «Деталь2». Обе детали являются каскадным удалением. Пока нет проблем. Но что, если обе детали имеют отношение «один ко многим» с какой-либо другой таблицей (например, «SomeOtherTable»). SomeOtherTable имеет столбец Detail1ID и столбец Detail2ID.

Master { ID, masterfields }

Detail1 { ID, MasterID, detail1fields }

Detail2 { ID, MasterID, detail2fields }

SomeOtherTable {ID, Detail1ID, Detail2ID, someothertablefields }

Другими словами: некоторые записи в SomeOtherTable связаны с документами Detail1, а некоторые из записей в SomeOtherTable связаны с записями Detail2. Даже если гарантировано, что SomeOtherTable-записи никогда не относятся к обоим деталям, теперь невозможно сделать каскадное удаление SomeOhterTable для обеих деталей, потому что существует несколько каскадных путей от Master до SomeOtherTable (один через Detail1 и один через Detail2). Теперь вы, возможно, уже поняли это. Вот возможное решение:

Master { ID, masterfields }

DetailMain { ID, MasterID }

Detail1 { DetailMainID, detail1fields }

Detail2 { DetailMainID, detail2fields }

SomeOtherTable {ID, DetailMainID, someothertablefields }

Все поля ID являются ключевыми полями и автоматическим приращением. Суть лежит в полях DetailMainId таблиц Detail. Эти поля являются как ключевыми, так и ссылочными. Теперь можно каскадно удалить все, удалив только основные записи. Недостатком является то, что для каждой detail1-record AND для каждой записи detail2 также должна быть запись DetailMain-запись (которая фактически создается сначала, чтобы получить правильный и уникальный идентификатор).

80
ответ дан Chuck Norris 21 August 2018 в 18:20
поделиться
  • 1
    Ваш комментарий помог мне понять проблему, с которой я столкнулся. Спасибо! Я бы предпочел отключить каскадное удаление для одного из путей, а затем обработать удаление других записей другими способами (хранимые процедуры, триггеры, код и т. Д.). Но я держу ваше решение (сгруппировавшись на одном пути) в виду возможных вариантов применения одной и той же проблемы ... – freewill 14 February 2014 в 23:39
  • 2
    Один для использования слова crux (а также для объяснения) – masterwok 11 December 2014 в 05:35
  • 3
    Это лучше, чем писать триггеры? Кажется странным добавить дополнительную таблицу, чтобы получить работу каскада. – dumbledad 2 February 2016 в 11:04

По звукам этого файла у вас есть действие OnDelete / OnUpdate на одном из существующих внешних ключей, которое изменит вашу таблицу кодов.

Итак, создав этот внешний ключ, вы создадите циклическая задача,

Например Обновление сотрудников приводит к изменению кодов с помощью действия «Обновление», что приводит к замене сотрудников на действие «Обновление» ... и т. Д. ...

Если вы публикуете свои определения таблиц для обеих таблиц, & amp; ваши определения внешнего ключа / ограничения, мы сможем сообщить вам, где проблема ...

2
ответ дан Eoin Campbell 21 August 2018 в 18:20
поделиться
  • 1
    Они довольно длинны, поэтому я не думаю, что могу опубликовать их здесь, но я буду очень благодарен за вашу помощь - не знаю, есть ли способ отправить их вам? Я попытаюсь описать это: единственные ограничения, которые существуют, - это три таблицы, у которых есть все поля, которые ссылаются на коды с помощью простого ключа INT Id. Проблема заключается в том, что Employee имеет несколько полей, которые ссылаются на таблицу кодов, и что я хочу, чтобы все они каскадировались до SET NULL. Все, что мне нужно, это то, что когда коды удаляются, ссылки на них должны быть равно нулю везде. – user 12 May 2009 в 09:13
  • 2
    публиковать их в любом случае ... Я не думаю, что кто-то здесь будет возражать, и окно кода будет правильно их форматировать в прокручивающем блоке :) – Eoin Campbell 12 May 2009 в 09:30

Имеется статья, в которой объясняется, как выполнять несколько путей удаления с помощью триггеров. Возможно, это полезно для сложных сценариев.

http://www.mssqltips.com/sqlservertip/2733/solving-the-sql-server-multiple-cascade-path-issue-with- а-триггер /

4
ответ дан Javier 21 August 2018 в 18:20
поделиться

Это потому, что у Emplyee может быть коллекция другой сущности. Квалификация и квалификация могут иметь некоторые другие университеты коллекции, например

public class Employee{
public virtual ICollection<Qualification> Qualifications {get;set;}

}

public class Qualification{

public Employee Employee {get;set;}

public virtual ICollection<University> Universities {get;set;}

}

public class University{

public Qualification Qualification {get;set;}

}

В DataContext это может быть как ниже

protected override void OnModelCreating(DbModelBuilder modelBuilder){

modelBuilder.Entity<Qualification>().HasRequired(x=> x.Employee).WithMany(e => e.Qualifications);
modelBuilder.Entity<University>.HasRequired(x => x.Qualification).WithMany(e => e.Universities);

}

, в этом случае есть цепочка от Employee to Qualification и От квалификации до университетов. Таким образом, это бросало мне такое же исключение.

Это сработало для меня, когда я изменил

    modelBuilder.Entity<Qualification>().**HasRequired**(x=> x.Employee).WithMany(e => e.Qualifications); 

To

    modelBuilder.Entity<Qualification>().**HasOptional**(x=> x.Employee).WithMany(e => e.Qualifications);
1
ответ дан RAJ 21 August 2018 в 18:20
поделиться

Триггер является решением этой проблемы:

IF OBJECT_ID('dbo.fktest2', 'U') IS NOT NULL
    drop table fktest2
IF OBJECT_ID('dbo.fktest1', 'U') IS NOT NULL
    drop table fktest1
IF EXISTS (SELECT name FROM sysobjects WHERE name = 'fkTest1Trigger' AND type = 'TR')
    DROP TRIGGER dbo.fkTest1Trigger
go
create table fktest1 (id int primary key, anQId int identity)
go  
    create table fktest2 (id1 int, id2 int, anQId int identity,
        FOREIGN KEY (id1) REFERENCES fktest1 (id)
            ON DELETE CASCADE
            ON UPDATE CASCADE/*,    
        FOREIGN KEY (id2) REFERENCES fktest1 (id) this causes compile error so we have to use triggers
            ON DELETE CASCADE
            ON UPDATE CASCADE*/ 
            )
go

CREATE TRIGGER fkTest1Trigger
ON fkTest1
AFTER INSERT, UPDATE, DELETE
AS
    if @@ROWCOUNT = 0
        return
    set nocount on

    -- This code is replacement for foreign key cascade (auto update of field in destination table when its referenced primary key in source table changes.
    -- Compiler complains only when you use multiple cascased. It throws this compile error:
    -- Rrigger Introducing FOREIGN KEY constraint on table may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, 
    -- or modify other FOREIGN KEY constraints.
    IF ((UPDATE (id) and exists(select 1 from fktest1 A join deleted B on B.anqid = A.anqid where B.id <> A.id)))
    begin       
        update fktest2 set id2 = i.id
            from deleted d
            join fktest2 on d.id = fktest2.id2
            join inserted i on i.anqid = d.anqid        
    end         
    if exists (select 1 from deleted)       
        DELETE one FROM fktest2 one LEFT JOIN fktest1 two ON two.id = one.id2 where two.id is null -- drop all from dest table which are not in source table
GO

insert into fktest1 (id) values (1)
insert into fktest1 (id) values (2)
insert into fktest1 (id) values (3)

insert into fktest2 (id1, id2) values (1,1)
insert into fktest2 (id1, id2) values (2,2)
insert into fktest2 (id1, id2) values (1,3)

select * from fktest1
select * from fktest2

update fktest1 set id=11 where id=1
update fktest1 set id=22 where id=2
update fktest1 set id=33 where id=3
delete from fktest1 where id > 22

select * from fktest1
select * from fktest2
0
ответ дан Tone Škoda 21 August 2018 в 18:20
поделиться

Мое решение этой проблемы, возникшее с использованием ASP.NET Core 2.0 и EF Core 2.0, состояло в следующем:

  1. Выполнить команду update-database в консоли управления пакетами (PMC) до создать базу данных (это приведет к тому, что «Введение ограничения FOREIGN KEY ... может вызвать циклы или несколько каскадных путей»).
  2. Запустите команду script-migration -Idempotent в PMC, чтобы создать скрипт, который можно запустить независимо из существующих таблиц / ограничений
  3. Возьмите полученный скрипт и найдите ON DELETE CASCADE и замените на ON DELETE NO ACTION
  4. Выполните измененный SQL по базе данных

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

Слишком плохо, что я не смог найти способ сделать это в Entity Framework Core 2.0.

Удачи!

-2
ответ дан user1477388 21 August 2018 в 18:20
поделиться
  • 1
    Вы можете изменить файл миграции для этого (без изменения сценария sql), то есть в файле миграции вы можете установить действие onDelete для ограничения из Cascade – Rushi Soni 17 March 2018 в 12:46
  • 2
    Лучше указать это, используя бесплатные аннотации, чтобы вам не приходилось это делать, если вы в конечном итоге удалите и заново создаете папку миграций. – Allen Wang 1 May 2018 в 15:08
  • 3
    По моему опыту, можно использовать безнадежные аннотации и их использовать (я их использую), но они часто бывают довольно затруднительными. Простое указание их в коде не всегда приводит к ожидаемому результату. – user1477388 2 May 2018 в 10:21
Другие вопросы по тегам:

Похожие вопросы: