2013-07-30 8 views
5

To zapytanie, kiedy uruchamiane na ADO.NET z MissingSchemaAction.AddWithKey rzuca wyjątek:ADO.net SQL Server 2012 - Zapytanie z kluczem kompozytowego i MissingSchemaAction.AddWithKey nieprawidłowo dodaje ograniczenie

Nie udało się włączyć ograniczenia. Jeden lub więcej wierszy zawiera wartości naruszające ograniczenia inne niż zero, unikalne lub klucze obce.

zapytania:

SELECT map.GroupId, b.PersonId 
FROM [GroupPersonMap] as map 
INNER JOIN [Person] AS b ON b.PersonId = map.PersonId 
GROUP BY map.GroupId, b.PersonId 

Kontrola miejscowi ujawnia, że ​​ograniczenie przez unikalność dla PersonId został dodany. Co więcej, uruchamianie tego samego zapytania w SQL Server Manager zwraca zestaw wyników bez ostrzeżeń i błędów. Ten dokładny kod był używany do pracy z SQL Server 2005. Używając SQL Server 2005, podczas uruchamiania tego zapytania na ADO.net, zapytanie tworzy poprawnie złożone wiązanie. Czy to jest problem z aktualizacją?

Na marginesie wiem, że ustawienie EnforceConstraints = false zapewnia obejście tego problemu. Idealnie jednak, chciałbym rozwiązać ten problem w jego katalogu głównym.

Setup, aby odtworzyć:

CREATE TABLE [GroupPersonMap] 
(
[GroupId] [int] NOT NULL, 
[PersonId] [int] NOT NULL 
) ON [PRIMARY] 
GO 
ALTER TABLE [GroupPersonMap] ADD CONSTRAINT [PK_GroupPersonMAP] PRIMARY KEY CLUSTERED ([GroupId], [PersonId]) 

CREATE TABLE [Person] 
(
[PersonId] [int] NOT NULL IDENTITY(1, 1), 
[Val] INT 
) ON [PRIMARY] 
GO 
ALTER TABLE [Person] ADD CONSTRAINT [PK_Person] PRIMARY KEY CLUSTERED ([PersonId]) 

następnie wprowadzenie wartości:

INSERT INTO [GroupPersonMap] 
SELECT 1, 1 
UNION ALL 
SELECT 2, 1 

INSERT INTO [Person] 
SELECT 1 
+1

W którym momencie pojawia się ten komunikat o błędzie? Po drugiej wstawce? – Codeman

Odpowiedz

2

Można znaleźć kilka przydatnych porad debugowania w tym poście:

Failed to enable constraints. One or more rows contain values violating non-null, unique, or foreign-key constraints

tym powiedział, Pierwszą rzeczą, która wyskakuje na mnie tutaj, jest to, że wszyscy łączą się z tobą d grupowanie są niepotrzebne. Napisałeś

SELECT map.GroupId, b.PersonId 
FROM [GroupPersonMap] as map 
INNER JOIN [Person] AS b ON b.PersonId = map.PersonId 
GROUP BY map.GroupId, b.PersonId 

ale można uzyskać takie same wyniki przy

SELECT * FROM GroupPersonMap 

Oto co mam na myśli:

1) Nie jesteś wybranie dowolnego pola z tabeli B (osoba) z wyjątkiem b .PersonId. Ale wiesz z klauzuli JOIN, że wartości b.PersonId muszą być równe map.PersonId, więc nie ma informacji pochodzących od osoby, która nie jest jeszcze w GroupPersonMap. Tak więc, możemy rozebrać się JOIN:

SELECT map.GroupId, map.PersonId 
FROM [GroupPersonMap] as map 
GROUP BY map.GroupId, map.PersonId 

2) Ale teraz, map.GroupId, map.PersonId jest dokładny klucz podstawowy z tej tabeli, więc wiemy, że żadna rzeczywista agregacja będzie occur-- każdą kombinację GroupID/PersonID jest wyjątkowy zgodnie z definicją. Zatem każdy rząd danych wejściowych "zgrupuje się" z jednym i tylko jednym rzędem danych wyjściowych. Oznacza to, że możemy rozebrać się z klauzulą ​​GROUP BY także:

SELECT map.GroupId, map.PersonId 
FROM [GroupPersonMap] as map 

3) A teraz to, co nam zostało w klauzuli SELECT jest map.GroupId, map.PersonId - to wszystkie pola w tabeli. Możesz więc jeszcze bardziej uprościć "SELECT * FROM GroupPersonMap", choć w praktyce dla kodu produkcyjnego najlepiej jest zawsze wyliczyć pożądane pola. (Określ dokładnie to, co chcesz, chroni przed późniejszymi zmianami schematu.)

Jeszcze jedna uwaga na temat JOIN: kiedy dołączasz do tabeli na swoim kluczu podstawowym, najlepiej dołączyć do całego klucza. (Jest to "pierwsze zamówienie" słynnej "trzeciej normalnej formy".) Gdy tabela ma klucz złożony, oznacza to uwzględnienie kryteriów JOIN i/lub WHERE dla każdego pola w kluczu. W przeciwnym razie możesz skończyć z łączeniem półkartezjańskim lub, w takich przypadkach, z możliwymi problemami, w których druga połowa klucza głównego pozostaje nieokreślona. Przypuszczam, że jeśli chcesz zostawić zapytanie tak jak jest, ale nadal usuniesz swój błąd, możesz to zrobić po prostu mówiąc, która grupa Cię interesuje (np. WHERE map.GroupId = xxxx). Jeśli naprawdę chcesz mieć listę wszystkich, to powiedziałabym, że uproszczony ponowny zapis bez niepotrzebnego JOINING i GROUPING byłby najlepszą linią pierwszego ataku.