sql-server - 通过分隔符分割多个字段

标签 sql-server t-sql

我必须编写一个可以对我们的数据库执行部分更新的 SP,更改存储在 PU 表的记录中。值字段包含所有值,由固定分隔符分隔。表字段指的是一个方案表,其中包含每个表的列名称,其方式与列字段中的类似。

现在,对于我的 SP,我需要使用列/值对拆分临时表中的值字段和列字段,PU 表中的每条记录都会发生这种情况。

一个例子:

我们的 PU 表看起来像这样:

CREATE TABLE [dbo].[PU](
    [Table] [nvarchar](50) NOT NULL,
    [Values] [nvarchar](max) NOT NULL
)

The PU table

为此示例插入 SQL:

INSERT INTO [dbo].[PU]([Table],[Values]) VALUES ('Person','John Doe;26');
INSERT INTO [dbo].[PU]([Table],[Values]) VALUES ('Person','Jane Doe;22');
INSERT INTO [dbo].[PU]([Table],[Values]) VALUES ('Person','Mike Johnson;20');
INSERT INTO [dbo].[PU]([Table],[Values]) VALUES ('Person','Mary Jane;24');
INSERT INTO [dbo].[PU]([Table],[Values]) VALUES ('Course','Mathematics');
INSERT INTO [dbo].[PU]([Table],[Values]) VALUES ('Course','English');
INSERT INTO [dbo].[PU]([Table],[Values]) VALUES ('Course','Geography');
INSERT INTO [dbo].[PU]([Table],[Values]) VALUES ('Campus','Campus A;Schools Road 1;Educationville');
INSERT INTO [dbo].[PU]([Table],[Values]) VALUES ('Campus','Campus B;Schools Road 31;Educationville');
INSERT INTO [dbo].[PU]([Table],[Values]) VALUES ('Campus','Campus C;Schools Road 22;Educationville');

我们有一个与此类似的方案表:

CREATE TABLE [dbo].[Schemes](
    [Table] [nvarchar](50) NOT NULL,
    [Columns] [nvarchar](max) NOT NULL
)

The Schemes table

为此示例插入 SQL:

INSERT INTO [dbo].[Schemes]([Table],[Columns]) VALUES ('Person','[Name];[Age]');
INSERT INTO [dbo].[Schemes]([Table],[Columns]) VALUES ('Course','[Name]');
INSERT INTO [dbo].[Schemes]([Table],[Columns]) VALUES ('Campus','[Name];[Address];[City]');

因此,PU 表的第一条记录应生成一个临时表,如下所示:

John

第五个将有:

Mathematics

最后,第 8 个 PU 记录应导致:

Campus A

你明白了。 我尝试使用以下查询来创建临时表,但遗憾的是,当 PU 记录中存在多个值时,它会失败:

DECLARE @Fields TABLE
(
    [Column] INT,
    [Value] VARCHAR(MAX)
)

INSERT INTO @Fields
    SELECT TOP 1
        (SELECT Value FROM STRING_SPLIT([dbo].[Schemes].[Columns], ';')), 
        (SELECT Value FROM STRING_SPLIT([dbo].[PU].[Values], ';'))
    FROM [dbo].[PU] INNER JOIN [dbo].[Schemes] ON [dbo].[PU].[Table] = [dbo].[Schemes].[Table]

TOP 1 正确获取第一个 PU 记录,因为每个 PU 记录在处理后都会被删除。

错误是:

子查询返回超过 1 个值。当子查询跟在 =、!=、<、<=、>、>= 后面或子查询用作表达式时,不允许这样做。

对于人员记录,拆分确实一次返回 2 个值/列,我只想将这些值存储在 2 个记录中,而不是收到错误。

重写上述查询有什么帮助吗?

另请注意,这些数据只是一般的废话。能够拥有 2 个具有分隔值的字段,且数量始终相等(例如,PU 表中的“人员”字段中始终具有 2 个分隔值),并将它们分成多个列/标题行,这就是要点问题的内容。

更新:工作实现

根据 Sean Lange 的(已接受的)答案,我能够制定出以下实现方案来克服该问题:

由于我需要重用它,因此组合列/值功能由一个新函数执行,声明如下:

CREATE FUNCTION [dbo].[JoinDelimitedColumnValue]
        (@splitValues VARCHAR(8000), @splitColumns VARCHAR(8000),@pDelimiter CHAR(1))
RETURNS TABLE WITH SCHEMABINDING AS
 RETURN
  WITH MyValues AS
(
    SELECT ColumnPosition = x.ItemNumber,
        ColumnValue = x.Item
    FROM  dbo.DelimitedSplit8K(@splitValues, @pDelimiter) x
)

, ColumnData AS
(
    SELECT ColumnPosition = x.ItemNumber,
        ColumnName = x.Item
    FROM  dbo.DelimitedSplit8K(@splitColumns, @pDelimiter) x
)

SELECT cd.ColumnName,
    v.ColumnValue
FROM MyValues v
JOIN ColumnData cd ON cd.ColumnPosition = v.ColumnPosition
;

对于上述示例数据,我将使用以下 SQL 调用此函数:

DECLARE @FieldValues VARCHAR(8000), @FieldColumns VARCHAR(8000)
SELECT TOP 1 @FieldValues=[dbo].[PU].[Values], @FieldColumns=[dbo].[Schemes].[Columns] FROM [dbo].[PU] INNER JOIN [dbo].[Schemes] ON [dbo].[PU].[Table] = [dbo].[Schemes].[Table]

INSERT INTO @Fields
SELECT [Column] = x.[ColumnName],[Value] = x.[ColumnValue] FROM [dbo].[JoinDelimitedColumnValue](@FieldValues, @FieldColumns, @Delimiter) x

最佳答案

这种数据结构使这种方式变得比应有的更加复杂。您可以在此处利用 Jeff Moden 的分离器。 http://www.sqlservercentral.com/articles/Tally+Table/72993/该拆分器与所有其他拆分器的主要区别在于,它返回每个元素的序数位置。为什么所有其他分离器不这样做,这超出了我的范围。对于这样的事情,这是需要的。您有两组分隔数据,并且必须确保它们都以正确的顺序重新组合。

我看到的最大问题是主表中没有任何内容可以充当正确排序结果的 anchor 。您需要一些东西,甚至是一个身份来确保输出行保持“在一起”。为了实现这一点,我只是向 PU 表添加了一个身份。

alter table PU add RowOrder int identity not null

现在我们有了一个 anchor ,这对于一个简单的查询来说仍然有点麻烦,但它是可以实现的。

这样的东西现在可以工作了。

with MyValues as
(
    select p.[Table]
        , ColumnPosition = x.ItemNumber
        , ColumnValue = x.Item
        , RowOrder
    from PU p
    cross apply dbo.DelimitedSplit8K(p.[Values], ';') x
)

, ColumnData as
(
    select ColumnName = replace(replace(x.Item, ']', ''), '[', '') 
        , ColumnPosition = x.ItemNumber
        , s.[Table]
    from Schemes s
    cross apply dbo.DelimitedSplit8K(s.Columns, ';') x
)

select cd.[Table]
    , v.ColumnValue
    , cd.ColumnName
from MyValues v
join ColumnData cd on cd.[Table] = v.[Table] 
    and cd.ColumnPosition = v.ColumnPosition
order by v.RowOrder
    , v.ColumnPosition

关于sql-server - 通过分隔符分割多个字段,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49733475/

相关文章:

sql - VARCHAR 转十进制

sql - 如何在 SQL Server 中将 DateTime 值迁移到 DateTimeOffset?

sql-server - BCP BLOB 导出损坏的文件

SQL - 如何检索一列中排名最高的行,而另一列中具有多个重复行

MySQL 与 SQL Server 与 Oracle

sql-server - SQL表的PK的数据类型如何影响查询性能?

sql-server - 为新表插入行,涵盖 TableA 和 TableB 中 ID 列的组合

sql-server - SQL - 从合并查询过滤器获取数据库名称

sql - SQL 从每个逗号分隔值中获取正确的值

sql - PIVOT 并复制表的某些列