2011 年 1 月 15 日修订
我确信某处有一个简单的方法!
就在这里。但首先,有两个问题。
-
该表不是关系数据库表。它没有唯一的键,这是 RM 和规范化所要求的(具体来说,每一行必须有一个唯一的标识符;不一定是 PK)。因此,SQL作为操作关系型数据库表的标准语言,无法对其进行基本操作。
- 它是一个堆(数据结构,按时间顺序插入和删除),其中包含记录而不是行。
- 任何使用 SQL 的操作都会非常慢,并且不正确
- 将 ROWCOUNT 设置为 1,执行行处理,SQL 将在堆上正常工作
- 最好的选择是使用任何 UNIX 实用程序对其进行操作(awk、cut、chop)。他们的速度快得令人眼花缭乱。满足您的要求所需的 awk 脚本需要 3 分钟才能编写,并且它将在几秒钟内运行数百万条记录(我上周写了一些)。
.
所以问题确实是SQL 查找非关系堆中第一次出现的数据集.
现在如果你的问题是SQL查找关系表中第一次出现的数据集,当然意味着一些唯一的行标识符,这将(a)在 SQL 中很容易,并且(b)在任何 SQL 风格中都很快......
- 除了 Oracle,众所周知子查询处理不好 https://stackoverflow.com/questions/4265213/how-to-turn-2-queries-with-common-columns-a-b-and-a-c-into-just-one-a-b/4279443#4279443(特别是托尼·安德鲁斯的评论,他是甲骨文方面的知名权威)。在这种情况下,请使用物化视图。
.
这个问题很笼统(没有抱怨)。但这些特定需求中的许多通常应用在更大的上下文中,并且该上下文具有此处的规范中没有的要求。通常需要一个简单的子查询(但在 Oracle 中使用物化视图来避免子查询)。子查询也取决于外部上下文、外部查询。因此,对小通用问题的答案将不包含对实际特定需求的答案。
无论如何,我不想回避这个问题。为什么我们不使用一个现实世界的例子,而不是一个简单的通用例子?和在关系表中查找一组数据在另一组数据中的第一次或最后一次出现,或者最小值或最大值 ?
主要查询
让我们使用▶数据模型◀ http://www.softwaregems.com.au/Documents/Student%20Resolutions/Mark%20DM.pdf从你之前的问题来看。
全部举报Alerts
自某个日期以来,随着持续时间的峰值,这不是Acknowledged
由于您将使用完全相同的技术(具有不同的表和列名称)来满足所有时间和历史要求,因此您需要完全理解子查询的基本构造及其不同的应用程序。
介绍
请注意,您不仅拥有一个带有关系标识符(复合键)的纯 5NF 数据库,而且自始至终都具有完整的时间功能,并且在不破坏 5NF(无更新异常)的情况下呈现时间要求,这意味着ValidToDateTime
周期和持续时间的数据是导出的,并且不会在数据中重复。重点是,这让事情变得复杂,因此这是 这不是子查询教程的最佳示例.
- Remember the SQL engine is a set-processor, so we approach the problem with a set-oriented mindset
- 不要将引擎简化为行处理;那是非常慢
- 更重要的是,不必要
- Subqueries are normal SQL. The syntax I am using is straight ISO/IEC/ANSI SQL.
- 如果您无法在 SQL 中编写子查询代码,那么您将very有限的;然后需要引入数据重复或使用大型结果集作为物化视图或临时表或各种附加数据和附加处理,这将是s.l.o.w to 非常慢, 更何况完全没有必要
- 如果在真正的关系数据库中(我的数据模型总是如此)有什么事情你无法在不切换到行处理或内联视图或临时表的情况下完成,请寻求帮助,这就是你在这里所做的。
- 在尝试理解第二个子查询之前,您需要完全理解第一个子查询(更简单); ETC。
Method
首先使用最小连接等构建外部查询,基于结构您需要的结果集,仅此而已。首先解决外层查询的结构非常重要;否则,您将来回尝试使子查询适合外部查询,反之亦然。
- 这恰好也需要子查询。因此,暂时保留该部分,稍后再拾取。目前,外部查询获取全部(不是未确认的)
Alerts
在某个日期之后
The ▶SQL代码◀ http://www.softwaregems.com.au/Documents/Student%20Resolutions/Mark%20Peak%20Plus.pdfrequired 位于第 1 页(抱歉,SO 编辑功能很糟糕,它破坏了格式,并且代码已经格式化)。
然后构建子查询来填充每个单元格。
子查询 (1) 派生 Alert.Value
这是一个简单的导出数据,选择Value
来自Reading
产生了Alert
。表是相关的,基数是1::1,所以它是PK上的直接连接。
- The type of Subquery required here is a Correlated Subquery, we need to correlate a table in the Outer query to a table in the (inner) Subquery.
- 为此,我们需要外部查询中的表的别名,以将其与子查询中的表关联起来。
- 为了进行区分,我仅使用别名来实现所需的关联,并使用完全限定名称来实现普通连接
- 子查询在任何引擎中都非常快(Oracle 除外)
- SQL 是一种繁琐的语言。但这就是我们所拥有的一切。所以要习惯它。
The ▶SQL代码◀ http://www.softwaregems.com.au/Documents/Student%20Resolutions/Mark%20Peak%20Plus.pdf所需内容位于第 2 页。
我特意为您提供了外部查询中的联接与通过子查询获取数据的混合,以便您可以学习(您也可以获取Alert.Value
通过连接,但这将是更麻烦).
我们需要派生的下一个子查询Alert.PeakValue
。为此,我们需要确定的时间持续时间Alert
。我们有一个开始Alert
期间;我们需要确定 Duration 的结束时间,即next(暂时)Reading.Value
那是在范围内。这还需要一个子查询,我们最好先处理它。
子查询 (2) 派生 Alert.EndDtm
稍微复杂一点的 Suquery 选择第一个Reading.ReadingDtm
,即大于或等于Alert.ReadingDtm
,有一个Reading.Value
小于或等于其Sensor.UpperLimit
.
处理 5NF 时态数据
用于处理 5NF 数据库中的时间要求(其中EndDateTime
is not存储,重复数据也是如此),我们致力于StartDateTime
仅,并且EndDateTime
is derived: 它是next StartDateTime
。这是时间概念Duration.
- 从技术上讲,它要少一毫秒(无论使用的数据类型的分辨率如何)。
- 然而,为了合理,我们可以谈论并报告,
EndDateTime
就像简单地Next.StartDateTime
,并忽略一毫秒的问题。
- The code should always use >=
This.StartDateTime
and < Next.StartDateTime
.
- 这消除了一系列可以避免的错误
- 请注意,这些比较运算符将时间持续时间括起来,并且应该按照上面的常规方式使用,它们完全独立于与业务逻辑相关的类似比较运算符,例如。
Sensor.UpperLimit
(即注意它,因为两者通常位于一个WHERE
子句,很容易将它们混淆或混淆)。
The ▶SQL代码◀ http://www.softwaregems.com.au/Documents/Student%20Resolutions/Mark%20Peak%20Plus.pdf所需的数据以及使用的测试数据位于第 3 页。
子查询 (3) 派生 Alert.PeakValue
现在很容易了。选择MAX(Value)
from Readings
之间Alert.ReadingDtm
and Alert.EndDtm
,持续时间Alert
.
The ▶SQL代码◀ http://www.softwaregems.com.au/Documents/Student%20Resolutions/Mark%20Peak%20Plus.pdf所需内容位于第 4 页。
标量子查询
除了是关联子查询之外,以上都是标量子查询,因为它们返回单个值;网格中的每个单元格只能填充一个值。 (返回多个值的非标量子查询是相当合法的,但不适用于上述情况。)
子查询 (4) 已确认的警报
好的,现在您已经掌握了上面的相关标量子查询(这些子查询填充集合中的单元格,这是由外部查询定义的集合),让我们看一下可用于约束外部查询的子查询。我们其实并不想要all Alerts
(上),我们想要Un-Acknowledged Alerts
:存在于的标识符Alert
,不存在于Acknowledgement
。那不是填充细胞,而是改变content的外集。当然,这意味着改变WHERE
clause.
- 我们不会改变结构外部集,所以没有改变
FROM
and existing WHERE
条款。
只需添加一个WHERE
排除集合的条件Acknowledged Alerts
。 1::1 基数,直接相关连接。
The ▶SQL代码◀ http://www.softwaregems.com.au/Documents/Student%20Resolutions/Mark%20Peak%20Plus.pdf所需内容位于第 5 页。
不同的是,这是一个非标量子查询,产生一组行(一列)。我们有一整套Alerts
(外部集)与整个集匹配Acknowledgements
.
- 处理匹配是因为我们已经告诉引擎子查询是相关,通过使用别名(不需要识别繁琐的连接)
- Use
1
,因为我们正在执行存在性检查。将其可视化为添加到Alert
由外部查询定义的集合。
- 切勿使用 *,因为我们不需要整组列,而且速度会更慢
- 同样,未能使用相关性意味着
WHERE NOT IN ()
是必需的,但同样,构造定义的列集,然后比较两个集。慢得多。
子查询 (5) Actioned Alerts
作为外部查询的替代约束,对于未执行的Alerts
,而不是(4),排除集合Actioned Alerts
。直接关联连接。
The ▶SQL代码◀ http://www.softwaregems.com.au/Documents/Student%20Resolutions/Mark%20Peak%20Plus.pdf所需内容位于第 5 页。
此代码已在 Sybase ASE 15.0.3 上使用 1000 进行了测试Alerts
和 200Acknowledgements
,不同的组合;和Readings
and Alerts
文档中已确定。所有执行的执行时间为零毫秒(分辨率为 0.003 秒)。
如果你需要的话,这里有▶文本格式的SQL代码◀ http://www.softwaregems.com.au/Documents/Student%20Resolutions/Mark%20Peak%20Plus.sql.
对评论的回应
(6) ▶注册阅读提醒◀ http://www.softwaregems.com.au/Documents/Student%20Resolutions/Mark%20Register%20Alert.sql
此代码在循环中执行(已提供),选择新的Readings
超出范围,并创建Alerts
,除非适用Alerts
已经存在。
(7) ▶从阅读中加载警报◀ http://www.softwaregems.com.au/Documents/Student%20Resolutions/Mark%20Load%20Alert.sql
鉴于您有完整的测试数据集Reading
,此代码使用 (6) 的修改形式来加载适用的Alerts
.
常见问题
当你知道如何做时,这就是“简单”。我再说一遍,如果没有编写子查询的能力,编写 SQL 的能力是非常有限的;它对于处理关系数据库至关重要,而这正是 SQL 的设计目的。
- Half the reason developers implement unnormalised data heaps (massive data duplication) is because they cannot write the subqueries required for Normalised structures
- 这并不是说他们“为了性能而进行了非规范化”;问题是他们无法为规范化编码。我已经看过一百遍了。
- 这里的例子是:您有一个完全规范化的关系数据库,困难在于对其进行编码,并且您正在考虑复制表以进行处理。
- 这还不包括时态数据库增加的复杂性。或 5NF 时态数据库。
- 标准化手段永远不要重复任何东西,最近被称为不要重复自己
- 掌握 Suqueries,您将处于第 98 个百分点:标准化、真正的关系数据库;零数据重复;非常高的性能。
我想你可以弄清楚剩下的疑问。
关系标识符
请注意,这个例子也恰好展示了使用的威力关系标识符,因为我们想要的表之间的几个表不必连接(是的!事实是关系标识符意味着更少而不是更多的连接,而不是更多)Id
键)。只需遵循实线即可。
- 您的临时要求需要包含以下内容的密钥
DateTime
。想象一下尝试用以下代码编写上面的代码Id
PK,将有两个级别的处理:一个用于连接(并且会有更多),另一个用于数据处理。
Label
我尽量避免使用口语标签(“嵌套”、“内部”等),因为它们不具体,并坚持使用特定的技术术语。为了完整性和理解:
- a Subquery after the
FROM
clause, is a Materialised View, a result set derived in one query and then fed into the FROM
clause of another query, as a "table".
- Oracle 类型将此称为内联视图。
- 在大多数情况下,您可以将相关子查询编写为物化视图,但这需要大量的 I/O 和处理(因为 Oracle 对子查询的处理非常糟糕,仅对于 Oracle,物化视图“更快”)。
.
-
中的子查询WHERE
子句是一个谓词子查询,因为它改变了结果集的内容(它所依据的内容)。它可以返回标量(一个值)或非标量(多个值)。
中的子查询WHERE
条款不need具有相关性;以下工作正常。识别所有多余的附属物:
SELECT [Never] = FirstName,
[Acted] = LastName
FROM User
WHERE UserId NOT IN ( SELECT DISTINCT UserId
FROM Action
)