当我运行以下代码时,它需要 22.5 分钟并进行 1.06 亿次读取。但是,如果我只运行内部 select 语句本身,它只需要 15 秒并执行 264k 读取。作为旁注,选择查询不返回任何记录。
知道为什么IF EXISTS
它会运行更长时间并进行更多读取吗?我还更改了 select 语句,SELECT TOP 1 [dlc].[id]
并在 2 分钟后将其杀死。
作为临时修复,我已将其更改为执行 count( * ) 并将该值分配给变量@cnt
。然后它做一个IF 0 <> @cnt
声明。但我认为EXISTS
会更好,因为如果在 select 语句中返回了记录,它会在找到至少一条记录后停止执行扫描/搜索,而count(*)
将完成完整查询。我错过了什么?
IF EXISTS
(SELECT [dlc].[ID]
FROM TableDLC [dlc]
JOIN TableD [d]
ON [d].[ID] = [dlc].[ID]
JOIN TableC [c]
ON [c].[ID] = [d].[ID2]
WHERE [c].[Name] <> [dlc].[Name])
BEGIN
<do something>
END
Run Code Online (Sandbox Code Playgroud) 在 SQL Server 中,行存储表上的非唯一非聚集索引在非聚集索引结构的所有级别合并了基础对象的书签(RID 或聚集键)。书签作为非聚集索引键的一部分存储在所有索引级别。
另一方面,如果非聚集索引是unique,则书签仅存在于索引的叶级别 - 不作为键的一部分(实际上,书签作为一个或多个包含的列存在)。
在 SQL Server 2016 中,可以在面向列的表(具有聚集列存储索引的表)上构建非聚集 b 树索引。
我们的 ETL 流程有一个长时间运行的 SELECT INTO 语句,它动态地创建一个表,并用数亿条记录填充它。
该声明看起来像 SELECT ... INTO DestTable FROM SrcTable
出于监控目的,我们希望大致了解此语句在执行时的进度(大约行计数、写入的字节数或类似内容)。
我们尝试了以下方法无济于事:
-- Is blocked by the SELECT INTO statement:
select count(*) from DestTable with (nolock)
-- Returns 0, 0:
select rows, rowmodctr
from sysindexes with (nolock)
where id = object_id('DestTable')
-- Returns 0:
select rows
from sys.partitions
where object_id = object_id('DestTable')
Run Code Online (Sandbox Code Playgroud)
此外,我们可以在 中看到事务sys.dm_tran_active_transactions
,但我无法找到一种方法来获取给定的受影响行数transaction_id
(类似于@@ROWCOUNT
也许,但带有transaction_id
as 参数)。
我知道在 SQL Server 上,SELECT INTO 语句是 DDL 和 DML 语句合二为一,因此,隐式表创建将是一个锁定操作。我仍然认为必须有一些聪明的方法来在语句运行时获取某种进度信息。
我希望在我的生产环境中部署日志传送。这意味着我将不得不管理备份链,这些备份链可能会被人们进行备份以刷新开发环境而中断。如果我遇到故障并想使用日志备份恢复到某个时间点,我还需要开发人员进行备份。
如果开发人员仅使用 COPY ONLY 备份,则不会发生这种情况。所以我的问题是:有没有办法限制用户只能执行 COPY ONLY 备份?
根据我对查询如何编译、存储和检索查询计划的有限了解,我了解多语句查询或存储过程将生成它的查询计划,该查询计划将存储在查询计划缓存中,以供查询在未来执行中使用。
我认为这个计划是通过查询哈希从查询计划缓存中检索的,这意味着如果查询被编辑和执行,哈希是不同的,并且会生成一个新计划,因为在查询计划缓存中找不到匹配的哈希。
我的问题是:如果用户执行的语句是多语句查询中的语句之一,它是否可以将缓存中已有的查询计划的相关部分用于多语句查询?我希望答案是否定的,因为哈希值显然不匹配,但是在多语句查询中对每个语句进行哈希处理是否更好,以便用户可以从查询中运行单个语句来使用它们?
我希望有一些我没有考虑到的并发症(我真的很想知道这些),但似乎我们可以在许多查询计划中存储相同的“语句计划”,占用更多空间和更多CPU 和生成时间。
可能只是显示我的无知。
更新时态表中的一行时,该行的旧值存储在历史表中,事务开始时间为SysEndTime
. 当前表中的新值将事务开始时间作为SysStartTime
。
SysStartTime
和SysEndTime
是时datetime2
态表用于记录某行何时是当前版本的列。事务开始时间是包含更新的事务开始的时间。
BOL 说:
系统 datetime2 列中记录的时间基于事务本身的开始时间。例如,在单个事务中插入的所有行都将在对应于 SYSTEM_TIME 周期开始的列中记录相同的 UTC 时间。
示例:我开始更新 Orders 表中的所有行,20160707 11:00:00
事务运行需要 5 分钟。这会在历史记录表中为每一行创建一行SysEndTime
as 20160707 11:00:00
。当前表中的所有行都有一个SysStartTime
of 20160707 11:00:00
。
如果有人在20160707 11:01:00
(更新正在运行时)执行查询,他们将看到旧值(假设默认读已提交隔离级别)。
但是,如果有人然后使用AS OF
语法来查询时态表,因为20160707 11:01:00
他们会看到新值,因为他们SysStartTime
将是20160707 11:00:00
.
对我来说,这意味着它不会像当时那样显示这些行。如果它使用事务结束时间,则问题将不存在。
问题:这是设计使然吗?我错过了什么吗?
我认为它使用事务开始时间的唯一原因是它是事务开始时唯一的“已知”。它不知道事务开始时何时结束,并且在结束时应用结束时间需要时间,这会使它应用的结束时间无效。这有意义吗?
这应该允许您重新创建问题。
假设用户 foo 拥有数据库 gee,并且用户 foo 已授予用户 bar 对数据库 gee 的所有访问权限
gee=> \l
List of databases
Name | Owner | Encoding | Collate | Ctype | Access privileges
-----------+----------+----------+-------------+-------------+-----------------------
gee | foo | UTF8 | en_US.UTF-8 | en_US.UTF-8 | =Tc/foo +
| | | | | foo=CTc/foo +
| | | | | bar=CTc/foo
Run Code Online (Sandbox Code Playgroud)
我们转储这个数据库,
$ pg_dump -U foo -v -f gee.dump gee
Run Code Online (Sandbox Code Playgroud)
在数据库中,gee 用户栏创建了一个架构。因此,因为所有这些用户我们将数据库 gee 恢复为用户 postgres(在用户 foo 创建了数据库 goo 之后),
$ sudo -u postgres psql -d goo -f gee.dump …
Run Code Online (Sandbox Code Playgroud) 因此,让我首先从我对 MongoDb 如何将数据存储在磁盘上的理解开始这个问题:因此,当您在 mongodb 中创建数据库时,它会分配一个名为的大文件,<databasename>.0
并在该文件中分配与特定数据对应的连续区域的范围集合或特定索引。
在填充此数据文件时,它会创建一个名为的新文件<databasename>.1
并以类似方式填充它。因此,假设最近插入到特定数据库中的数据将位于编号最高的文件中似乎是明智的(我的性能测试证实了这一点)。
但是,我看不出这对索引来说是如何正确的……因为我们在谈论 bTree,所以让这个 bTree 以相同的方式分散在文件中似乎是不可能/明智的。由于 Mongo 正在对索引进行维护,整个索引是否存在于一个范围内,直到它超出它,然后将其重新定位到当前(编号最高的数据文件)?
这对我来说变得很重要,因为当从 Amazon EBS 快照启动数据库时,在卷预热之前访问这些数据文件似乎有巨大的开销。我只对集合中最近 N 个文档的一个子集感兴趣。如果我可以确定我只需要最近的几个数据文件,我可以在启动 mongod 之前通过顺序读取来预热这些文件。
在我的应用程序中,我必须执行分布式锁定模式。因为我们已经有一个 SQL Server 实例可以使用,所以我们决定在我们的 Web 应用程序的 SQL 层实现锁定是最容易的。
可以根据多种条件获得锁,包括:
出于所有意图和目的,将上述两个条件视为int
数据类型。
在这种模式中,我们希望将我们所有的锁都视为 FIFO,我相信SERIALIZABLE
隔离级别会给我们带来这种好处。
以下是我们建议如何执行“锁定”:
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
IF EXISTS (SELECT 1 FROM locks WHERE LockType = @LockType AND ApplicationIdentifier = @ApplicationIdentifier)
BEGIN
-- Awesome, the lock will be acquired
INSERT INTO locks OUTPUT INSERTED.LockId VALUES (2,3)
END
ELSE
BEGIN
-- Someone already has the lock
SELECT -1
END
SET TRANSACTION ISOLATION LEVEL READ COMITTED
Run Code Online (Sandbox Code Playgroud)
和“解锁”:
DELETE FROM locks WHERE LockId = @LockId
Run Code Online (Sandbox Code Playgroud)
所以我的问题有两个方面: …
我有一个存储过程,它使用 Id 列表填充临时表 #employee_benefits。该表最终大约有 10,000 行长。下面的查询然后从一个名为 EmployeeBenefitData 的表中进行选择,该表有大约 400 万行。
SELECT ebd.EmployeeBenefitDataId, ebd.EmployeeBenefitId, ebd.[DataDefinitionId]
FROM #employee_benefits eb
INNER JOIN EmployeeBenefitData ebd ON eb.EmployeeBenefitId = ebd.EmployeeBenefitId
Run Code Online (Sandbox Code Playgroud)
瓶颈是 EmployeeBenefitData 表上的索引扫描。它首先进行索引扫描,然后将其加入临时表。临时表充当过滤器,这意味着在连接之前扫描所有数据的效率非常低。我添加了以下代码以将扫描更改为搜索并显着减少所需的读取量。
DECLARE @MinEmpBenId INT, @MaxEmpBenId INT
SELECT @MinEmpBenId = MIN(EmployeeBenefitId), @MaxEmpBenId = MAX(EmployeeBenefitId)
FROM #employee_benefits
SELECT ebd.EmployeeBenefitDataId, ebd.EmployeeBenefitId, ebd.[DataDefinitionId],
dd.TypeId, dd.DataDefinitionId, dd.Name, ebd.[Value], ebd.[Date], ebd.[Text]
FROM #employee_benefits eb
INNER JOIN EmployeeBenefitData ebd ON eb.EmployeeBenefitId = ebd.EmployeeBenefitId
INNER JOIN DataDefinition dd ON ebd.DataDefinitionId = dd.DataDefinitionId
WHERE ebd.EmployeeBenefitId >= @MinEmpBenId AND ebd.EmployeeBenefitId <= @MaxEmpBenId
Run Code Online (Sandbox Code Playgroud)
它对客户端统计数据产生了巨大的影响 …
sql-server ×7
backup ×2
performance ×2
columnstore ×1
exists ×1
index ×1
insert ×1
locking ×1
mongodb ×1
permissions ×1
pg-dump ×1
plan-cache ×1
postgresql ×1
security ×1
transaction ×1