问题比较简单。我需要计算 3 列,其中中间结果是大数,并且我在早期遇到问题,SQL Server 基本上四舍五入,无论是否进行任何强制转换/转换。
例如,让我们做一个简单的除法 1234/1233。计算器将产生 1,00081103000811。但是当我在 SQL Server 上执行此操作时,我们得到以下信息:
-- Result: rounded at 1.000811000... with trailing zeroes up until the 37 precision
SELECT CAST(CAST(1234 AS DEC(38,34))/CAST(1233 AS DEC(38,34)) AS DEC(38,37))
-- Result: rounded at 1.000811
SELECT CONVERT(DECIMAL(38,32), 1234)/CONVERT(DECIMAL(38,32),1233)
-- Correct result at 1,00081103000811
-- But this requires the zeroes to be put in manually when you don't
-- even know the precision of the end result
SELECT 1234.0/1233.00000000000000
Run Code Online (Sandbox Code Playgroud)
为什么会发生这种自动舍入?当您无法确定一个数字(int 或 dec 部分)有多大时,计算超长十进制值的最佳方法是什么,因为该表可以包含各种不同的值?
谢谢!
我们的项目运行着一个非常大、非常复杂的数据库。所以大约一个月前,我们注意到包含空值的索引列使用的空间变得太大。作为对此的回应,我编写了一个脚本,该脚本将动态搜索包含超过 1% 的空值的所有单列索引,然后在该值不为空的情况下删除并重新创建这些索引作为过滤索引。这将删除并重新创建整个数据库中的数百个索引,通常会释放整个数据库使用的近 15% 的空间。
现在我有两个问题:
A) 以这种方式使用过滤索引的缺点是什么?我认为它只会提高性能,但是否涉及任何性能风险?
B) 我们在删除和重新创建索引时收到错误(“不能删除索引 XYZ,因为它不存在或您没有权限”),即使在事后检查时,一切都完全按预期进行。这怎么会发生?
谢谢你的帮助!
编辑:回应@Thomas Kejser
嗨,谢谢,但事实证明这是一场灾难。当时我们不明白一些事情,例如:
最终,这些更改被还原。因此过滤索引是一个强大的工具,但您需要真正了解从这些列中提取的数据。除了空间问题之外,普通索引很容易应用,过滤索引代表了非常定制的解决方案。它们当然不是常规索引的替代品,而是在需要它们的特殊情况下对它们的扩展。
似乎很难找到系统版本化时态表与旧选项(例如 DB 触发器和 CDC)之间的比较。我目前没有时间在 SQL Server 2016 上编写扩展测试,所以我想我会在这里问一下。
基本上,触发器的典型优势是它们在独立和集群/alwaysOn 环境中更易于管理,可以实时同步,并且可以访问会话数据,例如用户 ID。
另一方面,CDC 虽然需要更多的管理并且是异步的,但要轻得多,因此性能要好得多。因此,如果对触发器引起的瓶颈可能成为问题有任何疑问,CDC 基本上将是最佳解决方案。在硬件要求方面,由于使用日志和 cdc 审计表来跟踪更改,CDC 对额外空间的要求可以忽略不计。
问题:时态表与上述两个相比如何?在速度、性能、存储空间使用方面。何时应该使用时态表而不是触发器或 CDC?我什么时候不应该?
我理解任何可能复杂的事情,因为 DB 审计背后的业务需求和技术限制不会有一个简单的答案,因为它在很大程度上取决于项目的要求和范围。但是,如果能对上述问题有更多的了解,我们将不胜感激。谢谢!
2021年编辑:由于对此一直有一些兴趣,几年后我已经非常熟悉上述所有内容,这里是性能方面的简短摘要:我对 SQL Server 2016 的某个版本进行了测试,涉及插入、更新和删除10000 行来自 40 个不同类型的表一一列出,并绘制了每个表的总体时间、基本锁定信息等。简单的总结是,触发器平均为操作增加了 500-1000% 的延迟,而使用临时表和 CDC 时,每个操作的额外延迟接近 10%。如果我有确切的结果会有所帮助,但我不再记得它们了。触发过程非常简化,但每个更改的列插入一行,而 temporal / cdc 插入一行,而不管其中更改了多少列。在这个意义上,由于同时插入多行的键争用,一些更改可能使触发器看起来比它们更慢。然而,很明显触发器是最不适合简单审计的工具。所以这是我在创建这篇文章时试图理解的差异的简单技术概要:
触发器只有在你真的需要一些内置到数据库中的自定义逻辑时才有用,以监视 DML 更改,修改特定数据,在特定实例中捕获用户 ID 等。但尽量避免它们像瘟疫一样。他们的表现很糟糕。如果您需要审计或日志记录,它们是您应该查看的最后一个地方。
临时表一旦运行起来就非常容易管理,尤其是在像 Always On 这样的 HADR 中。由于它们支持压缩并反映从父表到历史表的大多数模式更改,因此它们需要很少的维护。特别是对于新的 SQL Server 版本,您可以设置保留期以删除超过 x 年的数据,因此存储和清理方面的考虑也可以忽略不计。它们就像事情来临时一样容易被遗忘,除非对需要更改数据的父表进行一些奇特的更新,在这种情况下,您必须取消链接,修改父表和历史记录表,然后再次链接它们。但这些都是罕见的,而且相对容易做到。时态表包很健壮,可以很好地处理错误,因此您会发现它很难被意外破坏。
然后,CDC 非常适用于报告服务或类似的场景,在这些场景中,您不介意异步数据,但您需要分析更改,例如每晚批处理。您可以将保留设置设置为仅保留 x 天的数据,以将存储成本降至最低。也就是说,根据我的经验,CDC 很挑剔而且不是很稳定。DML 有时会在没有警告的情况下“破坏”它,因此您可能需要数据库级 DDL 触发器来警告您 CDC 跟踪的对象发生了变化。您可能还需要为 HADR 设置自定义监视作业,因为它本身不处理故障转移事件。并且 CDC 有一个非常讨厌的倾向,即在被禁用后无法重新启动,这与使用 MS 自己的作业未正确更新它的状态有关。这意味着它偶尔需要手动工作以确保正确删除清理和捕获作业及其引用。也就是说,SSIS / RS 集成得非常好,使他们可以轻松使用 …
trigger sql-server change-data-capture temporal-tables sql-server-2016
所以我最近才在始终在线的 AG 上启用 CDC。虽然许多消息来源声称在alwaysON AG上启用CDC就像在任何独立服务器上一样简单,但事实证明事实并非如此。正如 MS所说:
您应该在故障转移之前在所有可能的故障转移目标上创建[CDC 捕获和清理]作业,并将它们标记为禁用,直到主机上的可用性副本成为新的主副本。当本地数据库成为辅助数据库时,还应禁用在旧主数据库上运行的 CDC 作业。若要禁用和启用作业,请使用 sp_update_job (Transact-SQL) 的 @enabled 选项。有关创建 CDC 作业的详细信息,请参阅 sys.sp_cdc_add_job (Transact-SQL)。
事实证明,在我们在主副本上启用 CDC 后,辅助副本上绝对没有发生任何事情。因此,根据 MS 的建议,我进行了手动故障转移,并在那里创建了作业,然后又进行了一次故障转移回原始主数据库。
显然,正如上面引用中所暗示的那样,尽管 CDC 应该与alwaysOn AG 完全兼容,但它完全无法确定它当前是在主副本还是辅助副本上运行。
那么,背景介绍完了,让我们来回答我的问题:如何保证 CDC 作业始终在当前主副本上启用?并始终在辅助设备上禁用(以免产生错误)?
显然,alwaysOn AG 的一个重要点是,无论出于何种原因,您都不需要在主数据库因任何原因发生故障时手动到达那里,因为辅助数据库将弥补空闲并保持系统在线,我认为然后预计它会创建每个副本上的一个作业,不断轮询它们的状态并改变 CDC 作业的状态?
如果这是正确的,您对如何进行此操作有什么具体建议、具体脚本或指南等吗?因为显然这在全球范围内是一个相当普遍的困境,我必须在每个副本上手动创建一个新的 TSQL 作业,只是为了使用alwaysOn AG 执行 CDC 的基本基本功能,这似乎很奇怪。
这似乎不太可能,这就是我问的原因。或者我误解了什么?谢谢!
由于我们的一个表增长超出了 INT 的范围,这是在生成 DB 时未预测到的,我们需要将 PK 字段改为 BIGINT。
然而,在处理该表的第一步中,我们遇到了有关 tempdb 空间使用的问题:尝试删除其上的主键约束,以便我们可以更改它。我们的测试环境都没有为 tempdb 保留超过 50 GB 的空间,而该表目前占用大约 120 GB 的空间。该表只有 5 列,它们仅包含其他 INT 和一些短的 varchar。
在涉及 tempdb 和 logfile 时,你们有没有更好的,也许更轻松的建议来进行相同的更改?该表只有几列,所有列都包含很少的数据。示例如下,尽可能简单。
ALTER TABLE OURTABLE DROP CONSTRAINT OURTABLE_PK
ALTER TABLE OURTABLE ALTER COLUMN OURTABLE_ID BIGINT NOT NULL
ALTER TABLE OURTABLE ADD CONSTRAINT OURTABLE_PK PRIMARY KEY CLUSTERED (OURTABLE_ID)
Run Code Online (Sandbox Code Playgroud)
谢谢!
我一直在考虑创建一个具有相同定义和 BIGINT 作为批量插入的新表,然后在删除旧表的同时重命名它。但我不确定这是否会被证明是更好的解决方案,因为无论如何都必须在之后加入约束。
我需要将 XML 从文件解析为表,同时通过自定义生成的 ID 值保留它们的关系。
例如,如果我有以下 XML:
<root>
<construction>
<constructionName>randomname1</constructionName>
<project>
<projectname>another randomname</projectname>
<businesspartners>
<partnername>bilbo bagginses</partnername>
</businesspartners>
<employees>
<employee>
<empname>frodo</empname>
<empaddress>etc...</empaddress>
</employee>
</employees>
</project>
<info>
<randElement></randElement>
</info>
<constructionType>houses</constructiontype>
</construction>
<construction>
<...(etc, same as above, times n^10)/>
</construction>
</root>
Run Code Online (Sandbox Code Playgroud)
由此,我需要生成到下表的数据:
CONSTRUCTION (CONSTRUCTION_ID INT PRIMARY KEY
, CONSTRUCTIONNAME VARCHAR..
, CONSTRUCTIONTYPE VARCHAR.. )
PROJECT (PROJECT_ID INT PRIMARY KEY
, CONSTRUCTION_ID INT FOREIGN KEY REFERENCES CONSTRUCTION
, PROJECTNAME VARCHAR.., )
BUSINESSPARTNERS (BUSINESSPARTNERS_ID INT PRIMARY KEY
, PROJECT_ID INT FOREIGN KEY REFERENCES PROJECT
, …
Run Code Online (Sandbox Code Playgroud) 几天前我们发生了一系列崩溃和恢复,在这些之后,SQL Server 数据库一直表现得很奇怪。我们知道故障转移集群存在一些问题,因此我们不得不再次启动服务器以最终使数据库看似正常工作。
随之而来的问题之一是,我们运行了一个大脚本,该脚本动态删除现有索引并重新创建它们,唯一的区别是它们现在使用 WHERE 列 NOT NULL 进行过滤。然而,出于某种原因,当我从 SSMS 对象资源管理器中选择 SCRIPT INDEX -> CREATE TO -> NEW QUERY WINDOW 时,它提供了基本的索引创建脚本,其中不过滤索引。当我们具有完全权限的客户执行相同操作时,创建脚本会正确显示它已被过滤。
这可能是权限问题(在 Google 上没有找到任何此类问题),还是有可能在脚本正确记录更改时,节点或查询优化器或任何不同步的东西?
类似地,以前执行良好的几个查询(并且仍然在作为此数据库副本的不同数据库中执行良好),现在通过执行计划显示它们的行为有所不同。例如,其中之一有以下问题:
这些问题的原因可能是什么,甚至如何开始诊断问题?如上所述,数据库是彼此的副本。唯一的区别是问题数据库崩溃了,数据库被移动到故障转移集群,然后再次返回到正确的节点。索引没有碎片化,统计数据刚刚更新,SQL Server 的查询计划器似乎负载过重。
我真的很感激一些关于可能是什么原因以及我将如何诊断实际问题的专家建议,谢谢。
sql-server ×6
clustering ×1
decimal ×1
import ×1
index ×1
performance ×1
tempdb ×1
trigger ×1
xml ×1