小编Joe*_*ish的帖子

如何强制标量 UDF 在查询中只计算一次?

我有一个查询需要根据标量 UDF 的结果进行过滤。查询必须作为单个语句发送(因此我不能将 UDF 结果分配给局部变量)并且我不能使用 TVF。我知道标量 UDF 引起的性能问题,包括强制整个计划串行运行、过多的内存授予、基数估计问题和缺乏内联。对于这个问题,请假设我需要使用标量 UDF。

UDF 本身调用起来非常昂贵,但理论上查询可以由优化器在逻辑上以这样一种方式实现,即函数只需要计算一次。我为这个问题模拟了一个大大简化的例子。以下查询在我的机器上执行需要 6152 毫秒:

SELECT x1.ID
FROM dbo.X_100_INTEGERS x1
WHERE x1.ID >= dbo.EXPENSIVE_UDF();
Run Code Online (Sandbox Code Playgroud)

查询计划中的过滤器运算符表明该函数为每一行计算一次:

查询计划 1

DDL 和数据准备:

CREATE OR ALTER FUNCTION dbo.EXPENSIVE_UDF () RETURNS INT
AS
BEGIN
    DECLARE @tbl TABLE (VAL VARCHAR(5));

    -- make the function expensive to call
    INSERT INTO @tbl
    SELECT [VALUE]
    FROM STRING_SPLIT(REPLICATE(CAST('Z ' AS VARCHAR(MAX)), 20000), ' ');

    RETURN 1;
END;

GO

DROP TABLE IF EXISTS dbo.X_100_INTEGERS;

CREATE TABLE dbo.X_100_INTEGERS (ID INT NOT NULL);

-- insert 100 …
Run Code Online (Sandbox Code Playgroud)

performance sql-server functions query-performance

12
推荐指数
1
解决办法
1184
查看次数

如何防止 UNPIVOT 变成 UNION ALL?

我有一个有点复杂的 Oracle 查询,需要大约半小时才能完成。如果我采用查询的慢部分并单独运行它,它会在几秒钟内完成。这是隔离查询的 SQL Monitor 报告的屏幕截图:

好的计划

以下是作为完整查询的一部分运行时的相同逻辑:

糟糕的计划

颜色对应于两个屏幕截图中的相同表格。对于慢查询,OracleMERGE JOINJOIN. 结果,大约有 1.5 亿个中间行被不必要地处理。

我可以通过查询提示或重写来解决这个问题,但我想尽可能多地了解根本原因,以便将来避免这个问题,并可能向 Oracle 提交错误报告。每次我得到糟糕的计划UNPIVOT时,查询文本中的内容都会转换为UNION ALL计划中的一个。为了进一步调查,我想防止该查询转换发生。我一直找不到这个转换的名字。我也找不到可以阻止它的查询提示或下划线参数。我正在开发服务器上进行测试,所以一切顺利。

有什么我可以做,以防止的查询转换UNPIVOTUNION ALL?我在 Oracle 12.1.0.2 上。

由于 IP 原因,我无法共享查询、表名或数据。我无法想出一个简单的复制品。话虽如此,我不清楚为什么需要这些信息来回答这个问题。下面是 UNPIVOT 查询的示例以及作为 UNION ALL 实现的相同查询。

oracle union oracle-12c unpivot

12
推荐指数
1
解决办法
957
查看次数

为什么这个流聚合是必要的?

看看这个查询。它非常简单(有关表和索引定义以及重现脚本,请参见文章末尾):

SELECT MAX(Revision)
FROM dbo.TheOneders
WHERE Id = 1 AND 1 = (SELECT 1);
Run Code Online (Sandbox Code Playgroud)

注意:“AND 1 = (SELECT 1) 只是为了防止此查询被自动参数化,我觉得这使问题变得混乱 - 尽管有或没有该子句,它实际上获得了相同的计划

这是计划(粘贴计划链接)

使用流 agg 进行计划

由于那里有一个“top 1”,我很惊讶地看到流聚合运算符。对我来说似乎没有必要,因为保证只有一行。

为了测试这个理论,我尝试了这个逻辑上等效的查询:

SELECT MAX(Revision)
FROM dbo.TheOneders
WHERE Id = 1
GROUP BY Id;
Run Code Online (Sandbox Code Playgroud)

这是那个计划(粘贴计划链接):

没有流 agg 的计划

果然,group by 计划能够在没有流聚合操作符的情况下通过。

请注意,两个查询都从索引的末尾“向后”读取并执行“前 1”以获得最大修订。

我在这里缺少什么? 流聚合是否在第一个查询中真正起作用,还是应该能够消除它(这只是优化器的一个限制,它不是)?

顺便说一下,我意识到这不是一个非常实际的问题(两个查询都报告 0 毫秒的 CPU 和经过时间),我只是对这里展示的内部/行为感到好奇。


这是我在运行上述两个查询之前运行的设置代码:

DROP TABLE IF EXISTS dbo.TheOneders;
GO

CREATE TABLE dbo.TheOneders
(
    Id INT NOT NULL,
    Revision SMALLINT NOT NULL,
    Something NVARCHAR(23),

    CONSTRAINT PK_TheOneders PRIMARY KEY NONCLUSTERED …
Run Code Online (Sandbox Code Playgroud)

sql-server aggregate database-internals group-by sql-server-2017

12
推荐指数
1
解决办法
691
查看次数

为什么并行前 N 排序明显比串行前 N 排序的 CPU 效率高得多?

我正在针对 SQL Server 2019 CU14 进行测试。我有一个纯行模式查询,它从复杂的视图中选择前 50 行。完整查询在 MAXDOP 1 时需要 25426 毫秒的 CPU 时间,在 MAXDOP 2 时需要 19068 毫秒的 CPU 时间。并行查询总体上使用较少的 CPU 时间并不令我感到惊讶。并行查询适用于位图运算符,并且查询计划在一些方面有所不同。然而,令我惊讶的是前 N 个排序的操作员时间的巨大差异。

在串行计划中,根据运算符执行统计数据,前 N 个排序占用了大约 10 秒的 CPU 时间:

在此输入图像描述

MAXDOP 2 计划报告相同的前 N ​​排序大约需要 1.6 秒的 CPU 时间:

在此输入图像描述

我不明白为什么两个不同的查询计划之间会报告如此大的差异。父运算符中的计算标量非常简单,无法解释运算符时间的差异。它们是这样的:

[Expr1055] = Scalar Operator(CASE WHEN COLUMN_1 IS NULL THEN (0) ELSE datediff(day,COLUMN_1,getdate()) END),
[Expr1074] = Scalar Operator(CASE WHEN [Expr1074] IS NULL THEN (0) ELSE [Expr1074] END)
Run Code Online (Sandbox Code Playgroud)

计划的不同部分还有其他计算标量。我上传了匿名的实际计划如果有人想查看的话,

当我将不带 TOP 的完整查询结果加载到临时表中并对临时表执行 TOP 50 排序时,并行计划和串行计划都需要大约 1200 毫秒的 …

sql-server parallelism sql-server-2019 query-performance

12
推荐指数
1
解决办法
911
查看次数

文档中 sys.dm_exec_query_stats 警告的实际影响是什么?

文档sys.dm_exec_query_stats状态如下:

如果服务器上当前正在执行工作负载,则对 sys.dm_exec_query_stats 的初始查询可能会产生不准确的结果。可以通过重新运行查询来确定更准确的结果。

我有时会在活动工作负载期间查询该 DMV,并希望获得准确的结果。我不知道如何在实践中应用上述警告。我是否应该始终查询 DMV 两次并使用第二个结果集,因为这样会更准确?这感觉有点牵强。我是否需要了解 DMV 可能不准确的方式,以便我可以将其纳入分析?如果是这样,会出现什么类型的错误:丢失的行、过时的值、不一致的行或其他什么?

sys.dm_exec_query_stats在活动工作负载期间使用时的最佳做法是什么?

sql-server dmv

11
推荐指数
2
解决办法
140
查看次数

我可能会遇到未被发现的死锁吗?

sp_whoisactive第一次在服务器上运行时,我遇到了一些意想不到的事情:

在此输入图像描述

两场比赛已经持续了13天,但两场比赛似乎都在互相阻碍。看看sys.dm_tran_locks

在此输入图像描述

阻止进程阈值设置的配置值为 10 秒。其他死锁正在通过死锁监视器在服务器上成功解决。

来自参数的信息@get_locks

<Database name="DB1">
  <Locks>
    <Lock request_mode="S" request_status="GRANT" request_count="1" />
  </Locks>
  <Objects>
    <Object name="TBL1" schema_name="dbo">
      <Locks>
        <Lock resource_type="OBJECT" request_mode="IX" request_status="GRANT" request_count="1" />
        <Lock resource_type="PAGE" page_type="*" index_name="PK__TBL1__3214EC27326C5B6A" request_mode="U" request_status="GRANT" request_count="1" />
        <Lock resource_type="PAGE" page_type="*" index_name="PK__TBL1__3214EC27326C5B6A" request_mode="U" request_status="WAIT" request_count="1" />
      </Locks>
    </Object>
  </Objects>
</Database>


<Database name="DB1">
    <Locks>
        <Lock request_mode="S" request_status="GRANT" request_count="1" />
    </Locks>
    <Objects>
        <Object name="TBL2" schema_name="dbo">
            <Locks>
                <Lock resource_type="OBJECT" request_mode="Sch-S" request_status="GRANT" request_count="2" />
            </Locks>
        </Object>
        <Object name="TBL1" schema_name="dbo">
            <Locks>
                <Lock resource_type="OBJECT" request_mode="IX" …
Run Code Online (Sandbox Code Playgroud)

sql-server deadlock sql-server-2017

10
推荐指数
1
解决办法
237
查看次数

AT TIME ZONE 能否返回 2004 年之前的数据的不准确结果?

SQL Server 2016添加了AT TIME ZONE运算符。从文档中:

\n
\n

AT TIME ZONE 实现依赖于 Windows 机制来跨时区转换日期时间值。

\n
\n

AT TIME ZONEmscorlib.ni!TimeZoneInfo.ConvertTime根据针对简单查询的 ETW 跟踪调用该方法。乔纳森·凯哈亚斯 (Jonathan Kehayias) 发表了一篇博文,其中他从课堂上提取了所有时区规则System.TimeZoneInfo。我只能在输出中找到于 01/01/2004 或更高版本生效的规则:

\n

兹规则

\n

Rob Farley 在一篇博客文章中提到,2000 年时区规则的更改似乎并未得到遵守AT TIME ZONE

\n
\n

它通过使用 Windows 注册表来工作,其中包含所有这些信息,但遗憾的是,当回顾过去时,它\xe2\x80\x99 并不完美。澳大利亚于 2008 年更改了日期,美国于 2005 年更改了日期 \xe2\x80\x93 两国都在一年中的大部分时间实行夏令时。AT TIME ZONE 明白这一点。但它似乎并没有意识到,2000年的澳大利亚,由于悉尼奥运会,澳大利亚提前了大约两个月开始实行夏令时。

\n
\n

我觉得有大量间接证据表明,AT TIME ZONE对于早于 2004 年的日期,操作员可能会返回不准确的结果。但是,我找不到任何AT TIME ZONE使用该类的文档System.TimeZoneInfo,这些文档AT TIME ZONE对于较旧的日期可能不准确,或者 …

sql-server timezone

10
推荐指数
1
解决办法
1078
查看次数

取消旋转单行时如何摆脱无用的并行分支?

考虑以下查询,该查询对少量标量聚合进行了反透视:

SELECT A, B
FROM (
    SELECT 
      MAX(CASE WHEN ID = 1 THEN 1 ELSE 0 END) VAL1
    , MAX(CASE WHEN ID = 2 THEN 1 ELSE 0 END) VAL2
    , MAX(CASE WHEN ID = 3 THEN 1 ELSE 0 END) VAL3
    , MAX(CASE WHEN ID = 4 THEN 1 ELSE 0 END) VAL4
    , MAX(CASE WHEN ID = 5 THEN 1 ELSE 0 END) VAL5
    , MAX(CASE WHEN ID = 6 THEN 1 ELSE 0 END) VAL6
    , MAX(CASE …
Run Code Online (Sandbox Code Playgroud)

sql-server parallelism execution-plan sql-server-2017

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

为什么此 TVF 使用 GETDATE() 作为输入参数时会抛出错误 9820?

我正在 SQL Server 2019 CU14 上进行测试。考虑针对兼容级别为 130 或 140 的 SQL Server 数据库创建的以下表值函数:

-- requires database compat 130 or 140 to see the issue
CREATE OR ALTER FUNCTION [dbo].[TVF_BUG_REPRO_2] (@O DateTime, @Z varchar(50))
RETURNS TABLE
WITH SCHEMABINDING
AS
RETURN
SELECT
CAST(
       CASE
              WHEN SZ.Zone1 > '' THEN (@O at time zone SZ.Zone1) at time zone 'Pacific Standard Time' 
              WHEN LEN(@Z) > 3 THEN (@O at time zone @Z) at time zone 'Pacific Standard Time'
              ELSE @O
       END AS DATETIME
) …
Run Code Online (Sandbox Code Playgroud)

sql-server table-valued-parameters sql-server-2019

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

BIT 列是否为 CCI 提供任何性能优势?

BIT列在聚集列存储索引中使用时是否提供任何性能优势?例如,我对在 CCI 中定义列BIT而不是 所获得的任何性能优势感兴趣BIGINT。我正在使用 SQL Server 2016。

我对 CCI 压缩的工作原理知之甚少,但根据我所阅读的内容和一些测试,似乎数据类型(仅限于存储整数的精确数字)在列存储压缩方面确实无关紧要. 例如,如果我将 10 个完整的行组插入到带有BIGINT列而不是BIT列的表中,我看不到压缩行组之间的大小差异。以下是一项测试的源数据:

DROP TABLE IF EXISTS dbo.CCI_BIT_TEST_SOURCE;

CREATE TABLE dbo.CCI_BIT_TEST_SOURCE (
    ID1 BIGINT NOT NULL,
    ID2 BIGINT NOT NULL,
    ID_BIT BIT NOT NULL,
    ID_BIGINT BIGINT NOT NULL,
    INDEX CCI__CCI_BIT_TEST_SOURCE CLUSTERED COLUMNSTORE
);

INSERT INTO dbo.CCI_BIT_TEST_SOURCE WITH (TABLOCK)
SELECT
  t.RN
, t.RN
, t.RN % 2
, t.RN % 2
FROM
(
    SELECT TOP (10485760) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) …
Run Code Online (Sandbox Code Playgroud)

sql-server columnstore sql-server-2016

7
推荐指数
1
解决办法
853
查看次数