27

我真的很想在这里得到一些建议,以提供一些我正在将 Exchange 2007 中的消息跟踪日志插入 SQL 的背景信息。由于我们每天有数以百万计的行,因此我使用批量插入语句将数据插入到 SQL 表中。

实际上,我实际上是批量插入临时表,然后从那里将数据合并到实时表中,这是为了测试解析问题,因为某些字段在值周围有引号等。

这很好用,除了收件人地址列是一个由 ; 分隔的分隔字段之外。字符,有时可能会非常长,因为可能有很多电子邮件收件人。

我想采用这一列,并将值拆分为多行,然后将这些行插入另一个表中。问题是我正在尝试的任何事情要么花费太长时间,要么没有按照我想要的方式工作。

以这个示例数据为例:

message-id                                              recipient-address
2D5E558D4B5A3D4F962DA5051EE364BE06CF37A3A5@Server.com   user1@domain1.com
E52F650C53A275488552FFD49F98E9A6BEA1262E@Server.com     user2@domain2.com
4fd70c47.4d600e0a.0a7b.ffff87e1@Server.com              user3@domain3.com;user4@domain4.com;user5@domain5.com

我希望将其格式化为我的收件人表中的以下内容:

message-id                                              recipient-address
2D5E558D4B5A3D4F962DA5051EE364BE06CF37A3A5@Server.com   user1@domain1.com
E52F650C53A275488552FFD49F98E9A6BEA1262E@Server.com     user2@domain2.com
4fd70c47.4d600e0a.0a7b.ffff87e1@Server.com              user3@domain3.com
4fd70c47.4d600e0a.0a7b.ffff87e1@Server.com              user4@domain4.com
4fd70c47.4d600e0a.0a7b.ffff87e1@Server.com              user5@domain5.com

有没有人对我如何去做这件事有任何想法?

我非常了解 PowerShell,所以我尝试过,但是即使在 28K 记录上的 foreach 循环也需要很长时间才能处理,我需要能够尽可能快速/高效地运行的东西。

谢谢!

4

4 回答 4

62

如果您使用的是 SQL Server 2016+

您可以使用新STRING_SPLIT功能,我在这里写过博客,Brent Ozar 也写过这里

SELECT s.[message-id], f.value
  FROM dbo.SourceData AS s
  CROSS APPLY STRING_SPLIT(s.[recipient-address], ';') as f;

如果您仍在使用 SQL Server 2016 之前的版本

创建拆分函数。这只是众多示例之一:

CREATE FUNCTION dbo.SplitStrings
(
    @List       NVARCHAR(MAX),
    @Delimiter  NVARCHAR(255)
)
RETURNS TABLE
AS
    RETURN (SELECT Number = ROW_NUMBER() OVER (ORDER BY Number),
        Item FROM (SELECT Number, Item = LTRIM(RTRIM(SUBSTRING(@List, Number, 
        CHARINDEX(@Delimiter, @List + @Delimiter, Number) - Number)))
    FROM (SELECT ROW_NUMBER() OVER (ORDER BY s1.[object_id])
        FROM sys.all_objects AS s1 CROSS APPLY sys.all_objects) AS n(Number)
    WHERE Number <= CONVERT(INT, LEN(@List))
        AND SUBSTRING(@Delimiter + @List, Number, 1) = @Delimiter
    ) AS y);
GO

我在这里讨论了其他一些,这里以及比一开始在这里拆分更好的方法。

现在您可以简单地通过以下方式进行推断:

SELECT s.[message-id], f.Item
  FROM dbo.SourceData AS s
  CROSS APPLY dbo.SplitStrings(s.[recipient-address], ';') as f;

另外我建议不要在列名中添加破折号。这意味着您必须始终将它们放入[square brackets].

于 2012-06-13T15:31:36.217 回答
5

SQL Server 2016 包含一个新的表函数 string_split(),类似于之前的解决方案。

唯一的要求是将兼容性级别设置为 130 (SQL Server 2016)

于 2017-11-04T16:42:51.870 回答
4

您可以使用CROSS APPLY(在 SQL Server 2005 及更高版本中可用)和STRING_SPLIT函数(在 SQL Server 2016 及更高版本中可用):

DECLARE @delimiter nvarchar(255) = ';';

-- create tables
CREATE TABLE MessageRecipients (MessageId int, Recipients nvarchar(max));
CREATE TABLE MessageRecipient (MessageId int, Recipient nvarchar(max));

-- insert data
INSERT INTO MessageRecipients VALUES (1, 'user1@domain.com; user2@domain.com; user3@domain.com');
INSERT INTO MessageRecipients VALUES (2, 'user@domain1.com; user@domain2.com');

-- insert into MessageRecipient
INSERT INTO MessageRecipient
SELECT MessageId, ltrim(rtrim(value))
FROM MessageRecipients 
CROSS APPLY STRING_SPLIT(Recipients, @delimiter)

-- output results
SELECT * FROM MessageRecipients;
SELECT * FROM MessageRecipient;

-- delete tables
DROP TABLE MessageRecipients;
DROP TABLE MessageRecipient;

结果:

MessageId   Recipients
----------- ----------------------------------------------------
1           user1@domain.com; user2@domain.com; user3@domain.com
2           user@domain1.com; user@domain2.com

MessageId   Recipient
----------- ----------------
1           user1@domain.com
1           user2@domain.com
1           user3@domain.com
2           user@domain1.com
2           user@domain2.com
于 2018-03-09T06:47:01.820 回答
-1

for table = "yelp_business",将由分隔的列categories值拆分;为行并显示为category列。

SELECT unnest(string_to_array(categories, ';')) AS category
   FROM yelp_business;
于 2021-07-10T19:40:28.083 回答