SQL Prompt是一款实用的SQL语法提示工具。SQL Prompt根据数据库的对象名称、语法和代码片段自动进行检索,为用户提供合适的代码选择。自动脚本设置使代码简单易读–当开发者不大熟悉脚本时尤其有用。SQL Prompt安装即可使用,能大幅提高编码效率。此外,用户还可根据需要进行自定义,使之以预想的方式工作。

点击下载SQL Prompt正式版

标量UDF作为全局数据库常量的错误使用是一个主要的性能问题,每当SQL Prompt在任何生产代码中发现此错误时,都应进行调查。除非您需要在计算列或约束中使用这些全局常量,否则通常将值存储在内联表值函数中或使用视图会更安全,更方便。

开发人员倾向于期望能够在数据库中设置全局值,以提供诸如Pi的值之类的常量,或诸如税率,语言,文件URN或URL之类的变量。用户定义的标量函数返回单个值,因此似乎提供了理想的方法。这对于不经常执行的功能以及处理相对较小的数据集是很好的,但是在其他情况下,它可能会导致严重的查询性能问题。出现问题是因为SQL Server不相信未经模式验证的标量函数是精确的和确定性的,因此在执行它们时会选择最安全(尽管最慢)的选项。

调用具有BEGIN…END块的任何SQL Server函数都会产生开销,因为除非允许SQL Server通过使用架构绑定创建该函数来验证其输出,否则它将在过滤数据之前重新为每一行执行该函数。 ,即使您很明显每次都会返回相同的值。这是一个比较隐蔽的问题,尽管扩展事件会话将揭示实际发生的情况,但是它并没有真正显示其在执行计划中的全部意义。

简而言之,请勿在JOIN条件,WHERE搜索条件或SELECT列表中使用标量用户定义函数(UDF),除非该函数是架构绑定的。SQL Prompt实现了静态代码分析规则PE017,该规则专门用于帮助您检测和纠正此问题。除非您对模式绑定及其对数据库更改的后果有信心,否则最好使用将值传输到变量,或者使用诸如视图或内联表值函数之类的模块。

解决问题

如果SQL Prompt检测到您的代码出现PE017,该怎么办?

我们将设置所有可能的选项,运行一些性能测试,并提出一些建议。

符合模式的UDF
通过添加架构绑定以确保其经过系统验证,可以正确使用标量函数。清单1创建了相同的简单Wordcount函数的两个版本,首先不带模式绑定,然后带模式绑定,这两个版本都简单地返回一个常量。在每一种情况下,我们检查IsDeterministic,IsPrecise以及IsSystemVerified属性值对每个对象。

最后,它创建了第三个版本,该版本仅返回其参数值,仅用于检查这是否是SQL Server验证过程中的一个因素。
IF Object_Id(‘dbo.Wordcount’) IS NOT NULL DROP FUNCTION dbo.Wordcount
GO

CREATE FUNCTION dbo.Wordcount()
/**
Summary: >
A simple scalar multi-statement function
without schemabinding that returns a constant
Author: PhilFactor
Date: 01/02/2018
Returns: >
the integer value 5
**/
RETURNS INT
AS
BEGIN
RETURN 5
END
GO

/* we now test it to see whether SQL Server trusts it */
SELECT ObjectPropertyEx(
Object_Id(‘dbo.Wordcount’), N’IsDeterministic’) AS deterministic;

–Is a precise number returned? Whenever floating point operations are used in resolving
–expressions, the results are not precise, by the very nature of the way that the datatype is stored.
SELECT ObjectPropertyEx(Object_Id(‘dbo.Wordcount’), N’IsPrecise’) AS precise;

–Can SQL Server verify that the function is precise and deterministic?
SELECT ObjectPropertyEx(
Object_Id(‘dbo.Wordcount’), N’IsSystemVerified’) AS verified;
GO

IF Object_Id(‘dbo.WordcountSchemaBound’) IS NOT NULL DROP FUNCTION dbo.WordcountSchemaBound
GO

CREATE FUNCTION dbo.WordcountSchemaBound()
/**
Summary: >
A second version of a simple scalar multi-statement function
with schemabinding that returns a constant
Author: PhilFactor
Date: 01/02/2018
Returns: >
the integer value 5
**/
RETURNS INT
WITH SCHEMABINDING
AS
BEGIN
RETURN 5
END
GO

/* Repeat tests to see if SQL Server trusts dbo.WordcountSchemaBound*/
SELECT ObjectPropertyEx(
Object_Id(‘dbo.WordcountSchemaBound’), N’IsDeterministic’) AS deterministic;

SELECT ObjectPropertyEx(
Object_Id(‘dbo.WordcountSchemaBound’), N’IsPrecise’) AS precise;

SELECT ObjectPropertyEx(
Object_Id(‘dbo.WordcountSchemaBound’), N’IsSystemVerified’) AS verified;
GO

IF Object_Id(‘dbo.Wordcounter’) IS NOT NULL DROP FUNCTION dbo.Wordcounter
GO

CREATE FUNCTION dbo.Wordcounter
/**
Summary: >
A third version of a simple scalar multi-statement function
without schemabinding that merely returns its parameter
To test whether the absense of any parameter is a determining
Factor – whether adding a parameter here gets round the problem
Author: PhilFactor
Returns: >
the integer value passed to it
**/
(@howMany INT)
RETURNS INT
AS
BEGIN
RETURN @howMany
END
GO

/* Repeat tests to see if SQL Server trusts dbo.Wordcounter*/
SELECT ObjectPropertyEx(
Object_Id(‘dbo.Wordcounter’), N’IsDeterministic’) AS deterministic;

SELECT ObjectPropertyEx(Object_Id(‘dbo.Wordcounter’), N’IsPrecise’) AS precise;

SELECT ObjectPropertyEx(
Object_Id(‘dbo.Wordcounter’), N’IsSystemVerified’) AS verified;
GO
清单1
如果运行清单1,您将看到该函数的第二个版本WordCountSchemaBound,对这三个属性返回true。稍后我们将看到这对调用这些函数的所有查询的性能有多大影响。

尽管模式绑定具有许多优点,但是在这种情况下,这意味着您将被明确禁止将常量视为变量,这并不是一件坏事。如果您更改了“常量”函数(即您已在表中的约束或计算列中使用的函数),它将被证明很复杂。另外,如果尝试在数据库工作时更改常量,则使用正在执行的功能的计划将在功能上放置模式稳定性锁,这将防止您更改常量的值,因为它们需要更改模式。锁。

标量UDF的替代品

清单2展示了标量UDF的几种替代选择,它们可以在不需要或不想对其进行模式绑定的情况下保存数据库范围的值。首先是视图,然后是表值函数。
IF Object_Id(‘dbo.WordCountView’) IS NOT NULL DROP VIEW dbo.WordCountView
GO
CREATE VIEW dbo.WordCountView
AS
/**
Summary: >
A very simple view that returns a single row with one column
Author: PhilFactor
Date: 01/02/2018
Returns: >
a single row with a column called ‘wordcount’
/
SELECT 5 AS wordcount
GO
IF Object_Id(‘dbo.WordCountTVF’) IS NOT NULL DROP FUNCTION dbo.WordCountTVF
GO
CREATE FUNCTION dbo.WordCountTVF()
/

Summary: >
A table valued function that returns a single
row with a column called ‘wordcount’
Author: PhilFactor
Date: 01/02/2018
Returns: >
a single row with a column called ‘wordcount’
**/
RETURNS TABLE
AS
RETURN
(SELECT 5 AS wordcount)
GO
清单2
视图定义中引用的对象不能以使视图定义非法或强制SQL Server在视图上重新创建索引的方式进行更改。

尽管有额外的CHECK约束保护,但我没有使用表来保存常量的方法。表格的设计并非一成不变!作为破坏者,我会告诉您它们的表现和视图一样好。

性能测试

排列所有候选解决方案后,让我们看看它们的性能如何。我将测试每个选项能多快找出英语中常用的五个字母的单词。这些测试要求我们创建一个简单Commonwords表,其中包含所有常见单词的单列(主键)。要填充它,您需要下载commonwords 文件,然后运行清单3,并使用正确的文件路径。
DECLARE @AllCommonWords XML =
(SELECT * FROM OpenRowset(BULK ‘C:\MyPath\commonwords.XML’,
SINGLE_BLOB) AS x );

IF Object_Id(‘commonwords’, ‘U’) IS NOT NULL DROP TABLE commonwords;

CREATE TABLE commonwords (word VARCHAR(40) NOT NULL PRIMARY KEY);

INSERT INTO commonwords(word)
SELECT word = word.value(’@el’, ‘varchar(40)’)
FROM @AllCommonWords.nodes(’/commonwords/row’) AS CommonWords(word);
清单3
对于计时,我将使用我的文章如何使用SQL Prompt片段记录T-SQL执行时间中描述的简单测试工具。
– create a temporary table variable to hold timings
DECLARE @log TABLE
(
TheOrder INT IDENTITY(1, 1),
WhatHappened VARCHAR(200),
WhenItDid DATETIME2 DEFAULT GetDate()
)

----start of timing
INSERT INTO @log(WhatHappened) SELECT ‘Starting the test run’–place at the start

–first we see how fast it is with a literal number, as a benchmark
SELECT Count(*) FROM commonWords WHERE Len(word)=5
INSERT INTO @log(WhatHappened) SELECT ‘simple Query with literal number’–place at the start

–then see how long it takes if you transfer the ‘constant’ to a local variable
DECLARE @NumberOfLetters INT=dbo.wordcount()
SELECT Count(*) FROM commonWords WHERE Len(word)=@NumberOfLetters
INSERT INTO @log(WhatHappened) SELECT 'Same query but with value transferred from UDF to variable ’

–Now we see how long with the scalar UDF that just passes the value back
SELECT Count(*) FROM commonWords WHERE Len(word)=dbo.wordcounter(5)
iNSERT INTO @log(WhatHappened) SELECT 'Same but using a scalar function with parameter ’

– And now with the use of a scalar UDF function as a global constant
SELECT Count(*) FROM commonWords WHERE Len(word)=dbo.wordcount()
iNSERT INTO @log(WhatHappened) SELECT 'Same with a ‘‘constant’’ UD scalar function ’

– And now with the use of a schema-bound scalar UDF as a global constant
SELECT Count() FROM commonWords WHERE Len(word)=dbo.wordcountSchemaBound()
iNSERT INTO @log(WhatHappened) SELECT ‘Same with schema-bound ‘‘constant’’ UD scalar function’
–We’ll now use a view to do the same thing
SELECT Count(
) FROM commonWords
INNER JOIN dbo.WordCountView
ON Len(word)=wordcount
iNSERT INTO @log(WhatHappened) SELECT 'Using a view containing a constant with inner join ’

–We’ll now use a view with a cross join to do the same thing
SELECT Count(*) FROM commonWords
CROSS JOIN dbo.WordCountView
where Len(word)=wordcount
iNSERT INTO @log(WhatHappened) SELECT 'Using a ‘‘constant’’ view with a cross join ’

–and now with an inline table-valued function. Some functions are OK!
SELECT Count(*) FROM commonWords
INNER JOIN dbo.WordCountTVF()
ON Len(word)=wordcount
iNSERT INTO @log(WhatHappened) SELECT ‘Using an inline TVF to provide a constant’

–we see if a different syntax makes a difference
SELECT Count() FROM commonWords
cross JOIN dbo.WordCountTVF()
WHERE Len(word)=wordcount
iNSERT INTO @log(WhatHappened) SELECT ‘Using an inline TVF and cross join to provide a constant’
SELECT ending.whathappened AS test, DateDiff(ms, starting.whenItDid,ending.WhenItDid) [Time in ms] FROM @log starting
INNER JOIN @log ending ON ending.theorder=starting.TheOrder+1
–list out all the timings
GO
/
this is the end of the test section */
清单4
运行此命令时,我们验证查询中使用常量的所有形式均产生相同的结果。时代非常清楚地表明问题是什么,问题的严重程度

几乎不需要图表来强调PE017的恐怖-const UDF的不正确使用。SQL Server执行不受架构限制的未经验证的标量UDF的方式非常谨慎(每行询问“是否仍返回5? ”),速度慢了五十倍。

除了避免使用不受模式约束的标量UDF外,该测试还表明,平均而言,在其他任何向查询中获取恒定值的方法之间,性能没有真正的区别。在每种情况下,查询执行计划都是相同的。

现在,我们通过使用拆卸部分结束,将所有内容整理整齐放在我们的测试数据库中。
IF Object_Id(‘dbo.Wordcount’) IS NOT NULL
DROP function dbo.Wordcount
GO
IF Object_Id(‘dbo.WordcountSchemaBound’) IS NOT NULL
DROP function dbo.WordcountSchemaBound
GO
IF Object_Id(‘dbo.Wordcounter’) IS NOT NULL
DROP function dbo.Wordcounter
GO
IF Object_Id(‘dbo.WordCountView’) IS NOT NULL
DROP view dbo.WordCountView
GO
IF Object_Id(‘dbo.WordCountTVF’) IS NOT NULL
DROP function dbo.WordCountTVF
GO
清单5
推荐建议

如果您使用未经验证的标量函数,那么查询将非常缓慢,因为无论是否具有参数,您都将在每一行上执行该查询。

如果您面对大量使用标量UDF作为全局常量的继承代码,则可以使用架构绑定重做它们。但是,如果这些是全局变量,并且在实时系统中很少更改,那么我不会想到这种选择,因为如果不临时更改在约束或计算列中使用它的每个表,就无法更改模式绑定函数,要删除它们,请更改函数,然后替换约束和计算列。

视图或TVF的用途更加广泛,所以我更倾向于使用它们来保存“全局”值。如果更改了这些内容,则会记录该更改,因为它们需要DDL更改。唯一的问题是在约束或计算列中只能使用标量函数。如果使用表,那很好,但是请记住,更改常量不是DDL更改,因此您必须设置访问权限以拒绝任何人更改权限,例如税率!

SQL Prompt 使用教程>>>

SQL Prompt数据库教程:标量用户定义函数误用作常量相关推荐

  1. 数据库原理与应用(SQL Server)笔记 第十章 用户定义函数

    目录 前言 一.用户定义函数的定义 二.用户定义函数的分类 三.标量函数和内联表值函数 (一)标量函数的定义 (二)标量函数的调用 1.SELECT语句调用 2.EXEC语句调用 (三)内联表值函数的 ...

  2. SQL service基础(九)用户定义数据类型和用户定义函数的概念、创建及使用方法

    实验目标: 1.学习和掌握用户定义数据类型的概念.创建及使用方法. 2.学习和掌握用户定义函数的概念.创建及使用方法. 创建一个数据库,执行shiyan15.sql脚本 一.创建和使用用户定义的函数( ...

  3. db2 控制台执行创建函数语句_DB2中创建和使用SQL用户定义函数

    本文将为您详细介绍DB2数据库中创建用户自定义行数,用以扩展扩展内置的 DB2 函数的方法,供您参考,希望对您有所帮助. 可以创建用户定义函数来扩展内置的 DB2 函数.例如,创建计算复杂的算术表达式 ...

  4. 使用脚本编写 Vim 编辑器,第 2 部分: 用户定义函数

    用户定义函数 Haskell 或 Scheme 程序员会告诉您,函数对于任何严肃的编程语言来说都是最重要的特性.对于 C 或 Perl 程序员,他们也会告诉您完全相同的观点. 函数为严肃的程序员提供了 ...

  5. oracle 触发器 execute immediate,过程、触发器、用户定义函数和批处理中使用的 EXECUTE IMMEDIATE...

    过程.触发器.用户定义函数和批处理中使用的 EXECUTE IMMEDIATE EXECUTE IMMEDIATE 语句允许使用文字字符串(在引号中)和变量的组合来构建语句.例如,以下过程包含创建表的 ...

  6. SQL SERVER数据库中GUEST用户

    SQL SERVER数据库中GUEST用户 问题 谁邀请这些客人(guest)到我的数据库中?你可知道在你的SQL SERVER数据库中还存在guest用户?你可知道SQL SERVER 需要gues ...

  7. 转载--SQL还原数据库后孤立用户问题处理(SQL 数据库 拥有对象 无法删除)

    SQL还原数据库后孤立用户问题处理(SQL 数据库 拥有对象 无法删除) 所谓孤立帐户,就是某个数据库的帐户只有用户名而没有登录名,这样的用户在用户库的sysusers系统表中存在,而在master数 ...

  8. asp教程连接sql server数据库教程二种方法

    asp教程连接sql server数据库教程二种方法  ,sql server ole db连接方法:,本人链接sql2012已经成功,想可以帮助大家 本教程主要是讲了asp与mssql server ...

  9. 探索SQL Server数据库的DATABASEPROPERTYEX()函数

    In this article, we explore a SQL function to extract SQL database metadata using the DATABASEPROPER ...

最新文章

  1. Python 爬虫框架Scrapy安装汇总
  2. python中用lxml解析html
  3. Git常用命令速查表
  4. 扎心了,程序员2017到2019经历了什么?
  5. 珍珠(信息学奥赛一本通-T1384)
  6. [OpenJudge 3066]随机序列
  7. go 协程回调函数 传入参数_ECMAScript 6 入门教程—Generator 函数的异步应用
  8. 【Oracle】ORA-04031错误解决
  9. Javacript中(function(){})() 与 (function(){}()) 区别 {转}
  10. PHP函数库03:PHP生成曲线图函数
  11. 一些简单的二维数学的算法。
  12. Django学习-22-Form
  13. java下载execl优化_Java内存优化之POI Excel(二)
  14. C#网络编程----(三)委托和多线程
  15. 姜成转载:站群的操作方法
  16. 【备忘】李炎恢老师HTML5+CSS3教程与课件代码【共享完毕】下载
  17. win10系统点击控制台自定义快捷键失效问题解决
  18. c语言程序 5ms 延时,计算机单片机延时方法电脑c语言.docx
  19. 【C++课程设计——自助点餐系统】主页及登录界面
  20. html圆圈中间一个感叹号实现,一个圆圈加一个感叹号是什么标识?

热门文章

  1. 石油和天然气行业的大数据分析:新兴趋势Big Data analytics in oil and gas industry: An emerging trend
  2. 皮尔逊相关性系数和热力图
  3. k线符号图解大全_八种常见的K线符号
  4. ADM pro破解百度云限速 ADM pro设置方法 ES文件管理器
  5. 产品3d建模-3d可视化模型实现VR在线展示
  6. 手机WiFi和热点为何不能同时开
  7. AMD define函数
  8. openlayers6【五】地图图层数据来源 source 详解
  9. DZone每日必读-News:2022 年 Java 开发:预测和选定趋势
  10. memcached随笔练习