Chcę wymusić, aby tylko jeden rekord w tabeli był uważany za „domyślną” wartość dla innych zapytań lub widoków, które mogą uzyskać dostęp do tej tabeli.
Zasadniczo chcę zagwarantować, że to zapytanie zawsze zwróci dokładnie jeden wiersz:
SELECT ID, Zip
FROM PostalCodes
WHERE isDefault=True
Jak miałbym to zrobić w SQL?
mysql
database-design
emaynard
źródło
źródło
PostalCodes
pusty? Jeśli wiersz ma już właściwość, powinien być ustawiony na wartość false, chyba że w innym wierszu SQL ustawiono inny wiersz (jeśli taki istnieje) na true. Czy zero wierszy może mieć właściwość między granicami transakcji? Czy ostatni wiersz w tabeli powinien być zmuszony do posiadania właściwości i zapobiegać usuwaniu? Doświadczenie mówi mi, że „gwarancja dokładnie jednego rzędu” w rzeczywistości oznacza coś innego, często po prostu „najwyżej jednego rzędu”.Odpowiedzi:
Edycja: jest to ukierunkowane na SQL Server, zanim poznaliśmy MySQL
Istnieją
45 sposobów, aby to zrobić: w kolejności od najbardziej do najmniej pożądane pożądaneNie wiemy, co to jest RDBMS, jednak nie wszystkie mogą mieć zastosowanie
Najlepszym sposobem jest filtrowany indeks. Wykorzystuje DRI do zachowania wyjątkowości.
Kolumna obliczeniowa z wyjątkowością (patrz odpowiedź Jacka Douglasa) (dodana przez edycję 2)
Indeksowany / zmaterializowany widok, który jest jak filtrowany indeks przy użyciu DRI
Wyzwalacz (zgodnie z innymi odpowiedziami)
Sprawdź ograniczenie za pomocą UDF. Nie jest to bezpieczne w przypadku izolacji współbieżności i migawek. Zobacz Raz Dwa Trzy Cztery
Należy zauważyć, że wymóg „jeden domyślny” znajduje się w tabeli, a nie w procedurze przechowywanej. Procedura składowana będzie miała takie same problemy z współbieżnością jak ograniczenie sprawdzania z udf
Uwaga: wielokrotnie zadawane w SO:
źródło
Uwaga: Ta odpowiedź została udzielona, zanim stało się jasne, że pytający chciał czegoś specyficznego dla MySQL. Ta odpowiedź jest stronnicza w stosunku do SQL Server.
Zastosuj ograniczenie CHECK do tabeli, która wywołuje UDF i upewnia się, że jego wartość zwrotna wynosi <= 1. UDF może po prostu policzyć wiersze w tabeli GDZIE
isDefault = TRUE
. Zapewni to, że w tabeli nie będzie więcej niż 1 domyślny wiersz.Powinieneś dodać mapę bitową lub filtrowany indeks do
isDefault
kolumny (w zależności od twojego platforom), aby upewnić się, że ten UDF działa bardzo szybko.Ostrzeżenia
PostalCodes
tabeli, jednak pozostaje to problem z wydajnością w sytuacjach, w których prawdopodobne jest działanie oparte na zestawie.Biorąc pod uwagę wszystkie powyższe zastrzeżenia, zalecam skorzystanie z sugestii gbn filtrowanego unikalnego indeksu zamiast ograniczenia sprawdzania.
źródło
Oto procedura składowana (MySQL Dialect):
Aby upewnić się, że tabela jest czysta i procedura przechowywana działa, zakładając, że domyślnym identyfikatorem jest 200, uruchom następujące kroki:
Zamiast procedury przechowywanej, co powiesz na wyzwalacz?
Aby upewnić się, że tabela jest czysta i wyzwalacz działa, zakładając, że domyślnym identyfikatorem jest 200, uruchom następujące kroki:
źródło
Możesz użyć wyzwalacza, aby zastosować reguły. Gdy instrukcja UPDATE lub INSERT ustawia isDefault na True, SQL w wyzwalaczu może ustawić wszystkie inne wiersze na False.
Stosując to, musisz wziąć pod uwagę inne sytuacje. Na przykład, co powinno się zdarzyć, jeśli więcej niż jeden wiersz i zestawy UPDATE lub INSERT mają wartość domyślną True? Jakie zasady będą obowiązywać, aby uniknąć złamania reguły?
Czy to możliwe, że stan nie ma wartości domyślnej? Co się stanie, gdy aktualizacja ustawi wartość isDefault z True na False.
Po zdefiniowaniu reguł możesz je wbudować w wyzwalacz.
Następnie, jak wskazano w innej odpowiedzi, chcesz zastosować ograniczenie sprawdzania, aby pomóc w egzekwowaniu reguł.
źródło
Następujące zestawia przykładową tabelę i dane:
Jeśli utworzysz procedurę składowaną, aby ustawić flagę IsDefault i usuniesz uprawnienia do zmiany tabeli podstawowej, możesz wymusić to za pomocą poniższego zapytania. Wymagałoby to za każdym razem skanowania tabeli.
W zależności od wielkości tabeli indeks IsDefault (DESC) może spowodować jedno lub oba poniższe zapytania, unikając pełnego skanowania.
Jeśli nie możesz usunąć uprawnień z tabeli podstawowej, musisz egzekwować integralność w inny sposób i korzystasz z SQL2008, możesz skorzystać z filtrowanego unikalnego indeksu:
źródło
W przypadku MySQL, który nie ma przefiltrowanych indeksów, możesz wykonać następujące czynności. Wykorzystuje fakt, że MySQL dopuszcza wiele wartości NULL w unikalnych indeksach, i wykorzystuje dedykowaną tabelę i klucz obcy do symulacji typu danych, który może mieć tylko NULL i 1 jako wartości.
Dzięki temu rozwiązaniu zamiast wartości prawda / fałsz użyłbyś po prostu 1 / NULL.
Myślę, że jedyne, co może pójść nie tak, to to, że ktoś doda wiersz do
DefaultFlag
tabeli. Myślę, że nie jest to duży problem i zawsze możesz to naprawić za pomocą uprawnień, jeśli naprawdę chcesz być bezpieczny.źródło
To w zasadzie sprawiało ci problemy, ponieważ umieściłeś pole w niewłaściwym miejscu i to wszystko.
Mają tabelę „Domyślne” z kodem pocztowym, która zawiera identyfikator, którego szukasz. Zasadniczo dobrze jest również nazywać swoje tabele zgodnie z jednym rekordem, więc lepiej byłoby nazwać swoją tabelę początkową „Kod pocztowy”. Domyślne będą tabelą z jednym wierszem, dopóki nie będziesz musiał wyspecjalizować swoich domyślnych ustawień w stosunku do niektórych innych konfiguracji (takich jak historia).
Ostateczne zapytanie, które chcesz, jest następujące:
źródło