【问题标题】:Updating a comma seperated column更新逗号分隔的列
【发布时间】:2020-08-15 16:42:29
【问题描述】:

我有以下表格:

CREATE TABLE [dbo].[articles](
  [ID] [int] IDENTITY(1,1) NOT NULL,
  [name] [nvarchar](200) NOT NULL,
 CONSTRAINT [PK_articles] PRIMARY KEY CLUSTERED 
(
  [ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
CREATE TABLE [dbo].[test_users](
  [ID] [int] IDENTITY(1,1) NOT NULL,
  [GUID] [nvarchar](100) NOT NULL,
 CONSTRAINT [PK_test_users] PRIMARY KEY CLUSTERED 
(
  [ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY],
 CONSTRAINT [unique_guid] UNIQUE NONCLUSTERED 
(
  [GUID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
CREATE TABLE [dbo].[relational](
  [ID] [int] IDENTITY(1,1) NOT NULL,
  [articleid] [int] NOT NULL,
  [userguid] [nvarchar](100) NOT NULL,
 CONSTRAINT [PK_relational] PRIMARY KEY CLUSTERED 
(
  [ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]


ALTER TABLE [dbo].[relational]  WITH CHECK ADD  CONSTRAINT [FK_relational_articles] FOREIGN KEY([articleid])
REFERENCES [dbo].[articles] ([ID])


ALTER TABLE [dbo].[relational] CHECK CONSTRAINT [FK_relational_articles]


ALTER TABLE [dbo].[relational]  WITH CHECK ADD  CONSTRAINT [FK_relational_relational] FOREIGN KEY([userguid])
REFERENCES [dbo].[test_users] ([GUID])


ALTER TABLE [dbo].[relational] CHECK CONSTRAINT [FK_relational_relational]
GO
CREATE TABLE [dbo].[seperated](
  [ID] [int] IDENTITY(1,1) NOT NULL,
  [userguid] [nvarchar](100) NOT NULL,
  [articles] [nvarchar](max) NOT NULL,
 CONSTRAINT [PK_seperated] PRIMARY KEY CLUSTERED 
(
  [ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]

ALTER TABLE [dbo].[seperated]  WITH CHECK ADD  CONSTRAINT [FK_seperated_test_users] FOREIGN KEY([userguid])
REFERENCES [dbo].[test_users] ([GUID])


ALTER TABLE [dbo].[seperated] CHECK CONSTRAINT [FK_seperated_test_users]
GO
SET IDENTITY_INSERT [dbo].[test_users] ON 
INSERT [dbo].[test_users] ([ID], [GUID]) VALUES (1, N'guid1')
INSERT [dbo].[test_users] ([ID], [GUID]) VALUES (2, N'guid2')
INSERT [dbo].[test_users] ([ID], [GUID]) VALUES (3, N'guid3')
INSERT [dbo].[test_users] ([ID], [GUID]) VALUES (4, N'guid4')
SET IDENTITY_INSERT [dbo].[test_users] OFF
GO

4 行受影响

SELECT * FROM [dbo].[test_users]
GO
身份证 |图形用户界面 -: | :---- 1 |指导1 2 |引导2 3 |引导3 4 |指导4
SET IDENTITY_INSERT [dbo].[articles] ON 
INSERT [dbo].[articles] ([ID], [name]) VALUES (1, N'article1')
INSERT [dbo].[articles] ([ID], [name]) VALUES (2, N'article2')
INSERT [dbo].[articles] ([ID], [name]) VALUES (3, N'article3')
SET IDENTITY_INSERT [dbo].[articles] OFF
GO

3 行受影响

SELECT * FROM [dbo].[articles]
GO
身份证 |姓名 -: | :-------- 1 |第1条 2 |第2条 3 |第3条
SET IDENTITY_INSERT [dbo].[relational] ON 
INSERT [dbo].[relational] ([ID], [articleid], [userguid]) VALUES (1, 1, N'guid1')
INSERT [dbo].[relational] ([ID], [articleid], [userguid]) VALUES (2, 1, N'guid2')
INSERT [dbo].[relational] ([ID], [articleid], [userguid]) VALUES (3, 2, N'guid2')
INSERT [dbo].[relational] ([ID], [articleid], [userguid]) VALUES (4, 1, N'guid3')
INSERT [dbo].[relational] ([ID], [articleid], [userguid]) VALUES (5, 3, N'guid3')
SET IDENTITY_INSERT [dbo].[relational] OFF
GO

5 行受影响

SELECT * FROM [dbo].[relational]
GO
身份证 |文章ID |用户指南 -: | --------: | :-------- 1 | 1 |指导1 2 | 1 |引导2 3 | 2 |引导2 4 | 1 |引导3 5 | 3 |引导3
SET IDENTITY_INSERT [dbo].[seperated] ON 
INSERT [dbo].[seperated] ([ID], [userguid], [articles]) VALUES (1, N'guid1', N'1')
INSERT [dbo].[seperated] ([ID], [userguid], [articles]) VALUES (3, N'guid2', N'1,2')
INSERT [dbo].[seperated] ([ID], [userguid], [articles]) VALUES (4, N'guid3', N'1,3')
INSERT [dbo].[seperated] ([ID], [userguid], [articles]) VALUES (5, N'guid4', N'')
SET IDENTITY_INSERT [dbo].[seperated] OFF
GO

4 行受影响

SELECT * FROM [dbo].[seperated]
GO
身份证 |用户指南 |文章 -: | :------- | :-------- 1 |指导1 | 1 3 |指导2 | 1,2 4 |引导3 | 1,3 5 |指导4 |

db小提琴here

在文章表上有插入、删除和更新的触发器。

  • 插入后,在关系表中创建记录(仅适用于新文章)
  • 删除后,关系表中的记录被删除(仅适用于删除文章)
  • 更新后,会创建/删除记录(仅适用于更新的文章)

之后需要更新分隔表。

对于每个用户,必须恰好有 1 条记录,文章以逗号分隔(基本上是 user.guid,SELECT * FROM 关系式 WHERE userguid = user.guid)

在每次插入/删除/更新之后,使用 XML FOR PATH 完全更新分隔表。

然而这不再是一个可行的选择,因为这个操作不够快(关系表中有大约 5 - 1000 万条记录)

要解决此问题,我想仅为新/更新/删除的文章更新分隔表:

在触发器中,我用@articleId 声明了articleid。

插入时:添加 ',articleId' 或添加 '@articleId' 如果该行为空(不能为空)
删除时:从所有行中删除 articleId
更新时:在需要时删除并在需要时添加(取决于在关系表中找到的内容)

因为我基本上从不使用逗号分隔的东西,所以我不知道该怎么做。
任何帮助将不胜感激:)

注意事项:

  • 不,我不能在应用程序中执行此操作,因为创建文章记录的应用程序无法连接到数据库(它使用了一些晦涩的 API),而且由于网络原因也无法连接。
  • 插入、更新和删除的数量非常少(因此我们可以在触发器中执行此操作)
  • 我必须使用逗号分隔的结构,我无法更改模型,因为在此数据库之上运行的应用程序是第 3 方应用程序,我们无法更改,也无法要求修改...李>
  • 是的,这些表已缩小,并非所有列都在这里,因为它们对这个问题无关紧要

【问题讨论】:

  • 在逗号分隔的字符串中存储多个值是构建数据的错误方式。修复您的数据模型。
  • "你成为了一个经典错误的牺牲品,其中最著名的是“永远不要卷入亚洲的陆战”,但鲜为人知的是:“永远不要使用逗号” - 在一列中分隔数据! "
  • 为什么你的表只有一列,这是一个标识?抛开模型的问题(你现在可能没有能力改变它,我去过那里),你能分享一些示例数据和预期效果吗?

标签: sql sql-server csv tsql


【解决方案1】:

早安,

在每次插入/删除/更新之后,使用 XML FOR PATH 完全更新分隔表。

乍一看这毫无意义......

  1. 如果您编辑用户 x,y,z,那么您应该只更新 [separated] 表中的这些行。我看不出为什么要更新所有行。

  2. SQL Server 是一个表格数据库,即使它支持 XML 和 JSON 等非结构化数据,但在我们需要解析值时它通常表现不佳。

    您在 [relational] 表中拥有 [separated] 表所需的所有数据。所以解析每个值是没有意义的。

    你关心什么是当前值?如果您更改值(添加或删除 ID),则只需使用更改的用户 GUID(using the inserted/deleted logical table)使用来自 [relational] 表过滤器的信息更新 [seperated] 表中的值(字符串)。

1+2 可以使用“UPDATE FROM SELECT”来完成。您可以使用带有 STRING_AGG 函数的简单查询来获取 ID 列表。不要解析内容,而只需更新(如果需要,替换)并为插入/删除的所有已更改的行(并且只有那些更改的行)设置列 [articles] 的值

插入时:如果该行为空(不能为空),则添加 ',articleId' 或添加 '@articleId' 删除时:从所有行中删除 articleId 更新时:需要时删除,需要时添加(取决于在关系表中找到的内容)

同样的问题。不要解析每种情况的值。添加 ID 很简单,但删除可能更复杂。没有理由从字符串中“添加”或“删除”任何内容。只需将字符串的值更新为每次包含所有相关 ID 的新值。

注意!如果您将提供您的代码而不是故事,那么我们可以提供一个工作示例。您应该提供所有查询以重现整个场景,包括触发器以及您的新旧解决方案/尝试



方法 1(简单的解决方案,最适合删除 ID,在某些情况下适用于所有任务):更新 [seperated] 中的行,这些行在 [relational] 中使用 [inserted] 和 [deleted] 表进行了更改

一般而言,查询应该类似于下面的内容(请记住,这是在 [relational] 表更新之后完成的 - 我从该表中读取数据并重新创建已更改行的值)

-------------------------------------------------- Replace
--> assuming that each user has a row in [seperated] even if no id (as described)

;With NyCTE as (
    SELECT [userguid], SA = STRING_AGG([articleid], ',')
        WITHIN GROUP (ORDER BY [articleid] ASC)
    FROM dbo.[relational]
    where userguid in (select userguid from inserted)
    GROUP BY [userguid]
)
UPDATE [seperated] SET articles = NyCTE.SA
FROM[seperated]
INNER JOIN NyCTE ON NyCTE.userguid = [seperated].userguid
GO
--------------------------------------------------


方法 2(仅适用于添加 ID,在大多数情况下,这是最好的方法,因为我们确实需要聚合 [relational] 表,但只需要聚合插入的表):将 ID 添加到 [seperated] 中行的现有值,使用 [inserted] 和 [deleted] 表在 [relational] 中进行了更改

如果您想使用将 ID 添加到现有字符串的方法(请记住,SQL Server 不会添加或编辑字符串,而是在我们使用 UPDATE 时无论如何都会替换它。

---------------------------------------------------- Add
;With NyCTE as (
    SELECT [userguid], SA = STRING_AGG([articleid], ',')
        WITHIN GROUP (ORDER BY [articleid] ASC)
    FROM dbo.inserted
    where userguid in (select userguid from inserted)
    GROUP BY [userguid]
)
UPDATE [seperated] SET articles = REPLACE(REPLACE('@' + CONCAT (articles,',', SA), '@,', ''), '@', '')
FROM[seperated]
INNER JOIN NyCTE ON NyCTE.userguid = [seperated].userguid
GO
--------------------------------------------------


---------更多信息--------------

缺少信息和说明

在文章表上有插入、删除和更新的触发器。

请提供重现问题的完整代码(缺少触发器代码)。总是更喜欢在技术论坛中使用代码而不是故事。

目前使用 XML FOR PATH 完全更新分隔表

代码在哪里?!?

同样,您提供的是故事而不是代码。

在触发器中,我用@articleId 声明了articleid...

再来一次!您提供故事而不是示例代码。向我们展示您的工作。提供您测试的代码:-(

听起来您打算使用循环,这可能是个坏主意,或者更糟糕的是,您计划的解决方案只是为了解决一行更改。你有什么要插入的 100k 行?!?

触发器应该可能与“INSERTED”逻辑表一起使用,并为任务使​​用简单的 JOIN、EXCEPT 或 INTERSECT。您应该计划触发器以处理 SET 数据而不是单行。

插入时:添加 ',articleId' 或添加 '@articleId' 如果行为空(不能为空)。删除时:从所有行中删除 articleId。更新时:需要时删除,需要时添加(取决于在关系表中找到的内容)

SQL Server 不会将字符串“添加”到表中的值中。它只能更新整个值。它可以提前解析值,然后使用 UPDATE 并替换表中的值。如前所述,SQL Server 旨在在关系模型上获得最佳性能,而不是在循环中解析简单的字符串。

注意!如果您想安全起见,您应该每次都确认这确实是一个新的 id。如果您这样做,那么您将获得更复杂的任务。

【讨论】:

    猜你喜欢
    • 2013-06-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-03
    相关资源
    最近更新 更多