Использование sql EXCEPT для создания хранилища фактов EAVT - PullRequest
0 голосов
/ 26 марта 2020

Я изучаю базу данных datomi c и при этом имею go для того, чтобы взять некоторые из ее идей и реализовать их в sql в пошаговом режиме, чтобы настроить к новым способам моделирования данных. Этот вопрос действительно полностью касается SQL, но я просто упомяну это для справки, чтобы объяснить почему того, что я здесь делаю (хотя может быть интересно и для тех, кто интересуется datomi c тоже, именно поэтому я также добавил в вопрос тег datomi c).

Обычно мы избавляемся от отдельных таблиц для каждого типа, но я буду сохранять таблицу пользователей для этого примера, а не просто использовать Таблица Entities (может попробовать это позже, но пока нет).

create table users (
  id uuid,
  identity text  -- e.g. 'the yankees', 'man born as john in birmingham on date x/y/z'
);

Затем у нас есть хранилище EAVT, также с логическим значением added для указания добавления или отвода. Эта таблица только для добавления Мы никогда не будем выпускать обновления или удалять его.

create table eavt_log (
  user_id uuid,
  attribute text,
  value text,
  added boolean,
  created_at timestamp
);

Теперь некоторые данные, иллюстрирующие использование, предназначены

-- insert person number 12345 (imagine as national identity or birth certificate no.)
insert into users(id, identity) values (uuid_generate_v4(), 'p-12345');

-- lets insert some facts about a person previously known as john smith:
insert into eavt_log(user_id, attribute, value, added, created_at) values
  ((select id from users where identity='p-12345'),
     'name', 'John Smith', true, '1911-01-01'),
  ((select id from users where identity='p-12345'),
     'name', 'John Smith', false, '1931-01-01'),
  ((select id from users where identity='p-12345'),
     'name', 'John Bontine Smith', true, '1931-01-01');

Чтобы сделать это полезным (любая база данных должна обеспечивать кредитное плечо , как говорит Хикки), давайте попробуем найдите все невнятные имена для человека, ранее известного как Джон Смит.

Вот моя (плохая) попытка

-- find all currently unretracted names for person previously known as John Smith. This could
-- be 0, 1 (we hope), or more - it just depends though, and should, on what data has been input.
(select attribute, value from eavt_log
  where user_id = (select id from users where identity='p-12345')
  and attribute = 'name'
  and added = true
  order by created_at desc) -- <- can sneak this in w/o upsetting the except, as it's not in the select.
except
(select attribute, value from eavt_log
  where user_id = (select id from users where identity='p-12345')
  and attribute = 'name'
  and added = false);

Это дает:

 attribute |       value        
-----------+--------------------
 name      | John Bontine Smith
(1 row)

Что правильно для тестовых данных, которые мы предоставили.

Затем мы можем попытаться обобщить до

create view unretracted as (
(select user_id, attribute, value from eavt_log
  where added = true
  order by created_at)
except
(select user_id, attribute, value from eavt_log
  where added = false)
);

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

  ((select id from users where identity='p-12345'),
     'name', 'John Smith', false, '1941-01-01');

к фактам, вставленным выше, чтобы обозначить, что человек-12345 в 1941 году снова принял имя Джона Смита ( без , убирающего имя «Джон Бонтин Смит» ', поэтому в этом случае мы хотим , чтобы система возвратила два значения для его имени).

С этими данными ранее убирается этого идентичного значения приведет к тому, что позднее повторное утверждение того же значения будет исключено из результирующего набора, даже если оно было повторно подтверждено, из-за способа работы EXCEPT (мы не делали линейное сканирование таблицы, которое, я думаю, здесь может потребоваться?)

Мой вопрос (наконец-то!) - есть ли способ добиться этого в SQL? Может ли SQL дать нам больше рычагов здесь?

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

1 Ответ

1 голос
/ 26 марта 2020

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

Вы можете использовать DISTINCT ON в postgres, чтобы получить последнее значение для пользователя. Если вы используете это в дополнительном выборе, вы можете выбрать только те строки, для которых добавлено = true:

SELECT attribute, value 
FROM (
    SELECT distinct on (eavt_log.user_id, attribute, value)
           attribute, value, added
    FROM eavt_log
    JOIN users ON eavt_log.user_id = users.id
    WHERE attribute = 'name'
    ORDER BY eavt_log.user_id, attribute, value, created_at desc) sub
WHERE added = 't';

Редактировать: Вот скрипка

...