【问题标题】:How can sysname = null in this SQL Server system stored procedure if sysname cannot be null?如果 sysname 不能为 null,如何在此 SQL Server 系统存储过程中 sysname = null?
【发布时间】:2019-02-16 11:59:17
【问题描述】:

我读到了sysname cannot be null,但是在系统过程sp_grantdbaccess 的定义中,我看到将null 分配给了过程的一个参数(如果我没看错的话)。这怎么可能?

ALTER PROCEDURE [sys].[sp_grantdbaccess]
    @loginame   sysname,
    @name_in_db sysname = NULL OUT

【问题讨论】:

  • 如果您阅读了您提供的链接中的完整答案,您将看到SYSNAME NOT NULL 是默认值,但它也可以被覆盖,这实际上就是这里发生的事情。
  • 在某些情况下会忽略类型的默认可空性,存储过程参数就是其中之一。变量是另一个:DECLARE @n SYSNAME 可以正常工作,并且它总是会生成一个初始值为NULL 的变量。对于存储过程参数,重要的是参数是否被提供,而不是它的值——NULL 总是被允许的。 sp_grantdbaccess @loginame=NULL 产生错误只是因为sp_grantdbaccess 明确检查(通过sp_validname)——引擎不会阻止分配NULL
  • @RBarryYoung - 接受的答案引用官方文档的话说“sysname 是系统提供的用户定义数据类型,在功能上等同于 nvarchar(128),但它不可为空”与后面的评论冲突,即 null 只是默认值,这就是我问这个问题的原因 - 我认为官方文档优先于答案评论。
  • 谢谢@JeroenMostert - 从你的解释中我推断出引用的文档可能意味着数据类型 sysname 的列可能不会以空值持久化 - 而不是意味着该类型的变量可能不会在过程等范围内为空。
  • 这也不对。我最好写一个答案...

标签: sql-server stored-procedures sqldatatypes


【解决方案1】:

SYSNAME 不能是 NULL”是不正确的。链接问题的答案是正确的,当它说它相当于 NVARCHAR(128) NOT NULL 作为 default - 然后在列定义中有效地only。比较:

-- When not specified, columns are NULL
SET ANSI_NULL_DFLT_ON ON  

-- Works
CREATE TABLE T(N NVARCHAR(128)); INSERT T DEFAULT VALUES; SELECT * FROM T
GO
DROP TABLE T
GO

-- When not specified, columns are NOT NULL
SET ANSI_NULL_DFLT_ON OFF

-- Error: can't insert NULL
CREATE TABLE T(N NVARCHAR(128)); INSERT T DEFAULT VALUES; SELECT * FROM T
GO
DROP TABLE T
GO

现在用SYSNAME试试同样的方法:

-- When not specified, columns are NULL
SET ANSI_NULL_DFLT_ON ON  

-- Error: SYSNAME is NOT NULL, regardless of defaults
CREATE TABLE T(N SYSNAME); INSERT T DEFAULT VALUES; SELECT * FROM T
GO
DROP TABLE T
GO

但这并不意味着SYSNAME不能是NULL,我们所要做的就是说它可能是:

-- Works
CREATE TABLE T(N SYSNAME NULL); INSERT T DEFAULT VALUES; SELECT * FROM T
GO
DROP TABLE T
GO

在几乎所有使用类型的其他上下文中(变量、存储过程参数),我们不能指定 NULLNOT NULLNULL 值总是被允许的,所以这个 NOT NULL 元数据很少相关.上面的代码使用常规表并不是偶然的:如果你用一个表变量尝试同样的事情,你会发现ANSI_NULL_DFLT_ON被忽略并且NULL总是列的默认值,如果没有指定,所以只有相关的案例是:

-- Can't insert NULL
DECLARE @T TABLE (N SYSNAME); INSERT @T DEFAULT VALUES; SELECT * FROM T@
GO

-- OK
DECLARE @T TABLE (N SYSNAME NULL); INSERT @T DEFAULT VALUES; SELECT * FROM @T
GO

【讨论】:

  • 谢谢 - 答案教会我思考列定义与变量的不同上下文,并且 MS 的评论并不严格正确,即“sysname 是系统提供的用户定义数据在功能上等同于 nvarchar(128) 的类型,但它不能为空”;它可能应该附加“默认”。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-08-08
  • 1970-01-01
  • 2010-09-24
  • 1970-01-01
  • 1970-01-01
  • 2014-03-20
  • 2020-10-12
相关资源
最近更新 更多