- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在研究一些复杂的销售分析,这非常复杂……而且很无聊……
所以对于这个问题,我将使用一个有趣的、甜蜜的比喻:自动售货机。
但是我的实际表格的结构方式相同。
(您可以假设有很多索引、约束等。)
ITEM_TYPE
包含“士力架”、“银河”等行的表,但由于多种原因,我们的表不是这样构造的。
ID
,所有列都可以为空 - 这是一个真正的问题。
NULL
,然后
NULL
是我们需要用于分析和报告的官方值。
CREATE table "VENDING_MACHINES" (
"ID" NUMBER NOT NULL ENABLE,
"SNICKERS_COUNT" NUMBER,
"MILKY_WAY_COUNT" NUMBER,
"TWIX_COUNT" NUMBER,
"SKITTLES_COUNT" NUMBER,
"STARBURST_COUNT" NUMBER,
"SWEDISH_FISH_COUNT" NUMBER,
"FACILITIES_ADDRESS" VARCHAR2(100),
"FACILITIES_CONTACT" VARCHAR2(100),
CONSTRAINT "VENDING_MACHINES_PK" PRIMARY KEY ("ID") USING INDEX ENABLE
)
/
INSERT INTO VENDING_MACHINES (ID, SNICKERS_COUNT, MILKY_WAY_COUNT, TWIX_COUNT,
SKITTLES_COUNT, STARBURST_COUNT, SWEDISH_FISH_COUNT,
FACILITIES_ADDRESS, FACILITIES_CONTACT)
SELECT 225, 11, 15, 14, 0, NULL, 13, '123 Abc Street', 'Steve' FROM DUAL UNION ALL
SELECT 349, NULL, 7, 3, 11, 8, 7, NULL, '' FROM DUAL UNION ALL
SELECT 481, 8, 4, 0, NULL, 14, 3, '1920 Tenaytee Way', NULL FROM DUAL UNION ALL
SELECT 576, 4, 2, 8, 4, 9, NULL, '', 'Angela' FROM DUAL
VENDING_MACHINES
中的记录更新时表,执行触发器将每个单独的更改记录在单独的日志表中
VENDING_MACHINES_CHANGE_LOG
.
VENDING_MACHINES
中修改的每一列记录一个单独的行。表(除了
ID
)。
VENDING_MACHINES
中插入一个全新的行表(即新的自动售货机),将在
VENDING_MACHINES_CHANGE_LOG
中记录八行表 -
VENDING_MACHINES
中的每个非 ID 列对应一个表.
VENDING_MACHINES
的永久历史记录。表,所以我们不会创建外键约束 - 如果从
VENDING_MACHINES
中删除了一行我们希望在变更日志中保留孤立的历史记录。
ON UPDATE CASCADE
(?),所以触发器必须检查
ID
的更新列,并在整个相关表(例如更改日志)中手动传播更新。
CREATE table "VENDING_MACHINE_CHANGE_LOG" (
"ID" NUMBER NOT NULL ENABLE,
"CHANGE_TIMESTAMP" TIMESTAMP(6) NOT NULL ENABLE,
"VENDING_MACHINE_ID" NUMBER NOT NULL ENABLE,
"MODIFIED_COLUMN_NAME" VARCHAR2(30) NOT NULL ENABLE,
"MODIFIED_COLUMN_TYPE" VARCHAR2(30) GENERATED ALWAYS AS
(CASE "MODIFIED_COLUMN_NAME" WHEN 'FACILITIES_ADDRESS' THEN 'TEXT'
WHEN 'FACILITIES_CONTACT' THEN 'TEXT'
ELSE 'NUMBER' END) VIRTUAL NOT NULL ENABLE,
"NEW_NUMBER_VALUE" NUMBER,
"NEW_TEXT_VALUE" VARCHAR2(4000),
CONSTRAINT "VENDING_MACHINE_CHANGE_LOG_CK" CHECK
("MODIFIED_COLUMN_NAME" IN('SNICKERS_COUNT', 'MILKY_WAY_COUNT', 'TWIX_COUNT',
'SKITTLES_COUNT', 'STARBURST_COUNT', 'SWEDISH_FISH_COUNT',
'FACILITIES_ADDRESS', 'FACILITIES_CONTACT')) ENABLE,
CONSTRAINT "VENDING_MACHINE_CHANGE_LOG_PK" PRIMARY KEY ("ID") USING INDEX ENABLE,
CONSTRAINT "VENDING_MACHINE_CHANGE_LOG_UK" UNIQUE ("CHANGE_TIMESTAMP",
"VENDING_MACHINE_ID",
"MODIFIED_COLUMN_NAME") USING INDEX ENABLE
/* No foreign key, since we want this change log to be orphaned and preserved.
Also, apparently Apex doesn't support ON UPDATE CASCADE for some reason? */
)
/
INSERT INTO VENDING_MACHINE_CHANGE_LOG (ID, CHANGE_TIMESTAMP, VENDING_MACHINE_ID,
MODIFIED_COLUMN_NAME, NEW_NUMBER_VALUE, NEW_TEXT_VALUE)
SELECT 167, '11/06/19 05:18', 481, 'MILKY_WAY_COUNT', 5, NULL FROM DUAL UNION ALL
SELECT 168, '11/06/19 05:21', 225, 'SWEDISH_FISH_COUNT', 1, NULL FROM DUAL UNION ALL
SELECT 169, '11/06/19 05:40', 481, 'FACILITIES_ADDRESS', NULL, NULL FROM DUAL UNION ALL
SELECT 170, '11/06/19 05:49', 481, 'STARBURST_COUNT', 4, NULL FROM DUAL UNION ALL
SELECT 171, '11/06/19 06:09', 576, 'FACILITIES_CONTACT', NULL, '' FROM DUAL UNION ALL
SELECT 172, '11/06/19 06:25', 481, 'SWEDISH_FISH_COUNT', 7, NULL FROM DUAL UNION ALL
SELECT 173, '11/06/19 06:40', 481, 'FACILITIES_CONTACT', NULL, 'Audrey' FROM DUAL UNION ALL
SELECT 174, '11/06/19 06:46', 576, 'SNICKERS_COUNT', 13, NULL FROM DUAL UNION ALL
SELECT 175, '11/06/19 06:55', 576, 'FACILITIES_ADDRESS', NULL, '388 Holiday Street' FROM DUAL UNION ALL
SELECT 176, '11/06/19 06:59', 576, 'SWEDISH_FISH_COUNT', NULL, NULL FROM DUAL UNION ALL
SELECT 177, '11/06/19 07:00', 349, 'MILKY_WAY_COUNT', 3, NULL FROM DUAL UNION ALL
SELECT 178, '11/06/19 07:03', 481, 'TWIX_COUNT', 8, NULL FROM DUAL UNION ALL
SELECT 179, '11/06/19 07:11', 349, 'TWIX_COUNT', 15, NULL FROM DUAL UNION ALL
SELECT 180, '11/06/19 07:31', 225, 'FACILITIES_CONTACT', NULL, 'William' FROM DUAL UNION ALL
SELECT 181, '11/06/19 07:49', 576, 'FACILITIES_CONTACT', NULL, 'Brian' FROM DUAL UNION ALL
SELECT 182, '11/06/19 08:28', 481, 'SNICKERS_COUNT', 0, NULL FROM DUAL UNION ALL
SELECT 183, '11/06/19 08:38', 481, 'SKITTLES_COUNT', 7, '' FROM DUAL UNION ALL
SELECT 184, '11/06/19 09:04', 349, 'MILKY_WAY_COUNT', 10, NULL FROM DUAL UNION ALL
SELECT 185, '11/06/19 09:21', 481, 'SNICKERS_COUNT', NULL, NULL FROM DUAL UNION ALL
SELECT 186, '11/06/19 09:33', 225, 'SKITTLES_COUNT', 11, NULL FROM DUAL UNION ALL
SELECT 187, '11/06/19 09:45', 225, 'FACILITIES_CONTACT', NULL, NULL FROM DUAL UNION ALL
SELECT 188, '11/06/19 10:16', 481, 'FACILITIES_CONTACT', 4, 'Lucy' FROM DUAL UNION ALL
SELECT 189, '11/06/19 10:25', 481, 'SNICKERS_COUNT', 10, NULL FROM DUAL UNION ALL
SELECT 190, '11/06/19 10:57', 576, 'SWEDISH_FISH_COUNT', 12, NULL FROM DUAL UNION ALL
SELECT 191, '11/06/19 10:59', 225, 'MILKY_WAY_COUNT', NULL, NULL FROM DUAL UNION ALL
SELECT 192, '11/06/19 11:11', 481, 'STARBURST_COUNT', 6, 'Stanley' FROM DUAL UNION ALL
SELECT 193, '11/06/19 11:34', 225, 'SKITTLES_COUNT', 8, NULL FROM DUAL UNION ALL
SELECT 194, '11/06/19 11:39', 349, 'FACILITIES_CONTACT', NULL, 'Mark' FROM DUAL UNION ALL
SELECT 195, '11/06/19 11:42', 576, 'SKITTLES_COUNT', 8, NULL FROM DUAL UNION ALL
SELECT 196, '11/06/19 11:56', 225, 'TWIX_COUNT', 2, NULL FROM DUAL
VENDING_MACHINES
表,仅使用来自
VENDING_MACHINE_CHANGE_LOG
的数据 table 。
VENDING_MACHINES
中删除的行应该会重新出现。
VENDING_MACHINE
行,就像它在历史上的任何特定点存在一样。
VENDING_MACHINE_CHANGE_LOG
的示例数据很短,不足以产生完整的结果......
CHANGE_TIMESTAMP
排序):
VENDING_MACHINE_ID
排序:
VENDING_MACHINE_ID
提取最新的列值。 ,但我认为这种方法不适合这项艰巨的任务。
select vmcl.ID,
vmcl.CHANGE_TIMESTAMP,
vmcl.VENDING_MACHINE_ID,
vmcl.MODIFIED_COLUMN_NAME,
vmcl.MODIFIED_COLUMN_TYPE,
vmcl.NEW_NUMBER_VALUE,
vmcl.NEW_TEXT_VALUE
from ( select sqvmcl.VENDING_MACHINE_ID,
sqvmcl.MODIFIED_COLUMN_NAME,
max(sqvmcl.CHANGE_TIMESTAMP) as LAST_CHANGE_TIMESTAMP
from VENDING_MACHINE_CHANGE_LOG sqvmcl
where sqvmcl.CHANGE_TIMESTAMP <= /*[Current timestamp, or specified timestamp]*/
group by sqvmcl.VENDING_MACHINE_ID, sqvmcl.MODIFIED_COLUMN_NAME ) sq
left join VENDING_MACHINE_CHANGE_LOG vmcl on vmcl.VENDING_MACHINE_ID = sq.VENDING_MACHINE_ID
and vmcl.MODIFIED_COLUMN_NAME = sq.MODIFIED_COLUMN_NAME
and vmcl.CHANGE_TIMESTAMP = sq.LAST_CHANGE_TIMESTAMP
left join
特别命中
VENDING_MACHINE_CHANGE_LOG
的唯一索引表 - 这是设计使然。
最佳答案
我将忽略我认为这是一个“XY 问题”的感觉,只回答这个问题:
[How do I] Reconstruct historical table rows, based only on change-log data[?]
SELECT
c.id change_id,
c.change_timestamp as_of_timestamp,
c.vending_machine_id,
NULLIF(last_value(case when c.modified_column_name = 'SNICKERS_COUNT' THEN nvl(c.new_number_value,-99999) ELSE NULL END) ignore nulls over ( partition by c.vending_machine_id order by c.change_timestamp asc range between unbounded preceding and current row),-99999) snickers_count,
NULLIF(last_value(case when c.modified_column_name = 'MILKY_WAY_COUNT' THEN nvl(c.new_number_value,-99999) ELSE NULL END) ignore nulls over ( partition by c.vending_machine_id order by c.change_timestamp asc range between unbounded preceding and current row),-99999) MILKY_WAY_COUNT,
NULLIF(last_value(case when c.modified_column_name = 'TWIX_COUNT' THEN nvl(c.new_number_value,-99999) ELSE NULL END) ignore nulls over ( partition by c.vending_machine_id order by c.change_timestamp asc range between unbounded preceding and current row),-99999) TWIX_COUNT,
NULLIF(last_value(case when c.modified_column_name = 'SKITTLES_COUNT' THEN nvl(c.new_number_value,-99999) ELSE NULL END) ignore nulls over ( partition by c.vending_machine_id order by c.change_timestamp asc range between unbounded preceding and current row),-99999) SKITTLES_COUNT,
NULLIF(last_value(case when c.modified_column_name = 'STARBURST_COUNT' THEN nvl(c.new_number_value,-99999) ELSE NULL END) ignore nulls over ( partition by c.vending_machine_id order by c.change_timestamp asc range between unbounded preceding and current row),-99999) STARBURST_COUNT,
NULLIF(last_value(case when c.modified_column_name = 'SWEDISH_FISH_COUNT' THEN nvl(c.new_number_value,-99999) ELSE NULL END) ignore nulls over ( partition by c.vending_machine_id order by c.change_timestamp asc range between unbounded preceding and current row),-99999) SWEDISH_FISH_COUNT,
NULLIF(last_value(case when c.modified_column_name = 'FACILITIES_ADDRESS' THEN nvl(c.new_text_value,'#NULL#') ELSE NULL END) ignore nulls over ( partition by c.vending_machine_id order by c.change_timestamp asc range between unbounded preceding and current row),'#NULL#') FACILITIES_ADDRESS,
NULLIF(last_value(case when c.modified_column_name = 'FACILITIES_CONTACT' THEN nvl(c.new_text_value,'#NULL#') ELSE NULL END) ignore nulls over ( partition by c.vending_machine_id order by c.change_timestamp asc range between unbounded preceding and current row),'#NULL#') FACILITIES_CONTACT
FROM
VENDING_MACHINE_CHANGE_LOG c
ORDER BY
c.vending_machine_id, c.change_timestamp;
基本上,你有三个问题:
null
值(value)观? NEW_NUMBER_VALUE
很简单。例如,
SNICKERS_COUNT
列和使用
NEW_TEXT_VALUE
为
FACILITIES_ADDRESS
柱子。
SNICKERS_COUNT
柱子。您需要忽略不是对
SNICKERS_COUNT
的更改的更改。 .通过设置它们很容易忽略这些
null
.但是,实际的变化值也可能是
null
,我们不想忽略这些。所以,我们必须指定一个非
null
代表值(value)
null
我们不想忽略的值。该指定值必须是永远不会出现在实际数据中的值。对于数字列,我选择了 -99999,对于文本列,我选择了“#NULL#”。
VENDING_MACHINE_CHANGE_LOG
的全表扫描,我看不到您下车。 .
nullif(
last_value(
case when c.modified_column_name = 'SNICKERS_COUNT'
THEN nvl(c.new_number_value,-99999)
ELSE NULL END)
ignore nulls
over ( partition by c.vending_machine_id
order by c.change_timestamp asc
range between unbounded preceding and current row)
,-99999) snickers_count,
从这个内部表达开始:
case when c.modified_column_name = 'SNICKERS_COUNT'
THEN nvl(c.new_number_value,-99999)
ELSE NULL END
如果修改的列不是
SNICKERS_COUNT
,表达式为
NULL
.这是它可以为空的唯一方法。如果
new_number_value
是
NULL
,我们将其转换为我们指定的替身 (-99999)。
last_value(...case expression above...)
ignore nulls
over ( partition by c.vending_machine_id
order by c.change_timestamp asc
range between unbounded preceding and current row)
...这告诉 Oracle 为 case 表达式采用最近的非空值,“最近的”被定义为具有最高
change_timestamp
的行。对于具有相同
vending_machine_id
的一组行作为当前行,并且只包括到当前行的更改。
nullif(... last_value expression above...
,-99999) snickers_count
这将转换
null
的指定替代值回到真实
null
.
+-----------+---------------------------------+--------------------+----------------+-----------------+------------+----------------+-----------------+--------------------+--------------------+--------------------+
| CHANGE_ID | AS_OF_TIMESTAMP | VENDING_MACHINE_ID | SNICKERS_COUNT | MILKY_WAY_COUNT | TWIX_COUNT | SKITTLES_COUNT | STARBURST_COUNT | SWEDISH_FISH_COUNT | FACILITIES_ADDRESS | FACILITIES_CONTACT |
+-----------+---------------------------------+--------------------+----------------+-----------------+------------+----------------+-----------------+--------------------+--------------------+--------------------+
| 168 | 06-NOV-19 05.21.00.000000000 AM | 225 | | | | | | 1 | | |
| 180 | 06-NOV-19 07.31.00.000000000 AM | 225 | | | | | | 1 | | William |
| 186 | 06-NOV-19 09.33.00.000000000 AM | 225 | | | | 11 | | 1 | | William |
| 187 | 06-NOV-19 09.45.00.000000000 AM | 225 | | | | 11 | | 1 | | |
| 191 | 06-NOV-19 10.59.00.000000000 AM | 225 | | | | 11 | | 1 | | |
| 193 | 06-NOV-19 11.34.00.000000000 AM | 225 | | | | 8 | | 1 | | |
| 196 | 06-NOV-19 11.56.00.000000000 AM | 225 | | | 2 | 8 | | 1 | | |
| 177 | 06-NOV-19 07.00.00.000000000 AM | 349 | | 3 | | | | | | |
| 179 | 06-NOV-19 07.11.00.000000000 AM | 349 | | 3 | 15 | | | | | |
| 184 | 06-NOV-19 09.04.00.000000000 AM | 349 | | 10 | 15 | | | | | |
| 194 | 06-NOV-19 11.39.00.000000000 AM | 349 | | 10 | 15 | | | | | Mark |
| 167 | 06-NOV-19 05.18.00.000000000 AM | 481 | | 5 | | | | | | |
| 169 | 06-NOV-19 05.40.00.000000000 AM | 481 | | 5 | | | | | | |
| 170 | 06-NOV-19 05.49.00.000000000 AM | 481 | | 5 | | | 4 | | | |
| 172 | 06-NOV-19 06.25.00.000000000 AM | 481 | | 5 | | | 4 | 7 | | |
| 173 | 06-NOV-19 06.40.00.000000000 AM | 481 | | 5 | | | 4 | 7 | | Audrey |
| 178 | 06-NOV-19 07.03.00.000000000 AM | 481 | | 5 | 8 | | 4 | 7 | | Audrey |
| 182 | 06-NOV-19 08.28.00.000000000 AM | 481 | 0 | 5 | 8 | | 4 | 7 | | Audrey |
| 183 | 06-NOV-19 08.38.00.000000000 AM | 481 | 0 | 5 | 8 | 7 | 4 | 7 | | Audrey |
| 185 | 06-NOV-19 09.21.00.000000000 AM | 481 | | 5 | 8 | 7 | 4 | 7 | | Audrey |
| 188 | 06-NOV-19 10.16.00.000000000 AM | 481 | | 5 | 8 | 7 | 4 | 7 | | Lucy |
| 189 | 06-NOV-19 10.25.00.000000000 AM | 481 | 10 | 5 | 8 | 7 | 4 | 7 | | Lucy |
| 192 | 06-NOV-19 11.11.00.000000000 AM | 481 | 10 | 5 | 8 | 7 | 6 | 7 | | Lucy |
| 171 | 06-NOV-19 06.09.00.000000000 AM | 576 | | | | | | | | |
| 174 | 06-NOV-19 06.46.00.000000000 AM | 576 | 13 | | | | | | | |
| 175 | 06-NOV-19 06.55.00.000000000 AM | 576 | 13 | | | | | | 388 Holiday Street | |
| 176 | 06-NOV-19 06.59.00.000000000 AM | 576 | 13 | | | | | | 388 Holiday Street | |
| 181 | 06-NOV-19 07.49.00.000000000 AM | 576 | 13 | | | | | | 388 Holiday Street | Brian |
| 190 | 06-NOV-19 10.57.00.000000000 AM | 576 | 13 | | | | | 12 | 388 Holiday Street | Brian |
| 195 | 06-NOV-19 11.42.00.000000000 AM | 576 | 13 | | | 8 | | 12 | 388 Holiday Street | Brian |
+-----------+---------------------------------+--------------------+----------------+-----------------+------------+----------------+-----------------+--------------------+--------------------+--------------------+
关于sql - 仅基于更改日志数据重建历史表行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59108948/
背景 之前陆续写过一些和 OpenTelemetry 相关的文章: 实战:如何优雅的从 Skywalking 切换到 OpenTelemetry 实战:如何编写一个 OpenTeleme
我很困惑PSReadLine历史在 Powershell 中跨 session 工作。我可以在 PS 版本 5.1 中看到我以前的命令历史记录自动存储在 %userprofile%\AppData\R
我有一个实体,我正在从面板中保存我们的数据库,您可以在其中执行常规操作(编辑、添加等)。不是很大,大多数时候大约有 1k 行,而且这个数字可能总是在这个左右。该实体有一些与其他实体相关的字段(例如:位
有时有人想直接在环境中更改 crx 中的内容。 这通常是环境不工作状态的原因。而且往往很难找到问题的原因。而且我认为如果 cq5 crx 有审计日志会很有帮助。像这样。 12.12.12 21:03
这个问题与可以在其他问题之一中找到的模式有关here.基本上在数据库中,我存储用户,位置,传感器等。所有这些内容都可以由用户在系统中编辑,并且可以删除。 但是-在编辑或删除项目时,我需要存储旧数据;我
我需要随时跟踪许多项目及其状态。 例子 ItemId Location DateTime State 1 Mall A 2010-02-03 07:00 on
我有这个方法来添加 fragment : public void addFragmentOnTop(Fragment fragment) { getSupportFragmentManager()
我想了解 HTML5 历史对象。这是我开始的一个简单示例。 function addDialog(){ document.getElementById('d').style.
我如何使用 HTML5 history api。我确实通过了https://developer.mozilla.org/en/DOM/Manipulating_the_browser_history
我正在尝试找出在关系数据库中保存表的历史记录/修订的最佳方法。 我进行了一些研究和阅读,但不确定跟踪更改的最佳方式是什么。对于我的主表,我很确定我已经确定了一个修订表,以保持跟踪(见图),但我不确定是
这个问题在这里已经有了答案: Git: discover which commits ever touched a range of lines (6 个答案) 关闭 9 年前。 我一直在研究 gi
我有一个相当复杂的程序(带有 SWIG'ed C++ 代码的 Python,长期运行的服务器),它显示了不断增长的常驻内存使用量。我一直在使用常用的泄漏工具(valgrind、Pythons gc 模
我的 Git 存储库中有一行包含单词“Foo”的数百次提交。 是否有任何方法可以在上次的位置找到它的修订号? 最佳答案 这可以通过 -S 的镐 ( gitlog ) 选项来解决。 git log -
我不小心删除了一个文件(我不是他的创建者)并提交并将其推送到远程。现在我想让 git 取消删除此更改,但是当我使用 git revert #mistaken commit 时,它可以工作,但指责信息指
我使用 spyder 历史 Pane 查看我过去尝试过的命令,但最近我注意到它不会在我键入命令时更新。屏幕截图 1 显示了控制台和历史记录 Pane ,因为您可以看到历史记录中没有显示任何控制台条目。
我的应用程序使用 Camunda 7.7 运行。到目前为止,所有数据都保存在 Camunda 表 (ACT_XXX) 中——它们变得很大。所以现在我想清理表格并配置 Camunda,以便在 14 天后
我在 SVN 上有一个这样组织的旧项目: /一些/子目录/a/trunk/foo /一些/子目录/b/trunk/foo /一些/子目录/c/trunk/foo 我使用GitHub工具git-impo
我有一个通用的工作功能,为此我将使用 GNU Radio 的历史记录功能。在 block 的构造函数中,我调用了 set_history( m )。我以标准方式转换输入缓冲区: const flo
当我加载 php 页面时,我会附加一些数据。例如 MyPage.php?value=something。正如预期的那样,当我使用后退按钮来回移动时,它总是会加载附加的相同数据。我不想那样。我希望在页面
我们有一个相当大的库,我们需要定期将其导入(然后修补)到我们的代码库中。 SVN Book 似乎推荐了一个“vendor branch”方案,我们保留了“vendor drops”的补丁版本。这会起作
我是一名优秀的程序员,十分优秀!