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

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

Начало и конец строки могут меняться с годами. История этих изменений должна быть сохранена. Отчетность составляется ежегодно. Пользователь хочет увидеть, каким было начало и конец строки в 2016, 2017, 2018 и т. Д. c. Строка может меняться пару раз в год или только один раз в 2/3 года. Пользователь просто интересуется последними изменениями за указанный c год. Каково было действительное начало и конец 2016 или 2017 года и т. Д. c?

CREATE TABLE STRINGLIST
(
  ID            NUMBER,
  STRINGID      NUMBER,
  STRING        VARCHAR2(7 BYTE),
  STRING_START  NUMBER,
  STRING_END    NUMBER,
  CREATE_DATE   DATE,
  CREATED_BY    VARCHAR2(10 BYTE),
  EXPIRE_DATE   DATE,
  EXPIRED_BY    VARCHAR2(10 BYTE)
)

Insert into PMS2020.STRINGLIST
   (ID, STRINGID, STRING, STRING_START, STRING_END, 
    CREATE_DATE, CREATED_BY, EXPIRE_DATE, EXPIRED_BY)
 Values
   (1, 1, 'string1', 0, 5, 
    TO_DATE('02/02/2015 12:31:00', 'MM/DD/YYYY HH24:MI:SS'), 'user1', TO_DATE('04/02/2016 12:31:00', 'MM/DD/YYYY HH24:MI:SS'), 'user1');
Insert into PMS2020.STRINGLIST
   (ID, STRINGID, STRING, STRING_START, STRING_END, 
    CREATE_DATE, CREATED_BY, EXPIRE_DATE, EXPIRED_BY)
 Values
   (2, 1, 'string1', 0.1, 5, 
    TO_DATE('04/02/2016 12:31:00', 'MM/DD/YYYY HH24:MI:SS'), 'user1', TO_DATE('02/02/2016 12:31:00', 'MM/DD/YYYY HH24:MI:SS'), 'user1');
Insert into PMS2020.STRINGLIST
   (ID, STRINGID, STRING, STRING_START, STRING_END, 
    CREATE_DATE, CREATED_BY, EXPIRE_DATE, EXPIRED_BY)
 Values
   (3, 1, 'string1', 0.12, 5.2, 
    TO_DATE('04/02/2016 12:31:00', 'MM/DD/YYYY HH24:MI:SS'), 'user1', TO_DATE('04/02/2018 12:31:00', 'MM/DD/YYYY HH24:MI:SS'), 'user1');
Insert into PMS2020.STRINGLIST
   (ID, STRINGID, STRING, STRING_START, STRING_END, 
    CREATE_DATE, CREATED_BY, EXPIRE_DATE, EXPIRED_BY)
 Values
   (4, 1, 'string1', 0.5, 6, 
    TO_DATE('04/02/2018 12:32:00', 'MM/DD/YYYY HH24:MI:SS'), 'user1', TO_DATE('04/02/2019 12:32:00', 'MM/DD/YYYY HH24:MI:SS'), 'user1');
Insert into PMS2020.STRINGLIST
   (ID, STRINGID, STRING, STRING_START, STRING_END, 
    CREATE_DATE, CREATED_BY)
 Values
   (5, 1, 'string1', 0.75, 6.5, 
    TO_DATE('04/02/2019 12:32:00', 'MM/DD/YYYY HH24:MI:SS'), 'user1');
COMMIT;

String List Table

Текущий размер строки легко получить:

select *
from stringlist
where stringid = 1
and expiry_date is null;

Как будет выглядеть запрос, чтобы получить последний размер строки для:

2015 0,1 - 5

2016 0,12 - 5,2

2017 0,12 - 5,2 ( в 2017 году не было внесено никаких изменений, поэтому значение 2016 года остается в силе)

2018 0,5 - 6

2019 0,75 - 6,5

Пользователь должен будет просто выбрать тип Строка и год. И размер должен отображаться в отчете за выбранный год.

Очень простым способом будет просто добавить string_year. Таким образом, будет запись для каждой строки для каждого года. Но это даст вам дублирование на 2016 и 2017 годы, потому что в 2017 году ничего не изменилось.

Это, очевидно, очень простое c объяснение системы. «Строки» также имеют много различных атрибутов и т. Д. 1036 *. Но это не важно для этого вопроса.

Как вы справитесь с чем-то подобным? Каким будет ваш дизайн БД?

Ответы [ 2 ]

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

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

WITH string_years (stringid, report_year) AS
(
  SELECT s.stringid, y.y
  FROM (SELECT sl.stringid,
               EXTRACT(YEAR FROM min(create_date)) AS MIN_YEAR,
               EXTRACT(YEAR FROM max(create_date)) AS MAX_YEAR
        FROM stringlist sl
        GROUP BY sl.stringid) s
  INNER JOIN (SELECT MIN_YEAR+LEVEL-1 AS Y
              FROM (SELECT EXTRACT(YEAR FROM min(create_date)) AS MIN_YEAR,
                           EXTRACT(YEAR FROM max(create_date)) AS MAX_YEAR
                    FROM stringlist) s
              CONNECT BY LEVEL <= (s.max_year-s.min_year)+1) y ON y.y BETWEEN s.min_year AND s.max_year
)
SELECT sy.stringid, sy.report_year, 
       NVL(i.first_change, LAG(i.first_change) OVER (PARTITION BY sy.stringid ORDER BY sy.report_year)) AS START_INFO,
       NVL(i.last_change, LAG(i.last_change) OVER(PARTITION BY sy.stringid ORDER BY sy.report_year)) AS END_INFO
FROM string_years sy
LEFT JOIN (SELECT sl.stringid, 
                  EXTRACT(YEAR FROM sl.create_date) AS REPORT_YEAR, 
                  MAX(sl.string_start) KEEP (DENSE_RANK FIRST ORDER BY sl.create_date) AS FIRST_CHANGE,
                  MAX(sl.string_end) KEEP (DENSE_RANK LAST ORDER BY sl.create_date) AS LAST_CHANGE
            FROM stringlist sl
            GROUP BY sl.stringid, EXTRACT(YEAR FROM sl.create_date)) i ON sy.stringid = i.stringid AND sy.report_year = i.report_year

Сопровождающий SQLFiddle

В CTE string_years перечислены наши все годы строка активна, даже если у нее нет событий. Активные годы - это все годы между первым и последним событием для строки. Затем мы оставили это в нашей ежегодной выдержке, сгруппировав агрегатные функции по STRINGID и YEAR. Наконец, если строка имеет значение null, мы используем функцию LAG для получения значения за предыдущий год.

Если вы собираетесь ограничить этот запрос определенной c строкой или типом строки, это может не произойти Больно создавать еще один CTE до STRING_YEARS для использования вместо списка строк в остальной части запроса.

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

Если вы ищете один год за раз, вы можете сделать:

select max(string_start) keep (dense_rank last order by create_date) as string_start,
       max(string_end)   keep (dense_rank last order by create_date) as string_end
from stringlist
where create_date < last_day(to_date(to_char(2017), 'YYYY')) + 1;

STRING_START STRING_END
------------ ----------
         .12        5.2

SQL Скрипка

или:

select string_start, string_end
from (
  select string_start, string_end, row_number() over (order by create_date desc) as rn
  from stringlist
  where create_date < last_day(to_date(to_char(2017), 'YYYY')) + 1
)
where rn = 1;

STRING_START STRING_END
------------ ----------
         .12        5.2

SQL Fiddle

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...