回答问题"Split one column into multiple rows"的答案,我在这里重写为[1]。
Type = 'P'
的含义是什么?为什么要使用未记录的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
相关问题:
答案 0 :(得分:47)
为什么要使用未记录的master..spt-values
Sybase,因此它的私生子MS SQL为产品提供了各种特性和功能,这些特性和功能在系统过程中实现(而不是像sqlserver这样的二进制文件,它们是作为服务启动的)。这些系统过程过程是用SQL代码编写的,并命名为sp_%.
。除了一些秘密内部,它们与其他任何SQL代码具有相同的限制和需求。它们是Sybase ASE或SQL Server产品的一部分。因此,他们不是必需来记录它;并且内部位不能合理地标记为"未记录的"。
master..spt_values
包含所述系统过程在SQL表中需要的各种各样的部分,以生成各种报告。 sp
表示系统程序; spt
表示系统程序表;当然values
就是内容。
是什么(含义)Type =' P'
人们经常将spt_values
描述为"反规范化",但这是不正确的术语。正确的术语是折叠或打包。它是26个左右的逻辑查找表,每个表都经过精美的规范化,折叠成一个物理表,并带有Type
列来区分逻辑表。
现在在一个普通的数据库中,这将是一个严重的错误(只需查看&#34的答案;一个查找表或许多")。但是在服务器目录中,它是可取的,它取代了26个物理表。
" L"代表LockType Lookup; " V"代表DeviceType查找(V是整个服务器中Device的缩写);等类型" P2"包含按位序数,用于扩展打包到INT中的位。
为了执行Project,需要在SQL表格中提供已知边界内的一组连续数字,这是许多系统程序必须执行的。输入" P"是0到2047之间的连续数字列表。
这里使用的术语 Projection 是技术上的精确含义,自然的逻辑意义,而不是关系代数意义,这是不自然的。
因此spt_values,
只有一个目的是包含26个折叠的,另外分开的参考表和一个投影表。
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
EG。 sp_lock
生成活动锁的报告,将锁类型显示为字符串名称。但是master..syslocks
包含锁定类型为数字,它不包含那些名称;如果确实如此,那将是一张严重失真的桌子!如果执行查询(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 LockTypes的列表,而不是上面的查询生成的活动锁,显示活动锁(或Null)的数量。您不需要游标或WHILE
循环。我们可以 Project LockType Lookup表,通过活动锁的数量:
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列中的值列表。代码仅针对逗号或字符串结尾的每个字节激活。
在哪里以SQL表格的形式获取一组连续数字,而不是从头开始创建一个并插入其中?为什么,master..spt_values
当然。如果你知道它在那里。
(您可以通过阅读系统存储过程的代码,了解一下ASE或SQL Server的内部结构。)
请注意,一列中的任何CSV字段都是粗略标准化错误,它会中断2NF(包含重复值)和1NF(非原子)。注意,没有打包或折叠,它是一个重复组,它是非规范化的。这种严重错误的许多负面后果之一是,不必使用简单的SQL将重复组导航为行,而是必须使用复杂的代码来确定和提取未规范化的CSV字段的内容。这里spt_values P
为复杂代码提供了一个向量,使其更容易。
它有什么好处?
我想我已经回答了这个问题。如果你没有它,那么需要一个Numbers列表的每个系统过程都必须创建一个临时表;并将行插入其中;在运行其代码之前。当然,不必执行这些步骤,使系统过程更快。
现在,当您需要执行投影时,例如。将来的日历日期,或者其他什么,您可以使用spt_values
,而不必每次都创建自己的临时表(或创建自己的私有永久表并维护它)。
答案 1 :(得分:2)
在TSQL中拆分字符串的许多常见解决方案都需要一个数字列表;在这种情况下,有人正在使用spt_values表来提供它们。通过检查,此查询返回2048个连续整数的列表:
select number from master..spt_values where type = 'P'
我假设原始查询编写器使用spt_values作为整数,因为a)它“保证”可用,因此查询将始终有效,并且b)它避免了关于获取整数的替代方法的冗长解释。
主要的缺点是该表没有文档,因此使用它可能会造成混淆并且风险也很大(至少原则上,升级或Service Pack可能会更改表数据或结构,甚至完全删除它)。 / p>
有许多替代方法可以在不使用未记录的表的情况下获取数字列表(我使用表值函数):
答案 2 :(得分:1)
我知道这是一篇较旧的帖子,但我想我会添加更新。 Tally Table和基于cteTally表的分离器都有一个主要问题。它们使用连接的分隔符,当元素变宽并且字符串变长时会破坏它们的速度。
我已经解决了这个问题,并写了一篇关于它的文章,可以在他的网址中找到。的 http://www.sqlservercentral.com/articles/Tally+Table/72993/ 强>
新方法为VARCHAR(8000)的所有While循环,递归CTE和XML方法打开了大门。
我还会告诉你,一位名叫“彼得”的人甚至对那段代码做了改进(在文章的讨论中)。这篇文章仍然很有趣,我将在接下来的一两天内用Peter的增强功能更新附件。在我的主要增强功能和彼得所做的微调之间,我不相信你会找到一个更快的T-SQL-Only解决方案来分割VARCHAR(8000)。我也解决了VARCHAR(MAX)这类分离器的问题,并且正在为此写一篇文章。
答案 3 :(得分:-2)
现在工作正常
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) = '')
答案 4 :(得分:-4)
在SQL Server 2016中,我们现在有了新功能String_Split,我们可以使用它来拆分列。
例如,这是脚本:
DECLARE @String NVARCHAR(1000) = 'abc,def,ghi,jkl,mno,pqr,stu,vw,xyz';
SELECT * FROM STRING_SPLIT(@String,',');
执行上述脚本后,它将返回以下结果。
嗯,那就是它。以下是Kathi的resource,她在那里比较了表现。