Убедитесь, что значение может существовать только один раз в столбце для другого столбца. - PullRequest
1 голос
/ 16 октября 2019

У нас есть таблица, подобная приведенной ниже:

=# CREATE TABLE items (item_id INT NOT NULL, revision VARCHAR(2) NOT NULL, is_newest_rev BOOLEAN NOT NULL, UNIQUE (item_id,revision));

=# SELECT * FROM items;
 item_id | revision | is_newest_rev
---------+----------+---------------
     250 |       1A | f
     250 |       1B | t
     199 |       1F | t
      40 |       1A | f     <-note this, false
      40 |       1B | f     <-note this, false
      40 |       1C | t     <-note this, true

Таблица выше действительна.

Следующая таблица недействительна:

=# SELECT * FROM items;
 item_id | revision | is_newest_rev
---------+----------+---------------
     250 |       1A | f
     250 |       1B | t
     199 |       1F | t
      40 |       1A | f     <-note this, false
      40 |       1B | f     <-note this, false
      40 |       1C | t     <-note this, true
      40 |       1D | t     <-ERROR! 40 | 1C is already marked as true!

Мне нужносоздать ограничение для проверки запроса на вставку до его применения к таблице;чтобы проверить, что существует только один экземпляр t на item_id и revision.

Итак, если бы я попытался запустить INSERT INTO items VALUES (40, '1D', 't');, он потерпел бы неудачу, потому что 40, 1C уже помечен какt. Если бы все ревизии 40 были помечены как f, ТОГДА это сработало бы. Надеюсь, что это имеет смысл.

Извините за неправильное название, мне было трудно назвать вопрос.

Ответы [ 3 ]

3 голосов
/ 16 октября 2019

Это можно исправить с помощью частичного уникального индекса .

Рассмотрим:

CREATE UNIQUE INDEX items_custom 
    ON items (item_id, is_newest_rev)
    WHERE (is_newest_rev = 't');

Демонстрация на DB Fiddle :

CREATE TABLE items (
    item_id INT NOT NULL, 
    revision VARCHAR(2) NOT NULL, 
    is_newest_rev BOOLEAN NOT NULL, 
    UNIQUE (item_id,revision)
);

CREATE UNIQUE INDEX items_custom 
    ON items (item_id, is_newest_rev)
    WHERE (is_newest_rev = 't');

insert into items values(40, '1A', 't');
insert into items values(40, '1B', 'f');
insert into items values(40, '1C', 't');

ERROR:  duplicate key value violates unique constraint "items_custom"
DETAIL:  Key (item_id, is_newest_rev)=(40, t) already exists.
1 голос
/ 16 октября 2019

Рассчитать (не хранить) состояние приложения

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

Рассмотрите возможность использования последовательных данных вместо попытки обновить состояние.

Учтите, что событие даже отмена - это новая ревизия.

 item_id | revision 
---------+----------
     250 |       1
     250 |       2
     199 |       1
      40 |       1
      40 |       2
      40 |       3

Или добавьте метки времени для каждой ревизии, как показано ниже.

 item_id | revision | timestamp
---------+----------+-------------------------
     250 |       1  | 2019-10-16 07:22:00-05

Затем создайте таблицу следующим образом:

 CREATE TABLE items (item_id INT NOT NULL, revision INT NOT NULL, *...*, UNIQUE (item_id,revision));

Выберите, используя что-то вродеthis:

 SELECT * FROM items ORDER BY revision DESC LIMIT 1

Другой вариант - проверить состояние до и после вставки.

Состояние проверки вручную

Вы можете указать, что item_id и revision поля UNIQUE , но это не то, что вы хотите. Похоже, вы хотите, чтобы поле is_newest_rev дублировалось для true значений.

Я считаю, что вы должны написать оператор SELECT или COUNT и проверьте его значение перед выполнением вставки. Вероятно, вы захотите сделать это и после слов, если несколько процессов одновременно обращаются к базе данных.

SELECT COUNT(*) FROM items WHERE item_id = 40 AND is_newest_rev = 't';

Если счет>> 1, не вставляйте. Но, кроме того, я не верю, что большинство SQL допускают тип ограничений сущностей, которые вы ищете.

0 голосов
/ 16 октября 2019

Вы можете использовать триггер, как показано ниже

       Create trigger check before  Insert 
        On table for each row as
         If(Select count(*) from (Select 
           item_id,  
         count(distinct is_newest_rev)
         From table where 
        item_id=:new.item_id group by 
          item_id,
           having count(distinct 
            is_newest_rev)=2)>0
         Then
          //dont insert

        Else
          {Insert into......} 
        End if
        End
...