Jaka jest odpowiednia architektura indeksu, gdy jest zmuszona do wdrożenia IsDeleted (miękkie usuwanie)?

17

Obecnie mamy istniejącą bazę danych i aplikację, która jest w pełni funkcjonalna. W tym momencie nie mam możliwości zmiany architektury. Dzisiaj każda tabela w bazie danych ma pole „IsDeleted” NOT NULL BIT z domyślną wartością „0”. Gdy aplikacja „usuwa” dane, po prostu aktualizuje flagę IsDeleted na 1.

Trudno mi zrozumieć, w jaki sposób należy ustrukturyzować indeksy w każdej tabeli. W tej chwili każde zapytanie / dołącz / etc zawsze implementuje kontrolę IsDeleted. Jest to standard, którego muszą przestrzegać nasi programiści. Biorąc to pod uwagę, próbuję ustalić, czy wszystkie moje klastrowane indeksy klucza podstawowego w każdej tabeli muszą zostać zmienione, aby uwzględnić klucz podstawowy ORAZ pole BIT IsDeleted. Również od KAŻDEGO zapytania / dołączenia / etc. musi zaimplementować kontrolę IsDeleted, czy właściwe jest założenie, że KAŻDY POJEDYNCZY indeks (również niesklastrowany) powinien zawierać pole IsDeleted jako pierwsze pole indeksu?

Mam jeszcze jedno pytanie dotyczące przefiltrowanych indeksów. Rozumiem, że mogłem umieścić filtry w indeksach, takie jak „WHERE IsDeleted = 0”, aby zmniejszyć rozmiar indeksów. Ponieważ jednak każde sprzężenie / zapytanie będzie musiało zaimplementować kontrolę IsDeleted, czy uniemożliwiłoby to użycie filtrowanego indeksu (ponieważ kolumna IsDeleted jest używana w sprzężeniu / zapytaniu)?

Pamiętaj, że nie mam możliwości zmiany podejścia IsDeleted.

Philᵀᴹ
źródło

Odpowiedzi:

13

Najłatwiejszym rozwiązaniem jest pozostawienie kluczy i indeksów klastrowych w spokoju oraz użycie indeksów filtrowanych dla indeksów nieklastrowanych.

Ponadto możesz migrować niektóre duże tabele do partycjonowanych hałd lub partycjonowanych magazynów klastrowych w kolumnach (SQL Server 2016+), pozostawiając klucz podstawowy i unikalne indeksy bez partycjonowania. Umożliwiłoby to wypchnięcie niekluczowych kolumn wierszy IsDeleted do osobnej struktury danych, która może być dodatkowo skompresowana inaczej lub zapisana w innej grupie plików.

I upewnij się, że programiści używają literału zamiast parametru, aby odfiltrować wiersze IsDeleted. Z parametrem SQL Server musi używać tego samego planu zapytań dla obu przypadków.

NA PRZYKŁAD

SELECT ... WHERE ... AND IsDeleted=0

I nie:

SELECT ... WHERE ... AND IsDeleted=@IsDeleted

Użycie parametru uniemożliwi użycie filtrowanego indeksu i może sprawić kłopoty z węszeniem parametrów.

David Browne - Microsoft
źródło
Biorąc pod uwagę wszechobecność i znaczenie IsDeletedkolumny, niezależnie od fizycznego przechowywania, prawdopodobnie sensowne byłoby ujawnienie danych przez dwa widoki (opcjonalnie w różnych schematach), rozwiązując zarówno problem parametryzacji, jak i popełniając błędy przy dostępie do danych, które nie powinny były być dostęp jest mniej prawdopodobny. Dostęp do danych podstawowych ma znaczenie tylko w rzadkich przypadkach, w których usunięte i nieskasowane dane muszą być w jakiś sposób połączone, a wiersze faktycznie muszą zostać przełączone na „usunięte”.
Jeroen Mostert,
@JeroenMostert dobra rada. Można również użyć RLS lub czegoś takiego jak EF Core Global Query Filters. docs.microsoft.com/en-us/ef/core/querying/filters
David Browne - Microsoft
9

To może być niepopularna opinia, ale nie sądzę, że istnieje „rób to wszędzie” / jeden rozmiar pasuje do wszystkich odpowiedzi na twoje pytanie.

Jeśli masz zapytania, które skanują wiele wierszy IsDeleted bez powodu, jednym z rozwiązań jest utworzenie filtrowanego, nieklastrowanego indeksu w celu spełnienia tego zapytania.

Inną opcją jest utworzenie widoku indeksowanego, który może być wykorzystany przez wiele różnych zapytań, które są filtrowane tylko do nieusuniętych wierszy. Może to być szczególnie przydatne w wersji Enterprise Edition, gdzie automatyczne indeksowane dopasowywanie widoków działa bez podawania NOEXPANDpodpowiedzi.

W przypadku małych tabel lub tabel, które są mocno odczytywane, dodawanie filtrowanych nieklastrowanych indeksów lub widoków lub cokolwiek innego może być po prostu dodawaniem niepotrzebnego narzutu do bazy danych.

Josh Darnell
źródło
2

Przy rozsądnym założeniu, że usuwanie jest rzadkie, brak zmian w indeksach jest właściwym rozwiązaniem.

Odkryłem, że prędzej czy później trzeba zapytać o odniesienia do usuniętych wierszy, a wiersze znajdujące się w indeksach są nagle bardzo tego warte.

Pamiętaj, że o ile nie korzystasz z widoków, musisz edytować wszystkie zapytania, aby i tak uwzględnić filtry.

Jozuego
źródło
0

Widziałem system, w którym flaga IS_DELETED ma wartość 0 lub wartość PK. W innych systemach był to minus PK.

Ponieważ większość zapytań pobierała wartości za pomocą klucza „naturalnego” lub biznesowego (czasem wielozadaniowego), nigdy nie były one pytane przez PK z wyjątkiem połączeń ale zawsze dodawali AND IS_DELETED = 0 na końcu dla głównej tabeli i wszystkich połączonych tabel.

Ten system miał również tabelę kontroli dla każdej tabeli transakcyjnej, która śledziła zmiany; a aplikacja miała funkcję wyświetlania wszystkich zmian danych, w tym usuniętych danych.

Rick Ryker
źródło
0

Mam nadzieję, że masz prawo i możliwość zmiany zapytania.

Ponieważ jednak każde sprzężenie / zapytanie będzie musiało zaimplementować kontrolę IsDeleted, czy uniemożliwiłoby to użycie filtrowanego indeksu (ponieważ kolumna IsDeleted jest używana w sprzężeniu / zapytaniu)?

Chciałem powiedzieć jedną ważną kwestię, mam nadzieję, że uda mi się to wyjaśnić.

W złożonym zapytaniu, gdzie Transaction tablei Mastertabele są używane.

Używaj IsDeleted=0tylko w Transactiontabeli. Nie używać w Mastertabeli.

Przykład,

Select * from dbo.Order O
inner join dbo.category C on o.categoryid=o.categoryid
inner join dbo.Product P on P.Productid=o.Productid
where o.isdeleted=0

Nie ma sensu c.isdeleted=0(używanie w Categorytabeli). Nie jest to konieczne.

Podobnie, czy warto używać P.isdeleted=0?

Ponieważ chcę całego nieskasowanego Porządku i ich szczegółów.

Jak można Productusunąć, kiedy Orderjest Activelub gdziekolwiek Productidjest odwołanie.

W ten sposób, jeśli ostrożnie debugujesz w ważnym zapytaniu, być może możesz usunąć część isdeleted = 0.

Nie twórz na ślepo indeksu filtrowanego, najpierw wybierz wszystkie bardzo ważne i powolne zapytania.

Zoptymalizuj te powolne zapytania, a następnie zdecyduj tylko o Filtrowanym indeksie lub dostosuj indeks.

KumarHarsh
źródło