SQL Server中的SYSNAME数据类型是什么?

165

SQL Server的SYSNAME数据类型有什么用途?BOL说:

SYSNAME数据类型用于存储对象名称的表列、变量和存储过程参数。

但我不是很明白。你能提供一个使用案例吗?


3
除了下面这些优秀的回答外,它还用于在使用sys.types获取列元数据时给某人制造麻烦,因为它与nvarchar共享相同的system_type_id。 - StingyJack
8个回答

192

sysname是一种内置数据类型,仅限于128个Unicode字符,并且通常用于在创建脚本时存储对象名称。它的值不能为NULL

基本上与使用nvarchar(128) NOT NULL相同

编辑

正如评论中@Jim提到的那样,说实话我认为没有真正的商业案例需要使用sysname。它主要由Microsoft在构建SQL Server内部的sys表和存储过程等时使用。

例如,通过执行Exec sp_help 'sys.tables',你会发现列name被定义为sysname,这是因为此值实际上是一个对象本身(一个表)

我不会太担心它。

还值得注意的是,对于那些仍在使用SQL Server 6.5及以下版本(还有人在使用吗?),内置类型sysname等同于varchar(30)

文档

sysnamencharnvarchar的文档中定义,在备注部分:

sysname是一种系统提供的用户定义数据类型,与nvarchar(128)在功能上相同,不同之处在于它不可为空。 sysname用于引用数据库对象名称。

为了澄清上述备注,sysname默认情况下被定义为NOT NULL,当然可以将其定义为可空。此外,确切的定义可能因SQL Server实例而异。

使用特殊数据类型

sysname数据类型用于存储对象名称的表列、变量和存储过程参数。其确切定义与标识符的规则有关,因此在 SQL Server 实例之间可能会有所不同。 sysnamenvarchar(128) 在功能上是相同的,但默认情况下 sysname 是 NOT NULL 的。在早期版本的 SQL Server 中,sysname 被定义为 varchar(30)。

有关sysname允许还是禁止NULL值的更多信息,请参见https://stackoverflow.com/a/52290792/300863

仅仅因为它是默认值(NOT NULL),也并不能保证它就是!


1
你能提供使用案例吗?我认为你不会找到一个实际的商业理由来使用它。大多数情况下,它在 MS SQL 内部使用,因为它可能在表格等方面被广泛使用。 - Jim
12
在您的脚本中,可以使用“sysname”来实现前向(和后向)兼容性。 - Martin Smith
只是在这里提出我的见解:将我带到这里的列在存储过程中声明为 nvarchar(max) 非空,但在系统表中显示为 sysname - gloomy.penguin
3
@Barry 实际上,根据 sys.types 中的信息,它是一个 nvarchar(256) not null。请注意,系统类型ID = 231(nvarchar)。现在它在 TDS 中作为类型别名使用;类型别名的第一个ID是256,对应于 sysname。至于用法:sysname 用于信息模式。 - atlaste
2
@atlaste 在sys.tables中的max_length长度是以字节为单位的。nvarchar每个字符使用两个字节,这就是为什么它被定义为nvarchar(128)的原因。 - David Rushton
显示剩余5条评论

72

你能提供使用案例吗?

如果您需要创建一些动态SQL,那么在变量中使用 sysname 作为数据类型来保存表名、列名和服务器名是合适的。


8

提醒一下...

select * from sys.types where system_type_id = 231 会返回两行。

(我不确定这是什么意思,但我百分之百确定它正在干扰我的代码)

编辑: 我想它的意思是在这种情况下(我的情况)你应该通过用户类型ID进行连接,或者可能是用户类型ID和系统类型ID两者都进行连接。

name        system_type_id   user_type_id   schema_id   principal_id    max_length  precision   scale   collation_name                  is_nullable     is_user_defined     is_assembly_type    default_object_id   rule_object_id
nvarchar    231              231            4           NULL            8000        0           0       SQL_Latin1_General_CP1_CI_AS    1               0                   0                   0                   0
sysname     231              256            4           NULL            256         0           0       SQL_Latin1_General_CP1_CI_AS    0               0                   0                   0                   0

create procedure dbo.yyy_test (
    @col_one    nvarchar(max),
    @col_two    nvarchar(max)  = 'default',
    @col_three  nvarchar(1),
    @col_four   nvarchar(1)    = 'default',
    @col_five   nvarchar(128),
    @col_six    nvarchar(128)  = 'default',
    @col_seven  sysname  
)
as begin 

    select 1
end 

这个查询:

select  parm.name AS Parameter,    
        parm.max_length, 
        parm.parameter_id 
         
from    sys.procedures sp

        join sys.parameters parm ON sp.object_id = parm.object_id 
        
where   sp.name = 'yyy_test'

order   by parm.parameter_id

产生:

parameter           max_length  parameter_id
@col_one            -1          1
@col_two            -1          2
@col_three           2          3
@col_four            2          4
@col_five            256        5
@col_six             256        6
@col_seven           256        7

还有这个:

select  parm.name as parameter,    
        parm.max_length, 
        parm.parameter_id,
        typ.name as data_type, 
        typ.system_type_id, 
        typ.user_type_id,
        typ.collation_name,
        typ.is_nullable 
from    sys.procedures sp

        join sys.parameters parm ON sp.object_id = parm.object_id
        
        join sys.types typ ON parm.system_type_id = typ.system_type_id
        
where   sp.name = 'yyy_test'

order   by parm.parameter_id

给你这个:
parameter   max_length  parameter_id    data_type   system_type_id  user_type_id    collation_name                  is_nullable
@col_one    -1          1               nvarchar    231             231             SQL_Latin1_General_CP1_CI_AS    1
@col_one    -1          1               sysname     231             256             SQL_Latin1_General_CP1_CI_AS    0
@col_two    -1          2               nvarchar    231             231             SQL_Latin1_General_CP1_CI_AS    1
@col_two    -1          2               sysname     231             256             SQL_Latin1_General_CP1_CI_AS    0
@col_three   2          3               nvarchar    231             231             SQL_Latin1_General_CP1_CI_AS    1
@col_three   2          3               sysname     231             256             SQL_Latin1_General_CP1_CI_AS    0
@col_four    2          4               nvarchar    231             231             SQL_Latin1_General_CP1_CI_AS    1
@col_four    2          4               sysname     231             256             SQL_Latin1_General_CP1_CI_AS    0
@col_five    256        5               nvarchar    231             231             SQL_Latin1_General_CP1_CI_AS    1
@col_five    256        5               sysname     231             256             SQL_Latin1_General_CP1_CI_AS    0
@col_six     256        6               nvarchar    231             231             SQL_Latin1_General_CP1_CI_AS    1
@col_six     256        6               sysname     231             256             SQL_Latin1_General_CP1_CI_AS    0
@col_seven   256        7               nvarchar    231             231             SQL_Latin1_General_CP1_CI_AS    1
@col_seven   256        7               sysname     231             256             SQL_Latin1_General_CP1_CI_AS    0

sys.types 包含了你所创建的用户定义类型。如果你使用 create type MyInt from int 命令,那么你将会得到两行 system_type_id = 56 的结果。另一个默认情况下重复的类型是 240,它是层次结构、几何和地理系统类型的系统类型。 - Mikael Eriksson
我完全忘记了。那么,查询带有sysname的东西的理想方法是什么?由于它是一个“别名”,所以我可以只需要执行 where typ.name <> 'sysname' 吗?或者这会产生我不知道的其他后果吗? - gloomy.penguin
3
在系统类型 ID 和用户类型 ID 上与 sys.types 表进行 JOIN。SQL Fiddle - Mikael Eriksson
哦,那就是我在一开始说的话。我以为你在说那个是错误的... - gloomy.penguin
对不起,我没那个意思。我只是想指出一些其他可能会破坏你查询的因素,不仅 sysname 会在未连接两列的情况下给你带来麻烦。 - Mikael Eriksson

4

让我列举一个用例。希望这可以帮助您。在这里,我正在尝试查找DB“学生”中表“Stud_dtls”的表所有者。正如Mikael所提到的那样,当需要创建一些动态SQL来保存表名、列名和服务器名的变量时,可以使用sysname。只是想提供一个简单的示例来补充他的观点。

USE Students

DECLARE @TABLE_NAME sysname

SELECT @TABLE_NAME = 'Stud_dtls'

SELECT TABLE_SCHEMA 
  FROM INFORMATION_SCHEMA.Tables
 WHERE TABLE_NAME = @TABLE_NAME

2

sysname 是由 sp_send_dbmail 使用的,它是一个存储过程,用于“向指定的收件人发送电子邮件消息”,并位于 msdb 数据库中。

根据 Microsoft 的说法,

[ @profile_name = ] 'profile_name'  

Is the name of the profile to send the message from. The profile_name is of type sysname, with a default of NULL. The profile_name must be the name of an existing Database Mail profile. When no profile_name is specified, sp_send_dbmail uses the default private profile for the current user. If the user does not have a default private profile, sp_send_dbmail uses the default public profile for the msdb database. If the user does not have a default private profile and there is no default public profile for the database, @profile_name must be specified.


1
另一个用例是在使用SQL Server 2016+的AT TIME ZONE功能时。下面的语句将返回转换为GMT的日期。
SELECT 
CONVERT(DATETIME, SWITCHOFFSET([ColumnA], DATEPART(TZOFFSET, [ColumnA] AT TIME ZONE 'GMT Standard Time')))

如果您想将时区作为变量传递,请使用以下代码:
SELECT 
CONVERT(DATETIME, SWITCHOFFSET([ColumnA], DATEPART(TZOFFSET, [ColumnA] AT TIME ZONE @TimeZone)))

那么这个变量需要是 sysname 类型的(将其声明为 varchar 将会导致错误)。


1

顺带一提,如果您希望以这种方式探索数据库,可以将表名传递给有用的系统SP(存储过程):

DECLARE @Table sysname; SET @Table = 'TableName';
EXEC sp_fkeys @Table;
EXEC sp_help @Table;

0
你能提供使用案例吗?
任何你想要为数据库维护脚本存储对象名称的地方。例如,一个脚本清除某些具有日期列的表中的旧行。它配置了一个表,给出要过滤的表名、列名以及保留多少天的历史记录。另一个脚本将某些表转储到 CSV 文件中,并且同样配置了一个列出要转储的表的表。这些配置表可以使用 sysname 类型来存储表和列名。

你根本不需要那个。只需使用 nvarchar(128) not null 列即可。名称只是名称,它不必是 sysname 才能使用。 - Panagiotis Kanavos
2
当然,实际上它甚至不必是那种类型,nvarchar(300)也可以,或者如果您在表名中不使用Unicode,甚至只需使用varchar(因为我几乎确定没有人这样做)。 sysname的优点部分在于它使意图更清晰:此列保存对象名称;部分在于即使您迁移到使用不同数据类型的MSSQL的不同版本(如以前发生过的情况),它仍将是正确的类型。 - Ed Avis
如果发生这种情况,你的列就会出问题。sysname是一个用户定义类型,其usert_type_id为256。没有ALTER TYPE,因此无法更改它。您必须创建一个新类型,并将使用旧类型的所有列更改为新类型。如果微软决定更改此内容,他们必须将现有系统表数据迁移到新类型。您可以期望他们为已知的系统表执行此操作,但任何用户表都必须由用户迁移。 - Panagiotis Kanavos
假设定义扩大,您可能不会遇到任何读取数据的问题,但是您将无法写入超过旧定义的值。如果变窄,您可能会遇到读取问题,但这只是假设定义会改变。如果您想处理它,请不要使用sysname UDT。 - Panagiotis Kanavos
现在你知道它会随着每个数据库的变化而改变,这意味着你不能信任它。使用一个定义存储的数据将无法与使用不同定义的数据一起使用。 - Panagiotis Kanavos
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接