假设我有一张桌子:
CREATE TABLE T
(
TableDTM TIMESTAMP NOT NULL,
Code INT NOT NULL
);
Run Code Online (Sandbox Code Playgroud)
我插入一些行:
INSERT INTO T (TableDTM, Code) VALUES ('2011-01-13 10:00:00', 5);
INSERT INTO T (TableDTM, Code) VALUES ('2011-01-13 10:10:00', 5);
INSERT INTO T (TableDTM, Code) VALUES ('2011-01-13 10:20:00', 5);
INSERT INTO T (TableDTM, Code) VALUES ('2011-01-13 10:30:00', 5);
INSERT INTO T (TableDTM, Code) VALUES ('2011-01-13 10:40:00', 0);
INSERT INTO T (TableDTM, Code) VALUES ('2011-01-13 10:50:00', 1);
INSERT INTO T (TableDTM, Code) VALUES ('2011-01-13 11:00:00', 1);
INSERT INTO T (TableDTM, Code) VALUES ('2011-01-13 11:10:00', 1);
INSERT INTO T (TableDTM, Code) VALUES ('2011-01-13 11:20:00', 0);
INSERT INTO T (TableDTM, Code) VALUES ('2011-01-13 11:30:00', 5);
INSERT INTO T (TableDTM, Code) VALUES ('2011-01-13 11:40:00', 5);
INSERT INTO T (TableDTM, Code) VALUES ('2011-01-13 11:50:00', 3);
INSERT INTO T (TableDTM, Code) VALUES ('2011-01-13 12:00:00', 3);
INSERT INTO T (TableDTM, Code) VALUES ('2011-01-13 12:10:00', 3);
Run Code Online (Sandbox Code Playgroud)
所以我最终得到了一张类似于的表:
2011-01-13 10:00:00, 5
2011-01-13 10:10:00, 5
2011-01-13 10:20:00, 5
2011-01-13 10:30:00, 5
2011-01-13 10:40:00, 0
2011-01-13 10:50:00, 1
2011-01-13 11:00:00, 1
2011-01-13 11:10:00, 1
2011-01-13 11:20:00, 0
2011-01-13 11:30:00, 5
2011-01-13 11:40:00, 5
2011-01-13 11:50:00, 3
2011-01-13 12:00:00, 3
2011-01-13 12:10:00, 3
Run Code Online (Sandbox Code Playgroud)
如何选择每组相同数字的第一个日期,所以我最终得到:
2011-01-13 10:00:00, 5
2011-01-13 10:40:00, 0
2011-01-13 10:50:00, 1
2011-01-13 11:20:00, 0
2011-01-13 11:30:00, 5
2011-01-13 11:50:00, 3
Run Code Online (Sandbox Code Playgroud)
在一天的大部分时间里,我一直在搞乱子查询等等,出于某种原因我似乎无法破解它.我确定在某个地方有一个简单的方法!
我可能想要从结果中排除0,但这对于现在来说并不重要.
我确信某处有一个简单的方法!
\n\n就在这里。但首先,有两个问题。
\n\n该表不是关系数据库表。它没有唯一的键,这是 RM 和规范化所要求的(具体来说,每一行必须有一个唯一的标识符;不一定是 PK)。因此,SQL作为操作关系型数据库表的标准语言,无法对其进行基本操作。
\n\n所以问题实际上是SQL 查找非关系堆中第一次出现的数据集。
\n\n现在,如果您的问题是用SQL 来查找关系表中数据集的第一次出现(这当然意味着一些唯一的行标识符),那么(a)在 SQL 中很容易,(b)在任何 SQL 风格中都很快。 。
\n\n这个问题很笼统(没有抱怨)。但这些特定需求中的许多通常应用在更大的上下文中,并且该上下文具有此处的规范中没有的要求。通常需要一个简单的子查询(但在 Oracle 中使用物化视图来避免子查询)。子查询也取决于外部上下文、外部查询。因此,对小通用问题的答案将不包含对实际特定需求的答案。
无论如何,我不想回避这个问题。为什么我们不使用一个现实世界的例子,而不是一个简单的通用例子?并在关系表中查找一组数据在另一组数据中的第一次或最后一次出现,或者最小值或最大值?
\n\n主要查询
\n\n让我们使用上一个问题中的\xe2\x96\xb6Data Model\xe2\x97\x80 。
\n\n全部举报Alerts
自特定日期以来的所有情况(包括持续时间的峰值)Acknowledged
由于您将使用完全相同的技术(具有不同的表和列名称)来满足所有时间和历史要求,因此您需要完全理解子查询的基本构造及其不同的应用程序。
\n\n\n\n\n请注意,您不仅拥有带有关系标识符(复合键)的纯 5NF 数据库,而且自始至终都具有完整的时间功能,并且在不破坏 5NF(无更新异常)的情况下呈现时间要求,这意味着周期和持续时间
\nValidToDateTime
是派生的,并且在数据中不重复。重点是,这使事情变得复杂,因此这不是 子查询教程的最佳示例。
首先根据结构使用最小连接等构建外部查询仅此而已。首先解决外层查询的结构非常重要;否则,您将来回尝试使子查询适合外部查询,反之亦然。
\n\nAlerts
特定日期之后的所有(不是未确认的)\xe2\x96\xb6SQL代码\xe2\x97\x80位于第1页(抱歉,SO编辑功能太糟糕了,它破坏了格式,并且代码已经格式化)。
\n\n然后构建子查询来填充每个单元格。
\n\n子查询 (1) 派生 Alert.Value
Value
这是一个简单的派生数据,从Reading
生成的数据中选择Alert
。表是相关的,基数是1::1,所以它是PK上的直接连接。
\xe2\x96\xb6SQL代码\xe2\x97\x80位于第 2 页。
\n\n我特意为您提供了外部查询中的连接与通过子查询获取数据的混合,以便您可以学习(您也可以Alert.Value
通过连接获取,但这会更麻烦)。
我们需要的下一个子查询 派生Alert.PeakValue
。为此,我们需要确定 的时间持续时间Alert
。我们有持续时间的开始Alert
;我们需要确定持续时间的结束,这是范围内的下一个(暂时)Reading.Value
。这还需要一个子查询,我们最好先处理它。
子查询 (2) 派生 Alert.EndDtm
稍微复杂一点的 Suquery 选择第一个Reading.ReadingDtm
,即大于或等于Alert.ReadingDtm
,其中 aReading.Value
小于或等于其Sensor.UpperLimit
。
处理 5NF 时态数据
\n\n为了处理 5NF 数据库中的时间要求(其中EndDateTime
不存储重复数据),我们只处理 a StartDateTime
,并EndDateTime
导出:它是下一个 StartDateTime
。这是Duration的时间概念。
EndDateTime
简单地谈论和报告Next.StartDateTime
,并忽略一毫秒的问题。 This.StartDateTime
和< Next.StartDateTime
。\n\nSensor.UpperLimit
(即注意它,因为两者通常位于一个WHERE
子句中,并且很容易将它们混淆或混淆)。所需的 \xe2\x96\xb6SQL代码\xe2\x97\x80以及使用的测试数据位于第 3 页。
\n\n子查询 (3) 派生 Alert.PeakValue
现在很容易了。MAX(Value)
从和Readings
之间选择,持续时间Alert.ReadingDtm
Alert.EndDtm
Alert
。
\xe2\x96\xb6SQL代码\xe2\x97\x80位于第 4 页。
\n\n标量子查询
\n\n除了相关子查询之外,以上都是标量子查询,因为它们返回单个值;网格中的每个单元格只能填充一个值。(返回多个值的非标量子查询是相当合法的,但不适用于上述情况。)
\n\n子查询 (4) 已确认的警报
\n\n好的,既然您已经掌握了上述相关标量子查询,即填充集合中的单元格的子查询,集合是由外部查询定义的,让我们看一下可用于约束外部查询的子查询。我们并不真正想要所有 Alerts
(上面),我们想要Un-Acknowledged Alerts
:存在于 中的标识符Alert
,不存在于 中的标识符Acknowledgement
。这不是填充单元格,而是更改外部集的内容。当然,这意味着改变WHERE
条款。
FROM
,因此和现有 子句没有变化WHERE
。 只需添加一个WHERE
条件即可排除 的集合Acknowledged Alerts
。1::1 基数,直接相关连接。
所需的 \xe2\x96\xb6SQL代码\xe2\x97\x80位于第 5 页。
\n\n不同之处在于,这是一个非标量子查询,生成一组行(一列)。我们有一整套Alerts
(外部集)与一整套Acknowledgements
.
1
,因为我们正在执行存在性检查。将其可视化为添加到Alert
外部查询定义的集合上的列。WHERE NOT IN ()
需要 a,但同样,它会构造定义的列集,然后比较两个集。慢得多。子查询 (5) Actioned Alerts
作为外部查询的替代约束,对于未执行的Alerts
,而不是 (4),排除 的集合Actioned Alerts
。直接关联连接。
所需的 \xe2\x96\xb6SQL代码\xe2\x97\x80位于第 5 页。
\n\n此代码已在 Sybase ASE 15.0.3 上使用不同组合的1000Alerts
和 200进行了测试;Acknowledgements
以及文件中确定的Readings
和。Alerts
所有执行的执行时间为零毫秒(分辨率为 0.003 秒)。
如果您需要它,这里是文本格式的 \xe2\x96\xb6SQL 代码\xe2\x97\x80。
\n\n(6) \xe2\x96\xb6Register Alert from Reading\xe2\x97\x80
\n此代码在循环中执行(已提供),选择Readings
超出范围的 new 并创建Alerts
,除非Alerts
已存在适用的情况。
(7) \xe2\x96\xb6Load Alert From Reading\xe2\x97\x80
\n鉴于您有 的完整测试数据集Reading
,此代码使用 (6) 的修改形式来加载适用的Alerts
.
当你知道如何做时,这就是“简单”。我再说一遍,如果没有编写子查询的能力,编写 SQL 的能力是非常有限的;它对于处理关系数据库至关重要,而这正是 SQL 的设计目的。
\n\n我想你可以弄清楚剩下的疑问。
\n\n请注意,此示例也恰好演示了使用关系标识符的强大功能,因为我们想要的表之间的多个表不必连接(是的!事实是关系标识符意味着比Id
键更少而不是更多的连接)。只需遵循实线即可。
DateTime
. 想象一下尝试使用Id
PK 来编写上述内容,将会有两个处理级别:一个用于连接(并且会有更多的连接),另一个用于数据处理。我尽量避免使用口语标签(“嵌套”、“内部”等),因为它们不具体,并坚持使用特定的技术术语。为了完整性和理解:
\n\nFROM
是一个物化视图FROM
,是一个查询中派生的结果集,然后作为“表”输入到另一个查询的子句中。\n\n子句中的子查询WHERE
是谓词子查询,因为它更改结果集的内容(它所依据的内容)。它可以返回标量(一个值)或非标量(多个值)。
对于标量,使用WHERE column =
或任何标量运算符
对于非标量,使用WHERE [NOT] EXISTS
, 或WHERE column [NOT] IN
子句中的 SuqueryWHERE
不需要关联;以下工作正常。识别所有多余的附属物:
SELECT [Never] = FirstName,\n [Acted] = LastName \n FROM User \n WHERE UserId NOT IN ( SELECT DISTINCT UserId\n FROM Action\n )
Run Code Online (Sandbox Code Playgroud)