标签: sql-server

为什么这个带有参数的递归 CTE 在使用文字时不使用索引?

我在树结构上使用递归 CTE 来列出树中特定节点的所有后代。如果我在我的WHERE子句中写了一个文字节点值,SQL Server 似乎实际上只是将 CTE 应用于该值,给出一个实际行数较低的查询计划,等等

带有文字值的查询计划

但是,如果我将该值作为参数传递,它似乎实现了(假脱机)CTE,然后在事后对其进行过滤

带参数值的查询计划

我可能读错了计划。我没有注意到性能问题,但我担心 CTE 的实现可能会导致较大数据集出现问题,尤其是在繁忙的系统中。此外,我通常将这种遍历本身复合:我遍历祖先并返回后代(以确保收集所有相关节点)。由于我的数据如何,每组“相关”节点都相当小,因此实现 CTE 没有意义。当 SQL Server 似乎意识到 CTE 时,它在“实际”计数中给了我一些相当大的数字。

有没有办法让查询的参数化版本表现得像文字版本?我想把 CTE 放在一个可重用的视图中。

用文字查询:

CREATE PROCEDURE #c AS BEGIN;
    WITH descendants AS (SELECT
         t.ParentId Id
        ,t.Id DescendantId
    FROM #tree t
    WHERE t.ParentId IS NOT NULL
    UNION ALL SELECT
         d.Id
        ,t.Id DescendantId
    FROM descendants d
    JOIN #tree t ON d.DescendantId = t.ParentId)
    SELECT d.*
    FROM descendants d
    WHERE d.Id = 24
    ORDER BY d.Id, d.DescendantId; …
Run Code Online (Sandbox Code Playgroud)

sql-server optimization cte execution-plan recursive

9
推荐指数
2
解决办法
1306
查看次数

使用 XQuery 过滤掉一些孩子,但保留父母

SET NOCOUNT ON;
DECLARE @xml AS Xml = '<a><b>bbb</b><c>ccc</c><d>ddd</d></a>';
SELECT @xml;
SELECT @xml.query('/a/*[self::b or self::c]');
SET @xml.modify('delete /a/d');
SELECT @xml;
Run Code Online (Sandbox Code Playgroud)

给出以下结果集

原来的:

<a><b>bbb</b><c>ccc</c><d>ddd</d></a>
Run Code Online (Sandbox Code Playgroud)

过滤以排除非 (b|c) - 但缺少父母:

<b>bbb</b><c>ccc</c>
Run Code Online (Sandbox Code Playgroud)

我想要什么(可用于删除步骤,但不可用于 .query):

<a><b>bbb</b><c>ccc</c></a>
Run Code Online (Sandbox Code Playgroud)

是否可以在 XQuery 中保留父项?

sql-server xquery

9
推荐指数
1
解决办法
514
查看次数

Xml 索引,行数慢

在以下情况下,我没有得到索引查找。而不是<Number>xxx</Number>像这篇文章那样插入到 xml 列中为什么当 where 子句过滤 `value()` 时不使用二级选择性索引?, 用这个 xml 插入 100k 行<SomeText>NiceText</SomeText>和类似数量的 this <SomeText>MoreText</SomeText>。不需要是100k。只需要很多。然后添加索引

create selective xml index SIX_T on dbo.T(XMLDoc) for
(
    pathXQUERY = '/SomeText' as xquery 'xs:string' maxlength(8) singleton
);
Run Code Online (Sandbox Code Playgroud)

和二级索引

create xml index SIX_T_pathXQUERY on dbo.T(XMLDoc)
  using xml index SIX_T for (pathXQUERY);
Run Code Online (Sandbox Code Playgroud)

然后数数

select count(*)
from dbo.T as T
where T.XMLDoc.exist('/SomeText[. eq "MoreText"]') = 1;
Run Code Online (Sandbox Code Playgroud)

请注意,它不使用索引查找并且“慢”。数百万行可能需要几秒钟。如果我在标准列中插入相同的值并向其添加索引并执行

select count(id)
    from dbo.T as T where SomeTextColumn = 'MoreText'
Run Code Online (Sandbox Code Playgroud)

我立即得到结果。在 sql server 18.3.1 …

performance index xml sql-server execution-plan query-performance

9
推荐指数
2
解决办法
530
查看次数

sys.dm_os_ring_buffers 中的 CPU 使用率不正确

我正在运行带有 SQL Server 实例的云 VPS。因为它是供个人使用的,所以我使用的是 express 版(我不能使用开发者版,因为我在技术上运行了生产应用程序,而且我买不起 Standard+)。

我正在尝试使用Brent Ozar 的教程使用sp_BlitzFirst. 我遇到的问题是,无论当时的实际 CPU 使用情况如何,ProcessUtilizationinsys.dm_os_ring_buffers总是100以 形式出现。

虚拟机信息

@@version: Microsoft SQL Server 2017 (RTM-CU15) (KB4498951) - 14.0.3162.1 (X64) May 15 2019 19:14:30 版权所有 (C) 2017 Microsoft Corporation Express Edition(64 位)Linux (Ubuntu 18.04) .2 LTS)

主机: 1 & 1 Ionos VPS

lscpu 输出

Architecture:        x86_64
CPU op-mode(s):      32-bit, 64-bit
Byte Order:          Little Endian
CPU(s):              2
On-line CPU(s) list: 0,1
Thread(s) per …
Run Code Online (Sandbox Code Playgroud)

sql-server ubuntu dmv sp-blitz sql-server-2017

9
推荐指数
1
解决办法
611
查看次数

SQL Server 中的键范围 RangeI-N 锁兼容性

所述文档的状态,该RangeI-N锁(插入范围,空资源锁;插入新钥匙插入的索引之前用于测试范围)是用本身兼容(参见兼容性矩阵),因此,即使一个事务已获得的RangeI -N lock on特定键另一个事务也可以获得这样的锁。

在下面,它说

在事务中插入值时,在执行插入操作的事务期间不必锁定值落入的范围。锁定插入的键值直到事务结束就足以保持可序列化性。例如,给定这个 INSERT 语句:

INSERT mytable VALUES ('Dan');

RangeI-N 模式键范围锁放置在与名称 David 相对应的索引条目上以测试范围。如果锁定被授予,则插入 Dan 并对值 Dan 放置一个排它 (X) 锁。RangeI-N 模式键范围锁仅在测试范围时才需要,在执行插入操作的事务期间不会保持。其他事务可以在插入值 Dan 之前或之后插入或删除值。但是,任何尝试读取、插入或删除值 Dan 的事务都将被锁定,直到插入事务提交或回滚。

引用另一个来源- Microsoft SQL Server 2008 Internals: Transactions and Concurrency:

例如,当 SQL Server 尝试使用 Serializable 隔离将会话中的键之间的范围插入时,将获取 RangeIn-Null 锁。这种类型的锁并不常见,因为它通常非常短暂。直到找到正确的插入位置,它才会被持有,然后该锁被转换为 X 锁。

我的理解是这种类型的锁是在识别新插入的钥匙应该放置的范围的过程中(我假设这就是“测试范围”的意思)。发生这种情况后,锁被释放,新的钥匙被插入,并在其上放置一个 X 锁。

但是我不明白为什么说两个 RangeI-N 锁是相互兼容的。如果事务 A 和 B 都在同一个键上放置 RangeI-N 锁,因为他们都想在范围中插入一个新键并且事务 A 先执行插入,那么由 B 确定的键插入位置可能已经不正确,因为范围发生了变化(A 在那里插入了一个新值)。有人能解释一下吗?

sql-server transaction locking

9
推荐指数
1
解决办法
714
查看次数

有没有一种方法可以轻松验证 SQL 脚本是否与特定 SQL Server 版本兼容?

我目前正在做一个需要在不同SQL环境中运行的项目。我的大多数开发人员都有 SQL Server 2017。该应用程序在 SQL Azure 中运行,客户端最近开始运行一些使用 SQL Server 2016 的环境。该项目正在使用 SSDT 生成 Dacpac 文件,这对于开发来说工作正常,但是我们最近运行了开发人员使用 2017 年引入的系统函数的案例。引用此函数的存储过程脚本在我们的开发和 Azure 环境中运行良好,但在 2016 年环境中失败。

是否有一种简单的方法可以针对某个任意 SQL Server 版本验证脚本,而无需我们为希望支持的每个 SQL Server 版本创建多个测试环境?该方法不需要 100% 万无一失,但应该能捕捉到绝大多数潜在的兼容性问题。

sql-server ssdt compatibility-level

9
推荐指数
1
解决办法
637
查看次数

意外地以数字开头命名我的数据库中的所有表

我不小心将我的 sql 数据库中的所有表(100+)命名为前缀 2019_

直到现在才意识到这不是一个好主意(2年后才回到 SQL)

有什么方法可以将其批量交换为后缀甚至删除所有表?我什至无法打开它们。真的不想手动遍历它们。

如果这是一个愚蠢的问题,可能会在谷歌上搜索错误的术语

谢谢!

sql-server

9
推荐指数
1
解决办法
3571
查看次数

帮助性能调优主/详细信息(类似于收件箱的电子邮件)SQL 查询

过去几天我一直在搜索、观看视频,我想我已经尽可能地摸索了。给出我下面的例子,我正在寻找更具体的方向。

我有两张桌子正在处理。MessageThreads(400k 条记录)和 Messages(1M 条记录)。它们的模式如下所示。

消息表 在此处输入图片说明

消息线程索引

https://gist.github.com/timgabrhel/0a9ff88160ebc9e40559e1e10ecc7ee4

消息索引

https://gist.github.com/timgabrhel/d649074cbe82016e8a90f918c58c4764

我正在尝试提高我们的主要“收件箱”查询的性能。想想您的电子邮件提供商的收件箱。您将看到一个线程列表,一些是新的,一些是已读的,按日期排序,还可以预览最近发送的消息,无论是发送给您还是来自您。最后,这个查询有一个分页元素。默认情况下,我们需要 11 个项目。10 用于显示页面,+1 以了解下一页是否还有更多内容。

对于我们的一些长期用户,他们最多可以拥有 40K 条消息。

在过去的几天里,这个查询出现了许多不同的形式,但这就是我所了解的。我已经OUTER APPLY试过了,但我看到执行时间和统计数据更糟。

SET STATISTICS IO ON; /* And turn on the Actual Excecution Plan */

declare @UserId bigint
set @UserId = 9999

; WITH cte AS (
    SELECT
        ROW_NUMBER() OVER (ORDER BY SendDate DESC) AS RowNum, 
        MT.MessageThreadId, 
        MT.FromUserHasArchived, 
        MT.ToUserHasArchived, 
        MT.Created, 
        MT.ThreadStartedBy, 
        MT.ThreadSentTo, 
        MT.[Subject], 
        MT.CanReply, 
        MT.FromUserDeleted, 
        MT.ToUserDeleted,              
        LM.MessageId, 
        LM.Deleted, 
        LM.FromUserId, 
        LM.ToUserId, 
        LM.[Message], 
        LM.SendDate, 
        LM.ReadDate
    FROM MessageThreads MT 
    -- join the most recent …
Run Code Online (Sandbox Code Playgroud)

performance sql-server ssms query-performance

9
推荐指数
1
解决办法
463
查看次数

SQL Server - 有没有办法在使用索引视图时防止共享范围锁定为“无穷大”?

我有你的基本标题/细节表(想想订单和订单细节)。标题表有一个标识列作为聚集键,详细信息有一个标题 id 和一个行号列作为聚集键。header id 是一个不断增加的标识值,行号也是一个递增的值。

我试图在细节上添加一个索引视图来聚合数据,所以我们不必在代码中或通过触发器来做这件事,在现有系统中有它自己的一组并发问题。

在我们开始对其进行负载测试之前,一切看起来和工作正常。预计表中将添加约 1500 条详细信息/秒(90,000 条/分钟)。

当一行插入到明细表时,索引视图也会更新。在插入过程中,索引视图上出现共享范围锁 (RangeS-U)。取的范围是下一个键的当前键,类似于在可序列化隔离级别下获取锁的方式。连接是在已提交读下设置的。当表中不存在“下一个”键时,似乎会出现瓶颈。在这种情况下,共享锁被用于 'infinity(ffffffff)' 键。

这基本上描述了我看到的行为,但没有提供任何解决方法。 https://www.brentozar.com/archive/2018/09/locks-taken-during-indexed-view-modifications/

在上述负载下,服务器无法跟上插入的速度,并且事情开始备份得非常快。600 个并发连接中有 500 个在给定时间被阻塞。对不断增加的键的聚合索引视图似乎无法满足我们的并发要求。

我们使用的是 SQL Server 2012 标准版,很快就会升级到 2019。

有什么方法可以改变索引视图上的这种锁定行为,或者这对我来说是徒劳的,在这种情况下,我需要走基于代码/触发器的聚合之路,还是我错过了什么?如果 2019 没有表现出对我有用的行为,因为数据库将在工作完成之前升级。

包含的脚本代表所涉及的表,但显然不是实际的表。使用它们可以重现该行为。

设置

if object_id(N'dbo.LockTest') is null
begin
  create table dbo.LockTest
  ( LockTestID    int not null primary key
  , LockTestValue int     null
  );
  insert into dbo.locktest values(1, 1), (2, 2), (3, 3), (7, 7), (8, 8);
end;

if object_id(N'dbo.LockTestDetails') is null
begin
  create table dbo.LockTestDetails
  ( LockTestID  int not null
  , LineNumber …
Run Code Online (Sandbox Code Playgroud)

sql-server materialized-view locking

9
推荐指数
1
解决办法
561
查看次数

STRING_AGG 可以多次使用不同的分隔符吗?

我正在做一些字符串聚合,并注意到对我来说看起来像一个错误。STRING_AGG在具有不同分隔符参数的同一查询中使用了两次。但是,两者都产生了相同的结果(在两种情况下都使用“第一个”分隔符)。它似乎只在某些情况下发生。这是错误还是记录在案的行为?

首先,设置:

CREATE TABLE #Data
  ([Group] INT
 , Member  CHAR(1));

INSERT INTO #Data
VALUES (1, 'a'), (1, 'b')
     , (2, 'c'), (2, 'd');
Run Code Online (Sandbox Code Playgroud)

从表与VALUES构造函数。没有WITHIN GROUP ORDER BY,一切都很好。有了它,bug就出现了

SELECT Commas = STRING_AGG(Member, ', ') --WITHIN GROUP(ORDER BY Member)
     , Colons = STRING_AGG(Member, '::') --WITHIN GROUP(ORDER BY Member)
FROM #Data;

SELECT Commas = STRING_AGG(Member, ', ') --WITHIN GROUP(ORDER BY Member)
     , Colons = STRING_AGG(Member, '::') --WITHIN GROUP(ORDER BY Member)
FROM (VALUES (1, 'a'), (1, …
Run Code Online (Sandbox Code Playgroud)

sql-server sql-server-2017

9
推荐指数
1
解决办法
1399
查看次数