4

我正在研究 MSSQL,试图将一个字符串列拆分为多列。字符串列具有用分号分隔的数字,例如:

190230943204;190234443204;

但是,某些行的数字比其他行多,因此在数据库中您可以拥有

190230943204;190234443204;
121340944534;340212343204;134530943204

我已经看到了一些将一列拆分为特定数量的列的解决方案,但不是可变列。数据较少的列(用逗号分隔的 2 系列字符串而不是 3)将在第三位有空值。

想法?如果我必须澄清任何事情,请告诉我。

4

3 回答 3

3

将这些数据分成单独的列是一个很好的开始(逗号分隔的值是异端)。但是,“可变数量的属性”通常应该建模为一对多关系

CREATE TABLE main_entity (
  id INT PRIMARY KEY,
  other_fields INT
);

CREATE TABLE entity_properties (
  main_entity_id INT PRIMARY KEY,
  property_value INT,
  FOREIGN KEY (main_entity_id) REFERENCES main_entity(id)
);

entity_properties.main_entity_id是 的main_entity.id

恭喜你,你走在了正确的道路上,这叫做规范化。你即将达到第一范式。

但是请注意,这些属性应该具有明显相似的性质(即所有电话号码或地址等)。不要陷入阴暗面(也就是Entity-Attribute-Value 反模式),并试图将所有属性放入同一个表中。如果您可以识别多种类型的属性,请将每种类型存储在单独的表中。

于 2013-07-01T22:40:45.750 回答
1

如果这些都是固定长度的字符串(如问题所示),那么您可以相当简单地完成工作(至少相对于其他解决方案):

select substring(col, 1+13*(n-1), 12) as val
from t join
     (select 1 as n union all select union all select 3
     ) n
     on len(t.col) <= 13*n.n

如果所有条目的大小相同(如果它们的大小不同,则不那么容易),这是一个有用的技巧。但是,请考虑数据结构,因为分号(或逗号)分隔的列表不是一个很好的数据结构。

于 2013-07-01T23:01:16.063 回答
1

如果我是你,我会创建一个简单的函数,用 ';' 分隔值 像这样:

IF EXISTS (SELECT * FROM sysobjects WHERE id = object_id(N'fn_Split_List') AND xtype IN (N'FN', N'IF', N'TF'))
BEGIN
    DROP FUNCTION [dbo].[fn_Split_List]
END
GO

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE FUNCTION [dbo].[fn_Split_List](@List NVARCHAR(512))
RETURNS @ResultRowset TABLE ( [Value] NVARCHAR(128) PRIMARY KEY)
AS
BEGIN
    DECLARE @XML xml = N'<r><![CDATA[' + REPLACE(@List, ';', ']]></r><r><![CDATA[') + ']]></r>'

    INSERT INTO @ResultRowset ([Value])
    SELECT DISTINCT RTRIM(LTRIM(Tbl.Col.value('.', 'NVARCHAR(128)')))
    FROM @xml.nodes('//r') Tbl(Col)

    RETURN
END

GO

而不是简单地以这种方式调用:

SET NOCOUNT ON
GO

    DECLARE @RawData TABLE( [Value] NVARCHAR(256))

    INSERT INTO @RawData ([Value] )
    VALUES ('1111111;22222222')
          ,('3333333;113113131')
          ,('776767676')
          ,('89332131;313131312;54545353')

    SELECT SL.[Value]
    FROM @RawData AS RD
    CROSS APPLY [fn_Split_List] ([Value])  as SL

SET NOCOUNT OFF
GO

结果如下:

Value
1111111
22222222
113113131
3333333
776767676
313131312
54545353
89332131 

无论如何,函数中的逻辑并不复杂,因此您可以轻松地将其放在您需要的任何地方。

注意:用';'分隔的值的数量没有限制,但函数中有长度限制,如果需要,可以设置为 NVARCHAR(MAX)。

编辑:

正如我所看到的,您的示例中有一些行会导致函数返回空字符串。例如:

number;number;

将返回:

number
number
'' (empty string)

要清除它们,只需将以下 where 子句添加到上面的语句中,如下所示:

SELECT SL.[Value]
FROM @RawData AS RD
CROSS APPLY [fn_Split_List] ([Value])  as SL
WHERE LEN(SL.[Value]) > 0
于 2013-07-02T06:23:50.890 回答