Hot topics

读书《100 SQL Server Mistakes and How to Avoid Them》第十章 优化

第十章 优化 本章涵盖 实例级优化 查询优化 表优化 事务隔离级别 通过增加硬件来解决性能问题 在本章中,我们将深入探讨与 SQL Server 性能优化相关的错误和误解。我们将从实例级优化开始,讨论已弃用的跟踪标志的使用、关于即时文件初始化的误解以及内存配置错误。错误大致有两种类型:未能优化和错误的优化。   未能优化 我最近目睹了一次优化失败。我被要求查看一个提取、转换和加载(ETL)过程,这个过程在将数据加载到数据仓库时花费了大量时间。经过对该过程的检查,很明显团队没有进行容量规划(参见第9章)。虽然磁盘还有剩余空间,但数据文件的大小并未考虑每晚被导入数据库的大量数据。这导致数据文件在每晚多次以小增量扩展。即时文件初始化没有启用,这意味着每次增加文件空间时都会被清零,这在本章讨论的第二个错误中提到。启用即时文件初始化立即解决了问题,但我也让团队去规划他们的容量并相应地调整磁盘大小。 然后我们将继续讨论在优化查询时犯的错误。其中第一个错误是在本应与优化器合作时却与其对抗。接下来我们将讨论查询处理反馈,这一点经常被数据库管理员(DBA)忽视。   我们将继续研究表优化。在这里,我们将讨论一个常见的错误,即忽视有用的性能优化技术,包括分区表和压缩表。   接下来我们将看交易隔离级别,在这里我们将探讨我一次又一次看到的错误和误解。最后,我们将讨论当我们决定通过增加硬件来解决性能问题时会发生什么。   为了讨论本章的错误,我们将使用在第6章中创建的Marketing数据库以及在第9章中创建的MarketingArchive数据库。如果您已经按照第8章的示例操作,但尚未修复MarketingArchive数据库,请在继续之前运行清单10.1中的脚本。   警告: 在正常操作中,除非没有其他恢复选项(例如从备份恢复等),否则不应使用 REPAIR_ALLOW_DATA_LOSS,因为此选项可能会导致数据丢失。   USE master ; GO   ALTER DATABASE MarketingArchive SET SINGLE_USER ; GO   DBCC CHECKDB (MarketingArchive, REPAIR_ALLOW_DATA_LOSS) ; GO   ALTER DATABASE MarketingArchive SET MULTI_USER ; GO   对于对实例进行更改的示例,任何实例都可以,但承载 MarketingArchive 数据库的实例将是一个不错的选择。   错误54# 开启 TF1117 和 TF1118 跟踪标志是允许管理员更改特定配置的切换选项。每个标志都有一个三位或四位数字,虽然许多标志未被记录或与较旧版本的 SQL Server 相关,但确实存在一些有用的跟踪标志。例如,跟踪标志 3226 可用于抑制成功备份的消息。这可以减少 SQL Server 日志中的“噪音”。   根据跟踪标志的性质,可以将配置应用于本地会话或全局,即所有会话。例如,上述的3226标志只能在全局开启。   可以使用 DBCC TRACEON 和 DBCC TRACEOFF 命令来打开和关闭跟踪标志。将跟踪标志编号传递给命令以进行会话配置。对于全局配置,则传递第二个参数 -1。例如,以下脚本演示了如何在会话中启用跟踪标志 1224,然后在全局启用。此标志会禁用基于锁数量的锁升级:   DBCC TRACEON (1224) ; DBCC TRACEON (1224,-1) ;   全局跟踪标志的挑战在于,它们在数据库引擎服务重启后不会保留。因此,如果我们希望某个配置在重启后持续有效,必须将其作为 SQL Server 服务的启动参数添加。   前些时候,通常会使用两个这样的跟踪标志来帮助解决数据仓库类应用程序中的性能问题,以及在某些情况下 OLTP 类工作负载中的 TempDB 问题。具体而言,T1117(跟踪标志 1117 的常用缩写)用于在任何文件达到其自动增长阈值时增长文件组中的所有文件。SQL Server 使用比例填充算法在文件组中的各个文件之间分配数据。因此,同时增长所有文件可以防止 SQL Server “偏向”较大的文件,从而无法充分利用并行读取文件的数据的全部优势。   T1118用于强制统一区。 这意味着多个对象不能将页分配到同一区。默认情况下,小对象(小于64 KB)可以使用混合区。强制使用统一区有助于防止系统页(例如全局分配映射(GAM)、共享全局分配映射以及每个数据文件中存在的页空闲空间系统页)出现争用。统一区与混合区如图10.1所示。                   在 SQL Server 2016 中,T1117 和 T1118 都已被弃用,其功能已被移动到更低的层级,这意味着它们可以进行更细粒度的配置。这是一种更好的实现方式,因为配置可以应用于将从中受益的特定数据库,而不需要像跟踪标志那样将配置应用于所有数据库。   我仍然看到有人在数据库引擎服务上设置 T1117 和 T1118,甚至 Azure SQL Server 映像也配置了这些标志。然而,这是一个错误,因为自 SQL Server 2016 起,这些跟踪标志已经没有效果了。不清楚这一变化的管理员认为他们已经配置了这些设置,实际上并没有。   幸运的是,现在 TempDB 的文件均等增长是默认行为,但对于数据仓库仍应进行配置。统一区块现在是 TempDB 和用户数据库的默认行为。但这只是把问题反过来了。一位不清楚这些变化但希望使用混合区块和/或均等文件增长的数据库管理员,如果什么都不做,就会在不知情的情况下使用了默认的不希望的行为。   如果我们想要更改特定数据库的行为,那么我们应该做什么,而不是配置这两个跟踪标志呢?示例 10.2 中的脚本为 MarketingArchive 数据库开启了相等的文件增长,并关闭了统一区。脚本中的第一个命令使用 ALTER DATABASE..MODIFY FILEGROUP 来开启 AUTOGROW_ALL_FILES,这会覆盖 AUTOGROW_SINGLE_FILE 的默认设置。在第二条语句中,我们使用 ALTER DATABASE SET 选项来启用混合区。   ALTER DATABASE MarketingArchive MODIFY FILEGROUP [PRIMARY] AUTOGROW_ALL_FILES ;   ALTER DATABASE MarketingArchive SET MIXED_PAGE_ALLOCATION ON ;   提示: 这些命令也可以通过期望状态配置来执行(参见第8章)。   错误55# 未使用即时文件初始化 当 SQL Server 创建文件或扩展文件时,它必须将文件清零,这字面意思是用 0 填充任何空白空间。作为性能优化,可以通过使用瞬时文件初始化来跳过数据文件,有时也可以跳过事务日志文件的这一过程。为此,我们只需将运行数据库引擎服务的服务帐户赋予“执行卷维护任务”用户权限(SeManageVolumePrivilege)。SQL Server 就会在文件创建或扩展时自动跳过清零过程。微软在最近版本的 […]

读书《100 SQL Server Mistakes and How to Avoid Them》第九章 实例和数据库管理

第九章 实例和数据库管理 本章涵盖 常见的维护错误和误解 容量规划 数据库损坏 管理脚本 补丁 在本章中,我们将讨论偶然成为数据库管理员(DBA)的人常犯的维护和配置错误。作为本主题的一部分,我们将讨论在查看事务日志文件相关的一些误解之前,自动收缩数据库的影响,这些误解可能导致性能下降等问题。   接下来,我们将讨论容量规划。这是许多数据库管理员未能执行的任务,我们将看看潜在的后果。然后,我们将探讨一些关于脚本和自动化的常见错误,包括使用游标以及完全缺乏维护自动化。   最后,我们将探讨未能修补服务器的问题。我们将思考不进行修补的原因及其后果。我们还将讨论如何避免犯下这个错误。   本章的许多主题都集中在数据库管理员的日常工作上。因此,并非所有主题都会直接影响业务。然而,重要的是要记住,这些主题肯定会间接影响业务。例如,如果我们未能进行补丁更新,那么服务器被恶意攻击者攻击的风险就会增加,从而导致业务的巨大中断和声誉损害。   变化的是我们与业务团队的互动。到目前为止,我们讨论的大多数活动都是由业务团队主导的。相反,本章中的许多主题将由DBA团队主导,以避免或解决对业务的操作影响。例如,日志碎片化这样的问题并不是业务团队熟悉的,也不会要求我们去解决。相反,我们会持续关注这类问题,因为如果我们不关注,业务就会间接受到性能问题的影响。   对于本章中需要数据库的示例,我们将使用 MarketingArchive 数据库。要创建此数据库,您需要我们在第 4 章中创建的 Marketing 数据库。对于使用 SQL Server 实例的示例,我建议使用托管 Marketing 数据库的实例,尽管这不是必需的。   错误43# 自动收缩数据库 大多数IT专业人员都遇到过这样的情况:某个卷的磁盘空间快用完了,他们不得不进行手动维护以清理旧数据,避免卷被填满。许多数据库专业人员也遇到过这样的情况:他们曾请求存储区域网络(SAN)管理员扩展卷的大小,却只听到对方用牙齿吸气的声音,因为SAN的容量不足。   此外,大多数数据库专业人员都知道,当在云环境(如 AWS 或 Azure)中构建运行 SQL Server 的虚拟机时,其底层基础设施不归您的组织所有,这意味着扩展存储卷会产生直接成本。   因此,当一个刚接触 SQL Server 管理角色的人意识到有一个功能可以自动收缩数据库、回收未使用的空间时,他们可以被原谅会认为这听起来是一个好主意。我曾多次看到有些临时的数据库管理员在不了解后果的情况下,为他们所有的数据库启用了这个功能。   所以让我们假设 MagicChoc 有一个托管在 Azure 上的 SQL Server 实例。该实例承载四个数据库,具体如下: Marketing SalesManagerPlus TargetManager MarketingArchive   数据卷上的磁盘利用率徘徊在75%左右,这也是监控工具中磁盘空间警告的阈值。一位经验不足的数据库管理员响应该警告,在调查过程中发现了自动收缩数据库的选项,并将其打开。然而,仅仅一个小时后,多个应用团队开始提交工单,抱怨他们的应用程序性能急剧下降。这是因为启用自动收缩是一个错误。要理解原因,就必须了解自动收缩的工作原理。   SQL Server 中的一个后台任务会定期唤醒并检查是否有启用了自动收缩的数据库。如果有,它会找到第一个配置为自动收缩的数据库,并检查是否有可以回收的空闲空间。如果有,它将对该数据库执行收缩操作。任务然后会再次进入休眠状态。当它几分钟后再次唤醒时,它会检查并收缩(如果需要)下一个配置为自动收缩的数据库。它会永久以这种方式继续,以轮询模式在数据库之间移动。   当数据库执行自动收缩操作时,它将获取锁,这可能会导致需要获取锁的应用程序出现阻塞。如果配置了锁超时,这可能会导致性能问题甚至超时。   收缩操作也是资源密集型的。当收缩进行时,CPU 使用率可能会大幅增加,这可能会影响其他操作,尤其是在使用其他 CPU 密集型功能(如加密或压缩)的情况下。磁盘子系统也会非常繁忙,你可能会看到磁盘活动大幅增加,这可能会对应用程序造成额外的性能问题。这种情况由于收缩操作是完全记录的而被加剧,这意味着事务日志文件和数据文件的吞吐量将会很大。   在收缩操作完成后,一般数据库性能很可能会受到一种称为索引碎片的问题的负面影响。这种碎片是由于收缩操作重新组织数据文件内的页所引起的。然而,我们在这里不会详细讨论这个问题,因为我们将在下一节中详细讨论它。   虽然自动缩小数据库听起来是一个很有吸引力的想法,以保持磁盘空间的可控,但进行容量规划以确保数据量适当是一个更好的主意。本章后面我们将讨论容量规划。   下面列表中的查询可用于检查您是否有开启自动收缩的数据库。   SELECT name , is_auto_shrink_on FROM sys.databases ;   可以使用 ALTER DATABASE 语句关闭自动收缩。以下清单中的查询会关闭 Marketing 数据库的自动收缩。   ALTER DATABASE Marketing SET auto_shrink OFF WITH NO_WAIT ;   提示: 在第8章中,我们讨论了使用所需状态配置来执行配置最佳实践。如果你采用配置管理方法,那么强制关闭自动收缩是自动化的一个很好的候选项。   错误44# 在数据文件收缩后未能重新构建索引 在前一节中,我们讨论了为什么启用数据库的自动收缩不是一个好主意。然而,SQL Server 管理员也有能力手动收缩数据库或文件。作为一般规则,即使是手动收缩数据文件,也应避免。有些情况下,手动收缩数据库可能是有帮助的。   提示:缩小日志文件有不同的注意事项,我们将在本章后面讨论。 考虑上一节中讨论的 SQL Server 实例。它托管了四个数据库,并运行在 Azure 虚拟机上。其中一个数据库名为 MarketingArchive,用于存储历史营销数据。该数据库已运行很长时间,现在存储了九年的历史数据。业务需求只是存储最近三年的数据。数据卷已使用到 75% 的容量,因此有两种选择。要么扩展数据卷,要么从 MarketingArchive 数据库中回收六年的数据。如果扩展数据卷,将产生额外的 Azure 存储费用,而且存储的数据已不再需要。因此,在这种情况下,清除不再需要的数据并对数据库执行一次性收缩操作以回收空间是合理的。   在这种情况下,数据库管理员删除数据并收缩数据文件。他们知道在收缩操作进行时会有性能开销,所以他们在维护窗口期间执行此操作。   不幸的是,在操作完成后,用户开始抱怨系统性能下降,并且他们的许多查询比平时花费更长的时间才能完成。造成这种情况的原因是数据库中的索引已变得碎片化。为了理解这一点,让我们跟随数据库管理员缩小数据文件的路径,并探索操作期间发生了什么。但首先,让我们先回顾一下页面拆分和索引碎片的核心概念。   数据被存储在一系列 8 KB 的页面中,一系列连续的八个 8 KB 页面被称为一个区间(extent)。构成索引的数据页面存储在一个双向链表中。这意味着每个页面都存储一个指向下一个页面和上一个页面的指针。当一个数据页面变满时,下次 SQL Server 需要向该页面添加数据时,就必须创建一个新页面。这被称为页面拆分。页面拆分有两种类型,被称为“良性页面拆分”和“恶性页面拆分”。   要理解一个好的页面拆分,请参考图9.1中的示意图。该图说明了一个已满的索引。SQL Server 需要在最后一页插入一行新数据,但没有可用空间。因此,从区分配置中分配了下一页,并将新行插入到新页面上。                     当 SQL Server 需要拆分位于索引中间的页时,如果发生插入或扩展更新,就会发生不良页拆分。当这种情况发生时,SQL Server 必须创建的页将会乱序。当页乱序时,这被称为外部碎片。此外,为了在原始页上腾出空间,原始页的一半行将被移动到新页。这被称为内部碎片,并可能导致 SQL Server 必须读取更多的页才能返回相同数量的数据。不良页拆分如图 9.2 所示。                       在我们进一步探讨此情景之前,如果您希望跟随本节的示例,请使用清单9.3中的脚本来构建并填充MarketingArchive数据库。   注意:清单 9.3 中的脚本依赖于我们在第 6 章创建的 Marketing 数据库。 CREATE DATABASE MarketingArchive ; GO   USE MarketingArchive ; GO   CREATE […]

告别查词痛苦!这款免费软件Carlibre,让你的外文阅读效率提升10倍

告别查词痛苦!这款免费软件Carlibre,让你的外文阅读效率提升10倍   读外文原版书,你还在这样做吗? 遇到生词,手动敲到翻译软件里? 一边看书,一边手机开着查词页面? 来回切换窗口,思路频频被打断? 如果你的答案是“Yes”,那么今天这篇文章,就是为你准备的 “外文阅读救星”。 今天要介绍的主角,是一款免费、开源、功能极其强大的电子书管理&阅读神器——Calibre。很多人只知道它能把电子书格式换来换去(比如把azw3转成epub),却忽略了它内置阅读器中那个堪称外文阅读神器的功能:可配置的划词翻译。 为什么说它是外文阅读的神器? 想象一下这个场景: 你正在读一本英文原版小说,看到一个不错的句子,但其中有几个生词。你只需要用鼠标选中这个词组或整个句子,旁边立刻就会弹出一个翻译窗口,显示出精准的中文翻译。 更强大的是,这个翻译结果不是死板的。你可以让它调用谷歌翻译、必应翻译,甚至是需要付费但质量更高的DeepL,或者最新的ChatGPT/ChatGPT 。 不需要复制,不需要切换应用,不需要鼠标点来点去。 一切都在你的阅读界面里丝滑完成。 如何开启这个“外挂”功能? 第一步:配置你的专属翻译引擎(重点!) 打开一本英文书           像上面那样,选中一个单词, 在下面出现的图标,点第二个:查阅或搜索选中的文字 会跳出一个窗口,显示翻译                                                   当选中一段文字时,也有同样的效果。                                                   如何实现这个功能呢, 就是弹出界面里下面的添加资源按钮 点击添加资源按钮                   会显示自带的和自己添加的查询方式,每一个都有后台定义的网址, 我用的是bing 网址:https://cn.bing.com/translator?ref=TThis&text={word}           配置好就可以使用了,非常方便。   写在最后 在这个信息全球化的时代,语言不应该成为我们获取知识的障碍。 Calibre 就像一位沉默但强大的私人图书管理员,它不仅帮你把书架整理得井井有条,还给了你一把名为“划词翻译”的钥匙,让你能更自信、更流畅地打开外文世界的大门。 最关键的是,这一切都是免费的。 如果你还在为读外文书查词烦恼,不妨下载 Calibre,花十分钟配置一下。相信我,它会彻底改变你的阅读习惯。  

读书《100 SQL Server Mistakes and How to Avoid Them》第八章 SQL Server 安装

第八章 SQL Server 安装 本章涵盖 有意义的实例名称 使用正确的操作系统和 SQL Server 版本 自动安装 SQL Server 的占用空间 云中的安装注意事项   在本章中,我们将讨论用于安装 SQL Server 的各种选项。回顾十年前,规划 SQL Server 部署相对来说比较简单。我们需要选择最合适的 SQL Server 版本进行安装,并考虑如何安装它(图形界面还是脚本)。我们需要考虑实例所需的功能,并给它一个合适的名称,但大致就是这些。   快进到今天,SQL Server 的安装可能性无穷无尽。这可能导致一连串的错误,其中许多是由于在没有考虑更优替代方案的情况下进行传统安装而产生的。   在本章中,我们将为 MagicChoc 安装一系列 SQL Server 实例,并探讨可能等待意外数据库管理员 (DBA) 的一些陷阱。这将涵盖部署规划和执行的多个方面,从实例命名到选择合适的操作系统环境和 SQL Server 版本。我们还将探讨手动安装的挑战、安装不必要的功能以及实例过度合并的问题。   错误33# 使用晦涩的实例名称 在第2章中,我们讨论了命名标准以及使用未经深思熟虑的对象名称可能产生的后果。如果我们没有仔细考虑实例名称,SQL Server 实例也可能出现类似问题。为了探讨这一点,我们假设 MagicChoc 已经要求我们构建四个 SQL Server 实例,分布在两台服务器上,这些实例将托管以下应用程序使用的数据库:   Choc Maker, which is used by manufacturing Magic Sales, a sales team application Temperature Smart, an application used in the manufacturing process HR Manager, a tool used by the human resources team   有四个应用程序和四个实例,所以我们决定在两台服务器上创建四个实例,并为它们命名,使其与应用程序名称一致:   MagicServer1\ChocMaker MagicServer1\MagicSales MagicServer2\TempSmart MagicServer2\HRManager   当时,这听起来完全合理。然而几个月后,我们意识到这是一个错误。数据库负责人要求我们将巧克力制造商和温控智能应用整合到一个实例中。   我们现在有三个选项。我们可以让 Choc Maker 在一个名为 TempSmart 的实例中运行,这将可能在未来引起一定程度的混乱;我们也可以让 Temperature Smart 在一个名为 ChocMaker 的实例中运行,这同样会引起相同程度的混乱;或者我们可以将 SQL Server 实例重命名为 Manufacturing,这可能会产生大量额外工作,取决于应用程序如何访问该实例以及使用的功能。   那么,我是在推荐按部门而不是按应用来命名 SQL Server 实例吗?不,我当然不是。这可能会带来一系列挑战,例如在不同服务器上存在多个同名实例——更糟的是,可能需要在同一台服务器上有两个同名实例(这是不可能的)。当然,还存在部门在重组过程中更改名称的风险。例如,销售部门可能会拆分成两个部门,分别称为销售和市场。   我们如何命名实例取决于我们的组织以及 SQL Server 系统的结构。例如,除了根据应用程序或部门来命名实例外,它也可以以业务服务命名。   可能还需要有命名规范,其中包括名称中的各个方面。举一个例子,考虑实例名称 MAPRDENTLON01。这个实例名称通过两个字母的代码反映部门,MA = 制造。它通过三个字母的代码反映生态系统,在此例中 PRD = 生产。接下来的三个字母指定应用于该实例的许可类型。在此例中,ENT = 企业版。名称中的接下来的三个字符表示实例所在的位置:LON = 伦敦。最后,还有一个两位数的递增标识符。   简而言之,我的建议是应当对实例名称给予适当考虑,并以适当的方式进行命名,确保将来不会引起混淆。命名标准应在整个系统中保持一致。   错误34# 随意使用 Windows 直到2017年,SQL Server只能在Windows上运行。在两款产品均由微软开发的背景下,这一点非常合情合理。然而,在SQL Server 2017中发生了重大转变,微软引入了对SQL Server在Ubuntu、SUSE和Red Hat等Linux版本上运行的支持。它甚至支持在Docker容器中运行。但这难道不显得有点奇怪吗?我们难道不会只想在Windows上运行SQL Server吗?好了,让我们通过MagicChoc来探索一个场景。   在第4章中,我们讨论了MagicChoc如何觉得其流程过于分散,因此它委托开发了一个新应用程序,将其非互联网销售和采购功能整合到一个界面和一个后台中。我们没有讨论的是,这个应用程序将用Go语言编写,并将在运行Ubuntu的虚拟机上托管。性能和稳定性对该应用程序至关重要。   应用团队最初计划使用 MySQL,但 MagicChoc 的 DBA 团队在 MySQL 上没有经验。他们还想使用高级 SQL Server 功能,例如 AlwaysOn,应用团队意识到他们也可以受益于其他高级 T-SQL 功能,例如 HIERARCHYID 数据类型和窗口函数。   因此,决定让 DBA 团队在运行 Windows Server 2022 的虚拟机上构建 SQL Server 实例。但对于这种情况,这是一个错误。为什么这是一个错误的线索都在案例研究中。   首先,DBA 团队不想支持 MySQL,因为他们在该领域缺乏技能,但同时他们又希望基于 Linux 的开发人员切换到 Windows 环境来使用数据库引擎。如果 SQL Server 部署在 Linux 上,入门门槛本来会更低。   第二个原因是该应用程序正在 Ubuntu 上构建。这是一个无许可证的 Linux 发行版,这意味着成本是一个重要因素。许多企业应用程序都是在 Red Hat 或 SUSE 发行版上构建的,因为这些发行版有许可证费用,但也提供外部支持。如果 SQL […]

读书《100 SQL Server Mistakes and How to Avoid Them》第七章 错误处理、测试、源代码管理和部署

第七章 错误处理、测试、源代码管理和部署 本章涵盖处理 T-SQL 错误 代码故障排除 性能测试 现代开发实践   当人们想到数据库开发人员的角色时,很容易将注意力完全集中在编写高效的T-SQL代码上。然而,实际上,现代数据库开发人员必须考虑许多其他方面。错误处理是其中的第一个方面。如果一个过程在生产环境中抛出错误,我们希望能够优雅地处理该错误,以降低数据不一致等风险。错误处理甚至可以使代码重试,从而避免应用程序支持团队介入。我们也通常希望在错误发生时收到通知,以便进行调查。   我们还需要能够高效地调试代码错误。代码最终会有漏洞。这是生活的事实。当我们的代码中存在漏洞时,我们需要能够以高效的方式进行调试。如果写一段代码只需一天,但调试它却需要一周,那就毫无意义。因此,在处理复杂代码时,理解调试方法是至关重要的。   我经常看到SQL开发人员犯的一个常见错误是缺乏测试。我们将作为对SQL开发人员现代开发实践(缺乏)采用的更广泛的探讨的一部分来研究单元测试。我们将探讨将代码保存在源代码控制中、编写单元测试以及使用自动化构建和部署管道的好处。在第4章中,我们创建了一个名为MagicChoc的数据库,在第6章中,我们创建了一个名为Marketing的数据库。本章中我们将使用这两个数据库。   现代开发实践要求代码保存在源代码管理中,并使用 DevOps 流程来部署代码。这些实践多年来已经成为常态,但 SQL 开发人员似乎采用得较晚。我见过许多 SQL Server 团队仍将他们的“源代码”存储在数据库备份中,并手动运行复杂、精心制作的脚本来部署他们的数据层应用程序。本章中我们将探讨这两种错误。   开发工具 到目前为止,在本书中,除非我们探讨了 SQL Server 集成服务 (SSIS) 的时候,我一直在使用 SQL Server 管理工作室 (SSMS) 来创建示例,我假设你可能也一直在使用 SSMS 跟随操作。它对 SQL Server 专业人士来说非常熟悉,而且非常容易使用。 我们可以使用其他几种工具来开发 T-SQL,例如 Visual Studio Code、SQL Server 数据工具 (SSDT) 和 Azure Data Studio。对于一些高级开发技术,我们需要使用其中一些额外的开发工具。对于错误   错误26# 编写不处理错误的代码 如果我们的代码在生产环境中失败,最不希望发生的就是出现未处理的错误。如果我们的代码失败,我们希望它能够优雅地失败,并向应用支持团队提供有用的诊断信息,他们需要尝试解决问题。为了探讨这个问题,让我们首先查看一个没有错误处理的存储过程,并看看其中的一些后果。   清单7.1中的存储过程可以在MagicChoc数据库中创建。它是一个简单的存储过程,接受创建新销售订单所需的参数。它根据客户生成SalesOrderNumber所需的前缀,然后将记录插入到SalesOrderHeaders和SalesOrderDetails表中。   CREATE PROCEDURE dbo.InsertSalesOrder @SalesOrderNumber NVARCHAR(12), @SalesOrderDate DATE, @SalesPersonID INT, @SalesAreaID INT, @CustomerID INT, @SalesOrderDeliveryDueDate DATE, @SalesOrderDeliveryActualDate DATE, @CurrierUsedForDelivery NVARCHAR(32), @ProductID INT, @Quantity INT AS BEGIN BEGIN TRANSACTION   DECLARE @CustomerPrefix NVARCHAR(12) ; SET @CustomerPrefix = ( SELECT SUBSTRING(CustomerCompanyName,1,3) FROM dbo.Customers WHERE CustomerID = @CustomerID ) ;   INSERT INTO dbo.SalesOrderHeaders VALUES ( @CustomerPrefix + @SalesOrderNumber , @SalesOrderDate , @SalesPersonID , @SalesAreaID , @CustomerID , @SalesOrderDeliveryDueDate , @SalesOrderDeliveryActualDate , @CurrierUsedForDelivery ) ;   INSERT INTO dbo.SalesOrderDetails ( ProductID , Quantity , SalesOrderNumber ) VALUES ( @ProductID , @Quantity , @CustomerPrefix + @SalesOrderNumber ) ;   COMMIT END   让我们通过使用存储过程尝试创建一些销售订单来实践看看。下面清单中的脚本将执行我们的存储过程,销售订单将成功插入到 SalesOrderHeaders 和 SalesOrderDetails 表中。   EXEC dbo.InsertSalesOrder ‘1635D-U06’ , ‘2023-08-19’ , 1 , 1 , 2 , ‘2023-09-01’ , NULL , ‘Get Me There!’ , 5 , 1 ;   到目前为止,一切顺利!该过程运行成功,并且添加了一个销售订单。让我们使用清单7.3中的脚本再次执行存储过程。这一次,在执行存储过程之前,我们将把 SalesOrderDetails 表的名称更改为 SalesOrderLines。自然,这将在事务内部导致失败。   EXEC sp_rename ‘dbo.SalesOrderDetails’, ‘SalesOrderLines’ ; GO   […]

读书《100 SQL Server Mistakes and How to Avoid Them》第六章 SSIS开发

第六章 SSIS开发 本章涵盖 SSIS 简介和 SSIS 开发错误 丢失坏数据 不优化数据加载 将 SSIS 用作 T-SQL 编排工具 总是从源表中提取所有数据   SQL Server 集成服务,通常称为 SSIS,是随 SQL Server 企业版和标准版一起提供的工具,尽管标准版在功能上有所限制。它是一种提取、转换和加载 (ETL) 工具,允许开发人员在一个拖放式图形用户界面中构建数据移动和转换管道。   注意 本章中的示例将使用 SQL Server 数据工具,可以从 https://mng.bz/RNB0 下载。还需要安装 Integration Services 扩展,可以在“扩展 | 管理扩展”中找到,然后通过在市场中搜索 SQL Server Integration Services Projects 2022 进行安装。   一个 SSIS 包总是由一个控制流组成,该控制流协调包将运行的任务。在控制流中,我们可以创建零个或多个数据流,这些数据流用于在内存缓冲区中导入、导出和转换数据。   控制流上的任务由优先约束连接。这使我们能够设计我们的包以串行运行任务,而不是所有任务并行运行。遵循优先约束的任务总是在约束前的任务之后执行。   约束 SSIS 有三种类型的约束:成功、失败和完成。这些允许我们控制包的流程并创建自定义错误处理逻辑。连接到约束的任务具有以下行为: 与成功约束相关的任务将在前一个任务成功完成后执行。如果前一个任务失败,则不会执行。 与失败约束相关的任务将在前一个任务失败后执行。如果前一个任务成功,则不会执行。 与完成约束相关的任务将在前一个任务执行后运行。无论前一个任务成功还是失败,它都会被执行。 在 SSIS 控制流中,成功约束显示为绿色,失败约束显示为红色,完成约束显示为蓝色。   控制流中的优先级约束不应与数据流中的数据路径混淆。在数据流中,蓝色数据路径表示成功通过前一个组件的行将被传递到下一个组件。红色数据路径表示未通过前一个组件的行将被传递到下一个组件。没有完成数据路径。   当我们从集成服务项目模板创建项目时,我们会看到面前有一个空的控件流,并且左侧窗格中有 SSIS 工具箱。这个工具箱是我们可以将任务拖到控件流上或将组件拖到数据流上的地方。工具箱是上下文敏感的,因此如果我们处于数据流中,我们将看到数据流组件,而不是控件流任务。   每个数据流实际上都是我们控制流中的一个任务,因此我们可以通过将数据流任务拖到控制流上来创建一个数据流。然后,我们可以通过双击任务或切换到设计面板顶部的“数据流”选项卡来查看数据流。   连接管理器用于创建与数据源(如数据库和扁平文件)的连接。开箱即用的连接管理器包括 OLE DB 连接、ADO.NET 连接、文件、扁平文件和分析服务。为了性能,当在数据流中连接到 SQL Server 实例时,OLE DB 通常是最佳选择。ADO.NET 连接管理器通常用于在控制流中连接到 SQL Server 执行 T-SQL 语句任务。   OLE DB 配置 几年前,微软不再推荐使用 OLE DB。随后在 2018 年,它又被重新推荐,并发布了新的 OLE DB 驱动程序。然而,SQL Native Client 11 仍然不再推荐使用,并且不会随 SQL Server 2022 一起提供。因此,要在连接到 SQL Server 的应用程序(包括 SSIS)中使用 SQL Server OLE DB,您需要下载并安装 Microsoft OLE DB Driver 19 for SQL Server,下载地址为 https://mng.bz/AagK。 Microsoft Visual C 可再发行组件是此驱动程序的先决条件,因此需要先安装此组件。此可再发行组件可以在 https://mng.bz/ZVXO 找到。 一旦这些软件包安装完成,正确的 OLE DB 驱动程序将在提供程序下拉菜单中出现。然而,默认情况下,连接管理器会将“使用数据加密”属性设置为“强制”。除非您为 SQL Server 实例配置了证书,否则您需要将其更改为“可选”。可以使用连接管理器对话框中的“数据链接”按钮找到该属性,并导航到“全部”选项卡。   开发人员在开发 SSIS 包时可能会犯各种错误。在本章中,我们将探讨一种可能导致数据丢失的错误。我们将研究不优化数据加载的结果。我们还将查看仅将 SSIS 用作编排工具而未利用数据管道的影响。最后,我们将探讨未对数据提取进行过滤的错误。   为了探讨在使用 SSIS 时常见的一些错误,我们将继续使用 MagicChoc 示例,并使用以下场景。市场部将开始在网上投放广告,并希望跟踪潜在客户看到的展示次数(在线广告横幅的浏览次数)。展示数据将以 CSV 文件的形式接收,由 SSIS 包进行提取,该包计划每天运行一次。该包会将数据放入 Marketing 数据库的暂存模式中的一个表中。然后,它会将数据转换到 marketing 模式中的一个表中。最后,它会汇总数据并插入 reporting 模式中的汇总表,然后清空暂存表。   如果你希望跟随本章的示例,请查看本书代码仓库中的示例 CSV 文件。它的名称是 impressions.csv。你还需要使用下列清单中的脚本创建 Marketing 数据库。   CREATE DATABASE Marketing ; GO   USE Marketing ; GO   CREATE SCHEMA staging ; GO   CREATE SCHEMA marketing ; GO   CREATE SCHEMA reporting ; GO   CREATE TABLE staging.ImpressionsStage ( ImpressionUID […]

读书《100 SQL Server Mistakes and How to Avoid Them》第五章 T-SQL开发

第五章 T-SQL开发 本章涵盖 可能导致意外结果的错误 可能导致性能问题的错误 在T-SQL中避免使用游标循环 删除大量行的操作   SQL 是一种 ANSI 和 ISO 标准语言,允许数据库开发人员查询和操作关系数据库中的数据。T-SQL 是 SQL Server 的 SQL 语言方言,用于与 SQL Server 实例及其所托管的数据库进行交互。   在第4章中,我们为新的MagicChoc数据库设计并创建了表格。在本章中,我们将探讨一些较少经验的开发人员在T-SQL中可能犯的一些常见错误。作为T-SQL开发的示例,我们将参考MagicChoc,它希望我们开发将被其前端应用程序使用的逻辑。我们将利用这个机会开始探讨一些较少经验的开发人员在T-SQL中可能犯的一些常见错误。   对于更熟悉使用诸如 C 这样的语言编写应用程序代码的开发者来说,掌握 SQL 可能是一项挑战   T-SQL 中的大多数开发错误都会导致性能问题,这将是本章的主要关注点。然而,前两节将关注导致意外结果的错误。最后,我们将看一个在删除大量行时常见的错误。   错误14# 错误处理 NULL 值 MagicChoc 已要求我们查看数据并确认有多少产品子类别没有描述。因此,我们在以下清单中运行了查询。   SELECT COUNT(*) FROM dbo.ProductSubcategories WHERE ProductSubcategoryDescription = NULL ;   返回的结果是 0。太棒了。每个产品子类别都有描述,对吧?我们知道总共有 16 个子类别,所以让我们通过反转查询来再次检查结果,计算描述不为 NULL 的行数,使用以下列表中的查询。   SELECT COUNT(*) FROM dbo.ProductSubcategories WHERE ProductSubcategoryDescription <> NULL ;   等一下!这个查询的结果也返回了 0。这到底是怎么回事?一些刚接触 SQL 的开发者有时没有意识到,NULL 是一个未知值。因此,在比较中,NULL 不等于另一个 NULL。   要理解这一点,请考虑以下类比。我们的银河系中有多少颗星星?就我个人而言,我不知道这个答案。世界上有多少颗沙粒?同样,我个人也不知道。这是否意味着银河系中的星星数量等于世界上的沙粒数量?不,当然不等。可能相等,也可能不等。我不知道。因此,就像我不能说两个我不知道的数值是相同还是不同一样,SQL Server 也不能告诉我们两个它不知道的数值是相同还是不同。   为了解决这个问题,我们只需要在处理 NULL 值时调整语法,使用 IS 或 IS NOT,而不是 = 和 <>。例如,下面列表中的脚本成功返回了没有描述的产品子类别的计数,随后返回了有描述的产品子类别的计数。   SELECT COUNT(*) FROM dbo.ProductSubcategories WHERE ProductSubcategoryDescription IS NULL ;   SELECT COUNT(*) FROM dbo.ProductSubcategories WHERE ProductSubcategoryDescription IS NOT NULL ;   处理 NULL 值的另一个方面,可能会让 SQL 新手感到困惑,是使用 IS NULL 与 ISNULL() 函数。如我们刚刚所见,IS NULL 用于 WHERE 子句中以筛选结果集,使其仅返回列中包含 NULL 值的行。   另一方面,ISNULL() 函数用于 SELECT 列表、JOIN 子句或 UPDATE 语句的 SET 子句中,以将 NULL 值替换为非 NULL 值。例如,下面列表中的查询将返回完整的产品子类别列表,但 NULL 的描述将被替换为“无可用描述”这个值。   SELECT ProductSubcategoryName , ISNULL(ProductSubcategoryDescription, ‘No description available’) FROM dbo.ProductSubcategories ;   在处理 NULL 值时始终要小心。请记住,NULL 值不等于另一个 NULL 值。还要记住,IS NULL 语法用于筛选查询以返回 NULL 值,而 ISNULL() 函数用于将 NULL 值替换为非 NULL 值。   错误15# 将 NOLOCK 用作性能优化 MagicChoc 的销售应用程序有一个下拉框,里面填充了与客户相关的地址,这允许销售人员选择他们希望订单送达的收货地址。然而,当收货地址屏幕加载时,下拉列表加载很慢,我们被要求改进查询的性能。   我们听说在 SQL Server 中,锁定和阻塞可能会导致性能问题,有人提到有一个查询提示,称为 NOLOCK,可以提高性能。因此,我们修改了用于填充送货地址下拉菜单的查询,使其如下所示。   DECLARE @CustomerID INT ; SET @CustomerID = 2 ;   SELECT Street , […]

读书《100 SQL Server Mistakes and How to Avoid Them》第四章 数据库设计

第四章 数据库设计 本章涵盖了 SQL Server 中的设计错误以及为什么避免这些错误很重要 未能对数据库进行规范化的错误 设计和创建键时出现的错误   在本章中,我们将讨论在设计数据库时常见的错误。这些错误可能导致多种挑战,从而造成代码性能不佳等问题。   设计错误是在开发生命周期的最早阶段引入的,此时还没有编写任何代码。为了说明这一点,让我们关注 MagicChoc 公司。该公司认为其流程过于分散,因此委托开发一款新应用程序,该应用程序将把其非互联网销售和采购功能整合到单一界面和单一后台中。为此,MagicChoc 的管理团队表示他们希望存储以下数据项:   销售订单日期 销售订单号 销售人员姓名 销售人员邮箱 销售区域名称 销售区域经理 客户公司名称 客户联系人姓名 客户联系人邮箱 客户发票地址 客户送货地址 销售订单交货截止日期 销售订单实际交货日期 销售订单项目 销售订单数量 用于交货的快递 产品名称 产品库存水平 产品下次生产日期 产品下次生产数量 未完成订单 制造ID 产品类型名称 产品类型描述 产品类别名称 产品类别描述 产品子类别名称 产品子类别描述 供应商名称 供应商联系人姓名 供应商联系人邮箱 供应商地址 采购订单日期 采购订单号 采购订单项目 采购订单数量   在本章中,我们将设计存储这些数据所需的表结构。在此过程中,我们将探讨如何避免常见的设计错误。具体来说,我们将研究如果未能对数据库进行规范化可能出现的问题。然后,我们将研究与不当选择主键相关的性能问题。最后,我们将探讨不创建外键约束的后果。   错误11# 未能标准化 很多次,我问开发人员:“数据库是规范化的吗?”答案总是“是的!”不幸的是,在许多情况下,开发人员在数据库设计方面相对缺乏经验,他们误解了我的问题,以为我是在问“你设计的是联机事务处理(OLTP)数据库,而不是数据仓库吗?”实际上,他们并没有对数据库进行规范化,而只是凭自己的判断来决定数据库模式。   为了设计我们的数据库模式,我们将创建一个实体关系图(ERD),它是一个描述实体的图,实体是数据模型中的对象,以及描述数据对象的属性。ERD还描述了模型中对象之间的关系。   在接下来的章节中,我们将首先探讨设计数据库模式的错误方法,即使用判断方法。在这种方法中,我们仅依靠自己的经验来组织数据。由于每个人的经验不仅水平不同,而且本质上也不同,当我们使用这种无结构的方法时,会出现多种问题。我们将检查一些较为常见的问题,例如非原子值和数据重复。   最后,我们将探讨如何通过使用规范化来设计我们的数据库模式,以避免错误。我们将研究这种结构化的数据库建模方法,这种方法最早由埃德加·科德在20世纪70年代开发,并经受住了时间的考验。这是一系列有条理的步骤,用于消除数据冗余。在使用这种方法时,我们将遵循一套严格的规则,旨在避免数据重复并使模式尽可能高效。我们还将涉及如何使用实体关系图来测试我们的规范化,并了解数据泛化的概念。   依靠判断来设计模式 让我们用我们的判断来设计 MagicChoc 请求的新销售和采购数据库的模式。它希望建模 35 个数据项,所以让我们让事情更简单一些,把它们分成几个部分。让我们从销售订单数据开始——具体来说,是以下数据项:   销售订单日期 销售订单编号 销售人员姓名 销售人员邮箱 销售区域名称 销售区域经理 销售订单交付截止日期 销售订单实际交付日期 销售订单项目 销售订单数量 交付使用的快递   销售订单编号属性将是唯一的,因此这看起来像是我们实体的一个良好主键候选。棘手的方面是销售订单项与销售订单数量之间的关系。我们注意到存在明显的一对多关系。这是一种实体之间的关系,其中一个实体中的单个记录可以与另一个实体中的多个记录相关联。在这种情况下,任何给定订单可能订购多个产品,因此我们决定将订购的商品拆分到一个不同的实体中。我们还认为,由于我们有存储一般产品详细信息的需求,所以将产品详细信息排除,只关联到产品实体将是合理的,我们将很快设计该实体。   同样地,由于我们假设客户可能会下多个订单,并且我们知道根据需求需要存储客户详细信息,因此我们决定在创建 Orders 表时,应创建一个可以与 Customers 表连接的键。最后,我们注意到没有一个合适的主键候选,因此我们将创建一个人工键。这使我们得到了 ERD 的初步版本,如图 4.1 所示。                     接下来,我们将查看关于产品数据的要求,我们需要存储以下数据:   产品名称 产品库存水平 产品下次生产日期 产品下次生产数量 代工订单 制造ID 产品类型名称 产品类型描述 产品类别名称 产品类别描述 产品子类别名称 产品子类别描述   我们决定建立一个产品表,由于没有明显的自然键,我们将创建一个人工键,该键将与销售订单明细实体中的 ProductID 属性相链接。我们还注意到产品与产品类别之间可能存在一对多的关系,因此我们决定创建一个单独的产品类别实体,并通过另一个人工键将其与产品实体关联起来。如果我们将这些实体与销售订单实体结合起来,我们将得到图 4.2 所示的 ERD。                                 接下来,我们将构建 Customers 实体,我们知道它将具有以下属性:   客户公司名称 客户联系人姓名 客户联系人邮箱 客户发票地址 客户收货地址   为了避免出现宽而稀疏的表,我们决定将地址移到不同的实体中。我们还意识到,我们可以有一个单一的地址实体,同时关联客户发票地址和客户送货地址。如果发票地址与送货地址相同,这可以避免数据重复。我们通过为每种地址类型添加标志来实现这一点。我们将为这两个实体创建人工键。将这些实体附加到我们现有的设计中,会得到图 4.3 所示的 ERD。                                           最后,让我们为供应商和采购订单添加实体。我们需要为这些实体建模的数据属性是   供应商名称 供应商联系人姓名 供应商联系人邮箱 供应商地址 采购订单日期 采购订单编号 采购订单项目 采购订单数量   我们决定以类似于我们建模客户和销售订单的方式来建模供应商和采购订单。我们将拥有一个供应商实体,该实体将链接到我们现有的地址实体以存储供应商地址。我们将采购订单拆分为两个实体:一个用于采购订单头,另一个用于采购订单明细。同样,这是为了允许我们在同一订单中订购多个项目,而无需为每个项目重复订单数据。我们决定为供应商和采购订单明细创建一个人工键,但在采购订单头实体中使用采购订单号作为自然键。   如果我们将这些剩余的实体添加到我们现有的设计中,那么我们将得到图4.4所示的ERD。所以,在那里,我们有了一个完整的数据库模式设计。这并不难,是吗?但是使用这种方法有什么问题呢?问题在于,这个模式设计中存在多个错误,这些错误会使我们的数据库非常难以使用,并可能导致性能问题。在下一节中,我们将讨论一些错误及其后果。                   […]

读书《100 SQL Server Mistakes and How to Avoid Them》第三章 数据类型

第三章 数据类型 本章内容包括: 数据类型的重要性 使用错误标准数据类型的后果 使用高级数据类型的原因 处理 XML 和 JSON 数据的好处   错误6# 始终将整数存储为 INT 想象一下,我们有一个大型数据仓库。一个事实表有10亿行,并且与五个维度表关联,这些维度表各有30,000行。由于缓冲区缓存中的数据量大,性能很差且内存总是满的。当查询运行时,大量数据被写入TempDB。我们已经优化了查询,也已经审查了索引策略,并确保索引和统计信息都得到了良好的维护。看起来唯一能做的事情就是增加更多的硬件,但根据过去两年的趋势,我们怀疑如果增加更多的内存,只会将问题推到下一阶段。我们应该怎么做?一个起点是考虑审查我们的数值数据类型,特别是那些在主键/外键关系中使用的类型。   INT 是 SQL Server 中使用最广泛但也最容易被误用的数据类型。实际上,我们有四种专门用于存储整数的数据类型。这些数据类型在表中有详细说明。   整数类型   Data type Range Size (Bytes) TINYINT 0 to 255 1 SMALLINT –32,768 to 32,767 2 INT –2,147,483,648 to 2,147,483,647 4 BIGINT –9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 8   SELECT DATALENGTH(CAST(1 AS TINYINT)) AS TinyIntSize , DATALENGTH(CAST(1 AS SMALLINT)) AS SmallIntSize , DATALENGTH(CAST(1 AS INT)) AS IntSize , DATALENGTH(CAST(1 AS BIGINT)) AS BigIntSize ;     TinyIntSize    SmallIntSize    IntSize    BigIntSize 1              2               4          8   假设我们的五个维度表在其主键列中使用 INT 类型。每个维度表有 30,000 行,而 SMALLINT 数据类型可以表示的正值超过 32,000。这意味着如果我们预期维度不会大幅增长,那么我们可以在每行中节省 2 个字节。   此时,你可能会想,“我们为什么要在意节省2字节呢?”这个问题的答案需要一些简单的数学。每个五个维度表中都有30,000行。通过改用 SMALLINT,每个表我们只会节省 58 KB。但我们的事实表有 10 亿行。这意味着每个键可以节省 1.86 GB。将其乘以五个维度表,这意味着对于每个查询涉及事实表的所有行和所有五个键,我们将节省 9.3 GB。再将其扩展到数据仓库中的八个事实表。我们还应考虑索引的大小,这些索引是建立在这些外键列上的。现在考虑运行不同查询的并行会话。突然间,我们的数据类型选择对内存消耗产生了直接且显著的影响。   错误7# 始终使用可变长度字符串 想象一下,我们有一个存储美国地址的表。我们正努力确保数据尽可能节省空间。因此,我们对所有列使用可变长度字符串,包括地址的每一行以及邮政编码。我们知道缅因州的 Mooselookmeguntic 市和宾夕法尼亚州的 Kleinfeltersville 市拥有美国最长的城市名称,每个都有 17 个字符,所以我们将 CityName 列设置为 VARCHAR(17)。我们知道美国最长的州名是 Rhode Island and Providence Plantations,所以我们将 state 列设置为 VARCHAR(48)。我们知道邮政编码正好是 10 个字符,但因为我们知道每次长度都相同,我们应该使用 VARCHAR(10) 还是 CHAR(10)?这真的重要吗?无论哪种方式,数据长度都是 10 个字符,所以应该占用 10 个字节的空间,对吗?如果这是正确的,那么我们为什么还需要固定长度的字符串呢?事实是,这个假设并不正确。要理解原因,我们需要了解一些 SQL Server 是如何存储数据的。   SQL Server 将数据存储在一系列 8 KB 的页中,每八页组成一个 64 KB 的区块,这通常是读取的最小数据量。每个数据页都有一个 96 字节的页头,用于存储适用于整个页的信息,例如其唯一 ID 以及所属表(或索引)的对象 ID。   形成行的数据随后存储在页面上的槽中。然而,这些槽不仅存储数据。它们还必须存储少量元数据,以使数据有用。这些元数据包括关于存储在该槽中的记录类型的信息。例如,它是数据记录还是索引记录?它是否包含幽灵数据(已被逻辑删除但尚未实际移除的数据)?   其他元数据包括固定长度数据的长度(这不仅包括固定长度的字符数据,还包括整数等数据)、用于跟踪可变长度列是否包含 NULL 值的 NULL 值位图,以及版本标签,该标签用于诸如在线索引重建或具有乐观事务隔离级别的事务等操作。我们将在第 10 章讨论隔离级别。   然而,我们在这里真正关心的元数据部分是列偏移数组。它用于跟踪每个可变长度列在行中的起始位置。由于可变长度数据的长度可以是任意的,因此这个偏移表是 SQL Server 区分一段数据结束与下一段数据开始的唯一方式。   每个可变长度列在此表中都需要一个 2 字节的偏移量,这意味着每个可变长度列比固定长度列多使用 2 字节的空间。即使该列存储的是 NULL 值,这条规则仍然适用。因此,如果我们对邮政编码使用 CHAR(10),它将占用 10 字节的空间,但如果使用 VARCHAR(10),则会占用 12 字节的空间,尽管实际数据长度为 10 字节。   错误8# 编写你自己的层级代码 如果我们查看员工表,可能已经意识到 EmployeeID 和 ManagerID 列是用来表示员工层级的。因此,请参考图 3.2 中的组织架构图,它展示了 MagicChoc 高级管理团队的组织结构。   […]

读书《100 SQL Server Mistakes and How to Avoid Them》第二章 开发标准

第二章 开发标准   本章涵盖 开发标准简介 命名标准 编码标准 命名规范 编码标准 风格标准 技术标准   一个错误的服务器和DB命名例子                                   一个错误的存储过程例子   ALTER PROCEDURE dbo.proc01 AS BEGIN ;with t3 as (select col1, col2, col3 from tbl03) select t1.col1, t2.col2, t1.col2, t1.col3, t3.col1, t3.col2 from dbo.tbl01 t1 inner join tbl02 t2 on t1.col1 = t2.col1 and t1.col3 < 55 inner join t3 on t3.col1 = t1.col1 union select col1, col2, col3, NULL, NULL, ‘0’ from dbo.tbl04 ; END   错误 1: 无描述性的对象名称 一个命名不规范的例子                           不明晰的参数,一个函数名称   CREATE PROCEDURE sp_orders @CustomerID INT, @LineItems XML, @AddressID INT, @Address INT, @date DATETIME AS BEGIN DECLARE @Stock INT = 0 ; DECLARE @Product INT = 0 ;   INSERT INTO tbl_orders ( CustomerID, LineItems, BillingAddressID, DeliveryAddressID, Date ) VALUES ( @CustomerID, @LineItems, @AddressID, @Address, @date ) ;   SET @Stock = @LineItems.value(‘(/Product/@qty)[1]’, ‘int’) ; SET @Product = @LineItems.value(‘(/Product/@ProductID)[1]’, ‘int’) ;   EXEC sp_stockUpdate @product, @stock ;     修改后的函数例子:更新了参数名字, 使更具意义   CREATE PROCEDURE sp_stockUpdate @ProductStockLevel INT,               ① @StockID INT                          ① AS BEGIN UPDATE tbl_products SET StockQty = StockQty – @productStockLevel WHERE ProductID = […]
Recent Posts

HiddenMerit Daily · Issue 39

# 📊 HiddenMerit Daily · Issue 39 > **Focus on Database Frontiers, Practical Insights for DBAs** > June 9, 2026 | 5 Selected Global Breaking News ## 01|MongoDB 8.3 “AI‑Native” Launches Domestically, Alibaba Cloud Fires First Shot in AI Database Race In early June, Alibaba Cloud became the first in China to launch MongoDB 8.3. MongoDB 8.3 introduces an “AI‑Native” design philosophy – not “add‑on” AI support, but deep integration of three major capabilities – vector search, auto‑embedding, and intelligent O&M – directly into the database engine, achieving a trinity of “native search, native vectorisation, native O&M.” The three native AI capabilities in detail: – **Native Search**: Vector and full‑text search are built into the engine layer; a single pipeline completes hybrid search combining “vector + full‑text + scalar” (with `$rankFusion` stage using the RRF algorithm for score fusion), eliminating the need for applications to switch between multiple systems. – **Native Vectorisation**: Write‑time auto‑embedding, transparent to applications, zero sync overhead; the engine layer automatically listens for data changes via Change Stream, calls models to generate vectors, writes back to documents, and triggers index updates. – **Native O&M**: Natural language management, AI‑assisted slow query analysis, index recommendations, and parameter tuning, covering […]

HiddenMerit Daily · Issue 38

# 📊 HiddenMerit Daily · Issue 38 > **Focus on Database Frontiers, Practical Insights for DBAs** > June 8, 2026 | 5 Selected Global Breaking News ## 01|Alibaba Cloud Launches MongoDB 8.3 Domestically: Three “Native” Capabilities for Vector Search, Auto‑Embedding, and Intelligent O&M On June 1, Alibaba Cloud became the first in China to launch MongoDB 8.3. This version deeply integrates three major AI capabilities – vector search, auto‑embedding, and intelligent O&M – directly into the database engine, moving away from “add‑on” AI solutions and achieving an AI‑Native design philosophy of “no data movement, no capability assembly, simplified architecture.” **Three Native AI Capabilities**: – **Native Search**: Vector and full‑text search are built into the engine layer; a single pipeline completes hybrid search combining “vector + full‑text + scalar,” eliminating the need for applications to switch between multiple systems. – **Native Vectorisation**: Write‑time auto‑embedding, transparent to applications, zero sync overhead; the entire flow from data write to vector generation is completed within the same database. – **Native O&M**: Natural language management; AI‑assisted slow query analysis, index recommendations, and parameter tuning, covering all versions. MongoDB 8.3 also delivers impressive OLTP performance: compared to version 8.0, write throughput increases by 35%, read throughput […]

HiddenMerit Daily · Issue 37

# 📊 HiddenMerit Daily · Issue 37 > **Focus on Database Frontiers, Practical Insights for DBAs** > June 5, 2026 | 5 Selected Global Breaking News ## 01|Microsoft Azure HorizonDB Launches Public Preview: A PostgreSQL Rebuilt for AI Agents, Storage Engine Rewritten in Rust On June 2, at the Build 2026 conference keynote, Microsoft officially announced that **Azure HorizonDB** has entered public preview. This is a PostgreSQL‑compatible cloud database rebuilt from the ground up for agentic AI workloads, now available in 5 Azure regions globally. HorizonDB is not a simple upgrade to Azure Database for PostgreSQL, but a completely new architectural design. Key technical points: – **“Database‑as‑a‑log” architecture**: Transactions are committed directly to a shared WAL (Write‑Ahead Log) storage, achieving sub‑millisecond multi‑region commit latency and eliminating the multi‑step coordination overhead of traditional PostgreSQL. – **Rust storage engine**: Microsoft explicitly chose Rust over C/C++ to eliminate memory safety vulnerabilities such as buffer overflows at the language level. This design is highly significant for unattended AI agent high‑frequency query scenarios. – **Storage‑compute separation**: Storage automatically scales to 128TB, compute can scale to 3072 vCores, and supports up to 15 read replicas. – **DiskANN vector search**: Natively embedded vector search capability, supporting vectors […]

HiddenMerit Daily · Issue 36

# 📊 HiddenMerit Daily · Issue 36 > **Focus on Database Frontiers, Practical Insights for DBAs** > June 4, 2026 | 5 Selected Global Breaking News ## 01|Microsoft Azure HorizonDB for PostgreSQL Launches Public Preview: Cloud Database Designed for AI Workloads On June 2, Microsoft officially announced the public preview of **Azure HorizonDB for PostgreSQL**, a cloud‑native PostgreSQL database built specifically for AI applications, deeply integrating native AI capabilities such as vector search, large model inference, and intelligent index optimisation. HorizonDB is built on the PostgreSQL kernel and is specially optimised for emerging workloads such as generative AI, retrieval‑augmented generation (RAG), and AI agents. Core capabilities include: – **Native Vector Search**: Kernel‑level support for vector indexing, eliminating the need for additional vector database extensions, with significantly better vector query performance than the community pgvector extension. – **AI Inference Integration**: Built‑in AI functions allow direct invocation of Azure OpenAI services within SQL for embedding generation and text inference. – **Intelligent Auto‑Tuning**: Workload‑aware optimisation based on machine learning, automatically adjusting index strategies and query execution plans. Microsoft stated that HorizonDB aims to solve the “data fragmentation” pain point in AI application development – developers no longer need to move data between multiple […]