用于存储历史数据和每年报告数据的数据库结构
Database structure for storing historical data and reporting on data annually
我正在使用 Oracle 数据库。我有一个让我们说字符串的系统。不像数据类型字符串,而是像小绳子一样的字符串。
字符串的开始和结束可能会随着时间的推移而改变。必须保留这些更改的历史记录。报告每年进行一次。用户想查看 2016 年、2017 年、2018 年等字符串的开头和结尾是什么。
一个字符串每年可以更改几次或 2/3 年只能更改一次。用户只对特定年份的最新变化感兴趣。 2016 年或 2017 年等的有效开始和结束时间是什么?
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;
很容易得到当前的字符串大小:
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
用户只需要 select 字符串的类型和年份。
尺寸必须显示在 selected 年的报告中。
一个非常简单的方法就是添加一个 string_year。所以每年每个字符串都会有一个记录。但这会给你带来 2016 年和 2017 年的重复,因为 2017 年没有任何变化。
这显然是对系统的一个非常简单的解释。 "strings" 也有很多不同的属性等。但这对这个问题并不重要。
你会如何处理这样的事情?您的数据库设计是什么?
如果您一次只查找一年,您可以这样做:
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
或:
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
在这两种情况下,您都排除了下一年开始之前的所有数据,然后查找具有最新创建日期的行的值。
以下查询可用于查看某个字符串活跃的年份中的所有字符串数据:
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
string_years CTE 列出了字符串处于活动状态的所有年份,即使它没有事件。活跃年份是字符串的第一个事件和最后一个事件之间的所有年份。然后我们将其加入到我们的年度提取中,按 STRINGID 和 YEAR 对聚合函数进行分组。最后,如果某行为空,我们使用 LAG 函数获取前一年的值。
如果您要将此查询限制为特定字符串或字符串类型,在 STRING_YEARS
之前创建另一个 CTE 以在整个其余部分中代替 stringlist 可能不会有什么坏处查询。
我正在使用 Oracle 数据库。我有一个让我们说字符串的系统。不像数据类型字符串,而是像小绳子一样的字符串。
字符串的开始和结束可能会随着时间的推移而改变。必须保留这些更改的历史记录。报告每年进行一次。用户想查看 2016 年、2017 年、2018 年等字符串的开头和结尾是什么。 一个字符串每年可以更改几次或 2/3 年只能更改一次。用户只对特定年份的最新变化感兴趣。 2016 年或 2017 年等的有效开始和结束时间是什么?
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;
很容易得到当前的字符串大小:
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
用户只需要 select 字符串的类型和年份。 尺寸必须显示在 selected 年的报告中。
一个非常简单的方法就是添加一个 string_year。所以每年每个字符串都会有一个记录。但这会给你带来 2016 年和 2017 年的重复,因为 2017 年没有任何变化。
这显然是对系统的一个非常简单的解释。 "strings" 也有很多不同的属性等。但这对这个问题并不重要。
你会如何处理这样的事情?您的数据库设计是什么?
如果您一次只查找一年,您可以这样做:
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
或:
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
在这两种情况下,您都排除了下一年开始之前的所有数据,然后查找具有最新创建日期的行的值。
以下查询可用于查看某个字符串活跃的年份中的所有字符串数据:
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
string_years CTE 列出了字符串处于活动状态的所有年份,即使它没有事件。活跃年份是字符串的第一个事件和最后一个事件之间的所有年份。然后我们将其加入到我们的年度提取中,按 STRINGID 和 YEAR 对聚合函数进行分组。最后,如果某行为空,我们使用 LAG 函数获取前一年的值。
如果您要将此查询限制为特定字符串或字符串类型,在 STRING_YEARS
之前创建另一个 CTE 以在整个其余部分中代替 stringlist 可能不会有什么坏处查询。