Я хочу создать уникальное индексированное представление так, чтобы, если любые два ненулевых Location
были назначены одному и тому же Id
, это нарушало бы индекс и не позволяло их.
Я начал с моегосмотреть как
-- This is what I wish I could do but its wrong
SELECT Id, Location, COUNT(*) as Count, COUNT(Location) as LocationCount
FROM #X
GROUP BY Id, Location
ORDER BY Id, Location
Что не работает, поскольку существуют местоположения с одним идентификатором, которые имеют NULL-местоположение и ненулевое расположение, которое является действительным на основе бизнес-правил, но дублирует Id на основе группировки.
Это демонстрирует:
DROP TABLE IF EXISTS #X
SELECT *
INTO #X
FROM (
SELECT 1 as Id, 1 as Location
UNION
Select 1 as Id, NULL as Location
UNION
Select 2 as Id, NULL as Location
UNION
Select 3 as Id, 2 as Location
) X
-- This is what I wish I could do but its wrong
SELECT Id, Location, COUNT(*) as Count, COUNT(Location) as LocationCount
FROM #X
GROUP BY Id, Location
ORDER BY Id, Location
Результат:
Желаемый:
Поскольку идентификатор дублирован, его нельзя сделать индексированным представлением.
То, что я хочу, можно сделать, просто используя AVG, MAX или что-то в этом роде.игнорирует NULL:
SELECT Id, AVG(Location) as Location, COUNT(*) as Count, COUNT(Location) as LocationCount
FROM #X
GROUP BY Id
ORDER BY Id, Location
Но это не работает, потому что AVG
недопустимо в индексированном представлении, а также потому, что его группировка только по Id
, поэтому, если два ненулевых значения были назначены одному идентификатору,такие как 6 и 8, он будет возвращать среднее значение обоих (7) вместопозволяя это.
Поэтому я пришел к мысли, что могу создать два уникальных представления: одно для нулевых значений, а другое для ненулевых значений.затем создайте третий вид, который объединяет их на основе желаемой логики.Это работает, но это кажется обходным процессом для выполнения того, что я хочу, и требует больше накладных расходов и вычислений / обработки при использовании представления, но это лучше, чем отсутствие индексированных представлений:
;WITH IndexableViewNonNulls AS (
SELECT Id, Location, COUNT(*) as Count
FROM #X
WHERE Location IS NOT NULL
GROUP BY Id, Location
), IndexableViewNulls AS (
SELECT Id, Location, COUNT(*) as Count
FROM #X
WHERE Location IS NULL
GROUP BY Id, Location
), CTE AS (
SELECT *, Count as LocationCount
FROM IndexableViewNonNulls
UNION ALL
SELECT *, 0 as LocationCount
FROM IndexableViewNulls
)
SELECT
Id
,AVG(Location) as Location
,SUM(Count) as Count
,SUM(LocationCount) as LocationCount
FROM CTE
GROUP BY Id
Вот полная скрипка: https://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=f3df7c4b5703e9270ea1efe9bef5c152
Редактировать: Вот еще одна скрипка с индексированным представлением на месте.Обратите внимание, что вставка NULL нарушает уникальный индекс, но я не хочу этого.Я хочу, чтобы он нарушал, если все два ненулевых объекта все время сохраняют вид одинаковым (Местоположение показывает NULL, если ВСЕ равно NULL, местоположение, если есть 1 или более местоположений)
https://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=544b5983bf25cd9c2ff1f55d68bd34d8
Редактировать 2: Вот мое решение, использующее 2 индексированных представления и 1 нормальное представление для их объединения: https://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=b8cf5ff638305a0d228c856b6391246d
Редактирование 3: Таблица #X не является обычной таблицей, а просто добавлениеИндекс в таблице не является допустимым решением.В реальном сценарии таблица #X представляет собой само представление, поэтому решение должно использовать индексированные представления