Способ обеспечить уникальность в нескольких строках или изменить дизайн так, чтобы он не был необходим? - PullRequest
0 голосов
/ 20 апреля 2020

Я пытаюсь смоделировать "серийные номера, присвоенные вещам". Я хочу трактовать серийные номера как состоящие из меньших единиц серий цифр или символов - поэтому такой серийный номер, как «AB12CD», будет храниться как тройной ("AB", 12, "CD") :: (varchar, int, varchar). Серийные номера могут содержать любое количество компонентов (хотя вероятные значения находятся в диапазоне от 2 до 6), и компоненты всегда будут соответствовать (\d+)|([a-z]+). Цель здесь состоит в том, чтобы помочь запросам, таким как «все, где компонент N находится между i и j», без необходимости прибегать к манипуляциям со строками в каждой строке (разделение на некоторый разделитель, разбор целых, отслеживание порядковых номеров).

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

thing_id | component | char_part | num_part
------------------------------------------
1        | 1         | "AB"      | null
1        | 2         | null      | 12
2        | 1         | "AB"      | null
2        | 2         | null      | 12

Таким образом, в приведенной выше таблице как для вещи 1, так и для вещи 2 концептуально присвоен серийный номер "AB12". Индексированное представление с уникальным ограничением решило бы эту проблему, но мне пришлось бы использовать CTE для рекурсивного присоединения к порядковым номерам, а CTE в индексированных представлениях невозможны. Если бы я мог сделать CTE в индексированном представлении, я смог бы проиндексировать следующее и применить уникальное ограничение к «SN», которое будет нарушено, и жизнь будет хорошей:

thing_id | SN
------------------
1        | "AB12"
2        | "AB12"

Есть ли шаблон, который может быть рекомендован для этого, или способ обеспечить «многорядную уникальность»? Я надеюсь, что мне не придется хранить две таблицы вручную в syn c (одну с комбинированными строковыми значениями, одну с нарезанными компонентами).

Update / answer-i sh

Я удалил столбцы char_part и num_part и переместил их содержимое в узлы XML в столбце XML, а затем создал постоянный вычисляемый столбец, построенный из элементов XML. Теперь на серийный номер имеется только одна строка - столбец XML содержит произвольное количество компонентов, а к вычисляемому столбцу применяется ограничение уникальности.

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

1 Ответ

0 голосов
/ 21 апреля 2020

Я начал с этого, зацикливался на себе и получал больше вопросов, чем ответов. Прости за это. Публикация кого-либо, поскольку «внешнее обсуждение» может помочь вам найти правильное решение вашей проблемы.

Исходя из того, что вы сказали, я вижу, что это проблема нормализации. Решая эту проблему, я придумываю следующее, что, возможно, не совсем то, что вы ищете. Составление имен как I go вдоль…

CREATE TABLE dbo.Component
 (
    Component   varchar(10)  not null  primary key
 )

Пример данных в таблице Компонент:

AB
CD
12
34

(я не уверен, как это соотносится с вашими char_part и num_part значений; я работаю из предположения, что каждый компонент имеет уникальный идентификатор. Если у вас разные идентифицирующие коды для разных типов компонентов, то у вас гораздо более сложная проблема.)

Далее таблица Вещи, которые, хотя и собраны / собраны из Компонентов, по-прежнему являются собственными, и, следовательно, требуют собственной нормализованной таблицы «одна вещь на строку»:

CREATE TABLE dbo.Thing
 (
   ThingId  int  not null  identity(1,1)  primary key
 )

Образцы данных:

1
2
3
4

Далее, что связывает вещи с компонентами? Таблица «многие ко многим»!

CREATE TABLE dbo.ThingComposition
 (
    ThingId     int          not null
   ,Component   varchar(10)  not null
   ,primary key clustered (ThingId, Component)
 )

С данными

1, AB
1, 12
2, AB
2, 23
3, AB
4, CD
4, 12
4, 34

Эта структура или что-то похожее должно поддерживать ваш поиск по требованиям срезов

Приведенные выше данные приводят к серийным номерам, таким как:

1   AB12
2   AB23
3   AB
4   CD1234

Однако я не могу придумать ничего разумного * и декларативного в пределах SQL, которое не дает вам иметь в таблице ThingComposition следующее:

1, AB
1, 12
5, AB
5, 12

Многое также зависит от того, как вы строите свои серийные номера из базовых кодов компонентов, что не ясно из вашего описания. Являются ли «AB12» и «12AB» действительными серийными номерами? Возникают дальнейшие проблемы - как обеспечить уникальность серийного номера при добавлении, обновлении и удалении строк в нескольких таблицах с течением времени? Если у вас есть «AB12», как вы добавляете «AB1234», если вам нужно добавлять элементы по одной строке за раз? Вы «строите» Вещи, а затем немного переворачиваете (IsLive), говоря «сделано, теперь применяйте уникальность»? (Может быть сделано - отфильтрованные уникальные индексы.) Меняются ли серийные номера (компоненты в одной вещи) с течением времени? Разум поражает.

Я собирался предложить

CREATE TABLE Thing
 (
   ThingId       int           not null  identity(1,1)  primary key
  ,SerialNumber  varchar(100)  not null  --  enough characters?
 )

с уникальным индексом SerialNumber и тщательно сконструированным кодом приложения, чтобы создать и заполнить его правильным SerialNumber, но да, это получается сложный быстрый. У меня появилось много открытых вопросов, и я не могу на них ответить. Надеюсь, кто-то еще придумает лучшую идею!

* Триггеры не являются разумными. Не используйте триггеры.

...