为什么(以及如何)使用master..spt_values分隔列?

对问题“将一列分割成多行”的答案进行了质疑,我在这里重写为[1]。

Type = 'P'含义是什么(以及为什么使用未logging的master..spt_values来分割列)? 它有什么好处?


[1]

 CREATE TABLE dbo.Table1 ( Col1 CHAR(1), Col2 CHAR(1), Col3 CHAR(1), Col4 VARCHAR(50) ) GO INSERT INTO dbo.Table1 VALUES ('A','B','C','1,2,3') GO INSERT INTO dbo.Table1 VALUES ('D','E','F','6,7,8,9') GO SELECT T.col1, RIGHT(LEFT(T.col4,Number-1), CHARINDEX(',',REVERSE(LEFT(','+T.col4,Number-1)))) FROM master..spt_values, table1 T WHERE Type = 'P' AND Number BETWEEN 1 AND LEN(T.col4)+1 AND (SUBSTRING(T.col4,Number,1) = ',' -- OR SUBSTRING(T.col4,Number,1) = '') --this does not work correctly anyway 

相关问题:

  • 系统表格master..spt_values的目的是什么,它的值是什么意思?

目的

为什么使用无证的master..spt-values

Sybase及其混帐儿子MS SQL为产品提供了各种特性和function,这些特性和function是在系统过程(而不是像sqlserver这样的作为服务启动的二进制文件)中实现的。 这些系统程序过程是用SQL代码编写的,并命名为sp_%. 除了一些秘密的内部组件外,它们与其他SQL代码有相同的限制和需求。 它们是Sybase ASE或SQL Server产品的一部分。 因此,他们不需要logging它; 内部比特不能合理标记为“无证”。

master..spt_values包含所述系统过程在SQL表中所需要的各种各样的部分和片段,以产生各种报告。 sp表示系统过程; spt表示系统过程的表格; 当然values是内容。

查找表

什么是(意义)Type ='P'

人们经常将spt_values描述为“非标准化”,但这是不正确的术语。 正确的术语是折叠包装的 。 它是26个左右的逻辑查找表,每个都精美的标准化,折叠成一个物理表,用一个Type列来区分逻辑表。

现在在一个正常的数据库中,这将是一个严重的错误(只看“一个或多个查询表”的答案)。 但是在服务器目录中,它是可取的,它取代了26个物理表。

  • “L”代表LockType Lookup; “V”代表DeviceType Lookup(V代表整个服务器中的Device的简称); 等等。types“P2”包含按位顺序,用于扩展打包到INT中的位。

  • 为了执行投影,需要一系列已知范围内的连续数字,这些数字以SQL表格的forms提供,许多系统过程必须完成。 键入“P”是0到2047之间的连续数字的列表。

  • 术语“ 投影”在这里被用作技术上精确的意思,自然的逻辑意义,而不是关系代数的意思,这是不自然的。

因此,对于spt_values,只有一个目的spt_values,包含26个折叠的,否则分开的Reference表和一个Projection表。

扩张

那么普通的spt_values使用就像一个普通的Lookup或者Reference或者ENUM表一样。 首先,查找值:

  SELECT * -- list Genders FROM Gender 

它的使用方式与Person有一个需要扩展的GenderCode相同(非常扩展,这些奇怪的日子):

  SELECT P.*, -- list Person G.Name -- expand GenderCode to Name FROM Person P JOIN Gender G ON P.GenderCode = G.GenderCode 

例如。 sp_lock生成活动锁的报告,将锁types显示为string名称 。 但是, master..syslocks包含lockingtypes作为数字 ,它不包含这些名称 ; 如果是的话,这将是一个非常规范化的表! 如果执行查询(Sybase ASE代码,则必须进行转换):

  SELECT * -- list LockTypes FROM master..spt_values WHERE type = "L" 

您将在Lookup表中注意到66个LockType 号码名称 。 这允许sp_lock执行简单的代码,如上面的Person :: Gender:

  SELECT spid, -- list Active Locks DB_NAME(dbid), OBJECT_NAME(id, dbid), v.name, -- expand lock name page, row FROM master..syslocks L, master..spt_values LT WHERE L.type = LT.number -- AND type = "L" -- LockType Lookup table ORDER by 1, 2, 3, 4, 5, 6 -- such that perusal is easy 

投影

什么是Type ='P'的(意思)?

什么是投影,它是如何使用的?

比方说,例如,而不是上面查询产生的活动锁,你需要一个所有 66个LockType的列表,显示活动锁的数量(或Null)。 您不需要游标或WHILE循环。 我们可以通过活动锁的计数来投影LockType查找表:

  SELECT LT.name, -- list LockTypes [Count] = ( -- with count SELECT COUNT(*) FROM master..syslocks WHERE type = LT.number ) FROM master..spt_values LT WHERE type = "L" 

有几种方法,那只是一个。 另一种方法是使用派生表而不是子查询。 但是你仍然需要投影。

这通常是什么spt_values用于扩展或投影。 现在你知道它在那里,你也可以使用它。 它是安全的(在master数据库中),几乎用于所有的系统过程,这意味着系统过程不能没有它运行。

分裂一列?

啊,你不明白“一个CSV列分成多行”的代码。

  • 暂时忘记spt_values ,然后再次检查该代码。 它只需要一个连续的数字列表,这样就可以逐字节地逐步读取CSV列中的值列表。 该代码仅对每个逗号或string结尾的字节激活。

  • 在哪里得到一组SQL表格的连续数字,而不是从头开始创build一个并插入它? 为什么,当然是master..spt_values 。 如果你知道它在那里。

  • (您可以通过阅读系统存储过程的代码了解一些关于ASE或SQL Server的内部信息。)

  • 请注意,一列中的任何CSV字段都是严重规范化错误,它会打破2NF(包含重复值)和1NF(不是primefaces性)。 请注意,这是不包装或折叠,这是一个重复组,它是非标准化的。 这种严重错误的许多负面后果之一是,不必使用简单的SQL将重复组作为行进行导航,而必须使用复杂的代码来确定和提取未规范化的CSV字段的内容。 这里spt_values P为复杂的代码提供了一个向量,使得它更容易。

它有什么好处?

我想我已经回答了。 如果您没有它,每个需要数字列表的系统过程将不得不创build一个临时表; 并插入行中; 在运行它的代码之前。 当然,不必执行这些步骤,使系统程序更快。

现在,当你需要执行一个投影,例如。 日历date,或者其他什么,你可以使用spt_values ,而不必每次创build你自己的临时表(或者创build你自己的私有永久表并维护它)。

在TSQL中分割string的许多常见解决scheme都需要一个数字列表; 在这种情况下,有人正在使用spt_values表来提供它们。 通过检查,这个查询返回一个2048个连续整数的列表:

 select number from master..spt_values where type = 'P' 

我假设原来的查询编写器使用spt_values作为整数,因为a)它被“保证”是可用的,因此查询将始终有效,b)避免了有关获取整数的替代方法的冗长解释。

主要的缺点是该表没有logging,因此使用它可能会造成混淆,并且有点冒险(至less原则上,升级或服务包可能会更改表数据或结构,甚至完全删除)。

有很多替代方法可以在不使用未logging的表的情况下获取数字列表(我使用了一个表值函数):

SQL,辅助数字表

我知道这是一个较旧的post,但认为我会添加更新。 Tally表和cteTally表分割器都有一个主要问题。 他们使用连接的分隔符,并且当元素变宽,并且string变长时,会杀死它们的速度。

我已经解决了这个问题,并写了一篇关于它的文章,可以在他的URL后面find。 http://www.sqlservercentral.com/articles/Tally+Table/72993/

这个新的方法使VARCHAR(8000)的所有While循环,recursionCTE和XML方法成为可能。

我还会告诉你,一个名叫“彼得”的家伙甚至对这个代码做了一个改进(在这篇文章的讨论中)。 文章仍然很有趣,我将在第二天或第二天用Peter的增强function更新附件。 在我的主要增强和彼得的tweek之间,我不相信你会发现分裂VARCHAR(8000)的更快的T-SQL-Only解决scheme。 我也解决了VARCHAR(MAX)这种分离器的问题,并且正在为此写一篇文章。

现在它工作正常

 SELECT T.col1, RIGHT(LEFT(T.col4,Number-1),CHARINDEX(',',REVERSE(LEFT(','+T.col4,Number-1)))) FROM master..spt_values, table123 T WHERE Type = 'P' AND Number BETWEEN 1 AND LEN(T.col4)+1 AND (SUBSTRING(T.col4,Number,1) = ',' OR SUBSTRING(T.col4,Number,1) = '') 

现在在SQL Server 2016中,我们有了新的functionString_Split,我们可以使用它来分割列。

例如,这里是脚本:

 DECLARE @String NVARCHAR(1000) = 'abc,def,ghi,jkl,mno,pqr,stu,vw,xyz'; SELECT * FROM STRING_SPLIT(@String,','); 

执行上面的脚本后,会返回以下结果。

分裂的结果

那么,就是这样。 这是Kathi的资源 ,她在那里比较了performance。