Poszukiwania rozwiązania wiązania bez powiązania
Wyobraźmy sobie, że mamy jakiś zestaw zmiennych, którym chcielibyśmy nadawać wartości w zależności od kategorii, która je opisuje. Jednocześnie – pomimo przynależności do tej kategorii – jeżeli wartość zmiennej dla owej kategorii nie została zdefiniowana – chcielibyśmy, aby taka zmienna używała wartości domyślnej. Zakładamy też, że wartości domyślne posiadają wszystkie zmienne i jeśli zajdzie potrzeba uzależnienia wartości zmiennej od posiadanej przez nią kategorii, nie ma to oznaczać usuwania wartości domyślnej. Ona ma sobie cierpliwie czekać na ewentualny powrót do łask, gdy wartość w kontekście kategorii przestanie istnieć. Koniec końców chcielibyśmy przechowywać listę zmiennych i ich kategorii w dedykowanej tabeli serwera SQL (Variables), a powiązane z nimi, kategoryzowane wartości, w innej tabeli (Values).
Analizując to zagadnienie, dobrym rozwiązaniem, wydaje się być wypełnienie tabeli Values wartościami domyślnymi dla wszystkich zmiennych i nadaniu im kategorii pustej (null). Wówczas, jeśli dla jakiejś zmiennej zostanie ustalona wartość w kontekście posiadanej przez nią kategorii – do tabeli Values zostanie dodany kolejny wpis oprócz wpisu z kategorią pustą. Trzeba jedynie od tego momentu rozpoznawać fakt występowania takiej wartości i używać właśnie jej, a nie tej dla kategorii pustej. Jak to zrobić? Nie wydaje się to takie proste, bo złączenie dwóch tabel oczekuje konkretnych wartości (zatem dla domyślnych wiązanie nie zachodzi), a dodatkowo może być tak, że dana zmienna ma dwa warianty swojej wartości: domyślny i konkretny, i w takim wypadku należy podawać konkretny ignorując domyślny.
Zacznijmy może od stworzenia stosownych tabel. Najpierw tabelę zmiennych (na potrzeby wygodnego testowania rozwiązania używam tabel tymczasowych)
if object_id('tempdb..#Variables') is not null drop table #Variables; create table #Variables (id int identity(1, 1), variable varchar(10), category varchar(10)); insert into #Variables values ('v1', 'category1'), ('v2', 'category1'), ('v3', 'category1'); select * from #Variables;
id | variable | category |
---|---|---|
1 | v1 | category1 |
2 | v2 | category1 |
3 | v3 | category1 |
Teraz tabela wartości.
if object_id('tempdb..#Values') is not null drop table #Values; create table #Values (id int identity(1, 1), variable varchar(10), category varchar(10), value varchar(10)); insert into #Values values ('v1', null, 'A'), ('v2', null, 'B'), ('v3', null, 'C'), ('v1', 'category1', 'X'), ('v2', 'category1', 'Y'); select * from #Values;
id | variable | category | value |
---|---|---|---|
1 | v1 | NULL | A |
2 | v2 | NULL | B |
3 | v3 | NULL | C |
4 | v1 | category1 | X |
5 | v2 | category1 | Y |
Pora na zapytanie, które umożliwi rozróżnienie czy wybierać wartość domyślną czy konkretną.
with ExactCategory as ( select V.id, V.variable, VV.value, VV.category from #Variables V inner join #Values VV on VV.variable = V.variable and V.category = VV.category ) select id, variable, value, category from ExactCategory union select V.id, V.variable, VV.value, VV.category from #Variables V inner join #Values VV on VV.variable = V.variable where VV.category is null and V.variable not in (select variable from ExactCategory) order by variable;
id | variable | value | category |
---|---|---|---|
1 | v1 | X | category1 |
2 | v2 | Y | category1 |
3 | v3 | C | NULL |
Rozwiązanie opiera się tutaj na powiązaniu w zapytaniu wewnętrznym wszystkich wartości konkretnych z ich zmiennymi, a następnie powiązania wszystkich zmiennych z wartościami domyślnymi z wyłączeniem zmiennych, które już otrzymały konkretne, dedykowane wartości. Z pomocą przyszło tutaj Common Table Expression, bez którego zapytanie nie byłoby tak czytelne.
Jak widać dla zmiennych v1 i v2 wybrano dedykowane wartości, zaś dla zmiennej v3 użyta została wartość domyślna.