Обеспечение связи двух значений столбца в SQL Server - PullRequest
1 голос
/ 30 мая 2019

Я использую Microsoft SQL Server 2017 и мне было интересно узнать, как ограничить определенные отношения.У меня возникли проблемы с формулировкой, поэтому я бы хотел поделиться с вами примером.

Рассмотрим следующую гипотетическую базу данных.


Customers
+---------------+
|  Id  |  Name  |
+---------------+
|  1   |  Sam   |
|  2   |  Jane  |
+---------------+
Addresses
+----------------------------------------+
|  Id  |  CustomerId  |  Address         |
+----------------------------------------+
|  1   |  1           |  105 Easy St     |
|  2   |  1           |  9 Gale Blvd     |
|  3   |  2           |  717 Fourth Ave  |
+------+--------------+------------------+
Orders
+-----------------------------------+
|  Id  |  CustomerId  |  AddressId  |
+-----------------------------------+
|  1   |  1           |  1          |
|  2   |  2           |  3          |
|  3   |  1           |  3          |  <--- Invalid Customer/Address Pair
+-----------------------------------+

Обратите внимание, что последний Order связывает клиента с адресом, который ему не принадлежит.Я ищу способ предотвратить это.

(Вы можете спросить, зачем мне вообще нужен CustomerId в таблице Orders. Чтобы было ясно, я понимаю, что Address уже предлагает мне ту же информацию без возможности использования недопустимых пар. Однако я бы предпочел, чтобы Order сглаживался так, чтобы мне не приходилось направлять через адрес для поиска клиента.)

Из связанного с этим чтения, которое я смог найти, может показаться, что одним из методов может быть включение ограничения CHECK для пользовательской функции.Эта определяемая пользователем функция будет выглядеть примерно так:

WHERE EXISTS (SELECT 1 FROM Addresses WHERE Id = Order.AddressId AND CustomerId = Order.CustomerId)

Хотя я думаю, что это сработает, учитывая некоторую "общность" статей, которые я смог найти, яя не совсем уверен, что это мой лучший вариант.

Альтернативой может быть полное удаление столбца CustomerId из таблицы Addresses и добавление другой таблицы с Id, * 1032.*, AddressId.Order будет ссылаться на это Id.Опять же, мне не нравится идея проходить через вспомогательную таблицу, чтобы получить Customer или Address.

Есть ли более чистый способ сделать это?Или я просто ошибаюсь?

Ответы [ 2 ]

1 голос
/ 30 мая 2019

Вы можете спросить, зачем мне вообще нужно CustomerId в таблице Orders. Чтобы было ясно, я признаю, что Address уже предлагает мне ту же информацию без возможности использования недопустимых пар. Тем не менее, я бы предпочел, чтобы Order сглаживался так, чтобы мне не приходилось направлять через адрес для поиска клиента.

Если вы столкнулись с проблемами с производительностью, в первую очередь необходимо создать или изменить правильные индексы. А СУБД обычно хороши в операциях соединения (с правильными индексами). Но да, нормализация иногда может помочь в настройке производительности. Но это должно быть последним средством. И если этот путь выбран, нужно действительно знать, что он делает, и быть очень осторожным, чтобы не повредить больше в конце дня, который он получил. У меня есть сомнения, что у вас нет вариантов здесь, и вам действительно нужно идти по этому пути. Вы, вероятно, лаете не на то дерево. Поэтому я рекомендую вам выбрать «нормальный», «вменяемый» путь, просто добавить customerid в orders и создать правильные индексы.

Но если вы действительно настаиваете, вы можете попытаться сделать (id, customerid) ключом addresses (с уникальным ограничением), а затем создать внешний ключ на основе этого.

ALTER TABLE addresses
            ADD UNIQUE (id,
                        customerid);

ALTER TABLE orders
            ADD FOREIGN KEY (addressid,
                             customerid)
                            REFERENCES addresses
                                       (id,
                                        customerid);
1 голос
/ 30 мая 2019

Хороший вопрос, однако в корне кажется, что вы боретесь с созданием ограничения внешнего ключа для чего-то, что не является внешним ключом:

Orders.CustomerId -> Addresses.CustomerId

Нет простого встроенного способа сделать это, потому что обычно это не делается. В идеальных методах СУБД вы должны стремиться инкапсулировать данные определенных типов в свои собственные таблицы только . Другими словами, старайтесь избегать избыточных данных.

В приведенном выше примере владение адресом является избыточным как в таблице адресов, так и в таблице заказов, поэтому для их синхронизации требуются дополнительные проверки. Это может легко выйти из-под контроля с большими наборами данных.

Вы упомянули:

Однако я бы предпочел, чтобы Заказ был сведен таким образом, чтобы мне не приходилось направлять адрес для поиска клиента.

Но именно поэтому реляционная база данных является реляционной. Это делается для того, чтобы отдельные данные можно было хранить отдельно и ссылаться на относительные идентификаторы.

Я думаю, что лучшим решением было бы просто отменить это требование.

Другими словами, просто наберите:

Customers
+---------------+
|  Id  |  Name  |
+---------------+
|  1   |  Sam   |
|  2   |  Jane  |
+---------------+
Addresses
+----------------------------------------+
|  Id  |  CustomerId  |  Address         |
+----------------------------------------+
|  1   |  1           |  105 Easy St     |
|  2   |  1           |  9 Gale Blvd     |
|  3   |  2           |  717 Fourth Ave  |
+------+--------------+------------------+
Orders
+--------------------+
|  Id  |  AddressId  |
+--------------------+
|  1   |  1          |
|  2   |  3          |
|  3   |  3          |  <--- Valid Order/Address Pair
+--------------------+

С учетом вышесказанного, для точного достижения цели у вас есть просмотров доступных для такого рода вещей:

create view CustomerOrders
as

select  o.Id OrderId,
        a.CustomerId,
        o.AddressId
from    Orders
join    Addresses a on a.Id = o.AddressId

Я знаю, что это довольно тривиальный вариант использования для представления, но я хотел вставить в него заглушку, потому что им часто пренебрегают и они пригодятся при организации больших наборов данных. Используя WITH SCHEMABINDING, их также можно индексировать для повышения производительности.

...