如何将两个单独表中的两列拆分为视图中的多行?
Posted
技术标签:
【中文标题】如何将两个单独表中的两列拆分为视图中的多行?【英文标题】:How do I split two columns in two separate tables into joined multiple rows in a view? 【发布时间】:2016-03-29 10:01:59 【问题描述】:我有两个单独的表格,每个表格都包含字符分隔的单元格。一张表包含所有关键数据,另一张表包含所有 val 数据。在应用程序中,两个表都加载到记录集中,两个单元格被拆分为两个数组,然后将这些数组“并排”使用以创建键/值对。
试图将数据库与应用程序解耦,我基本上会创建一个模拟这种行为的视图。
我创建了一些示例表和数据来更好地说明它。
/* Create tables */
CREATE TABLE [dbo].[tblKeys](
[KeyId] [int] NOT NULL, /*PK*/
[KeyData] [nvarchar](max) NULL
)
CREATE TABLE [dbo].[tblValues](
[ValId] [int] NOT NULL, /*PK*/
[KeyId] [int] NOT NULL, /*FK*/
[Language] [nvarchar](5) NOT NULL,
[ValData] [nvarchar](max) NULL
)
/* Populate tables */
INSERT INTO [dbo].[tblKeys] ([KeyId], [KeyData]) VALUES
(1, '1|2|3'),
(2, '1|2|3|4'),
(3, '2|1')
INSERT INTO [dbo].[tblValues] ([ValId], [KeyId], [Language], [ValData]) VALUES
(1, 1, 'en', 'Apple|Orange|Pear'),
(2, 1, 'sv-se', 'Äpple|Apelsin|Päron'),
(3, 2, 'en', 'Milk|Butter|Cheese|Cream'),
(4, 2, 'sv-se', 'Mjölk|Smör|Ost|Grädde'),
(5, 3, 'en', 'Male|Female'),
(6, 3, 'sv-se', 'Man|Kvinna')
视图中所需的最终结果如下所示:
| KeyId | KeyData | Language | ValData
+-------+---------+----------+----------+
| 1 | 1 | en | Apple |
| 1 | 2 | en | Orange |
| 1 | 3 | en | Pear |
| 1 | 1 | sv-se | Äpple |
| 1 | 2 | sv-se | Apelsin |
| 1 | 3 | sv-se | Päron |
...
等等
我在 *** 上看到过类似的问题,但它们都处理以类似方式倾斜的单个表。在将 KeyData 和 ValData 两列中的数据位置重新组合成正确的键/值对时,我需要倾斜这两个表。
我将如何以有效的方式做到这一点?
[编辑]:数据库设计不是我的。这是我继承的一些旧废话。我知道这很糟糕。就像在可怕中一样糟糕。
【问题讨论】:
不要像 | 那样存储数据分开的物品。只会给你带来很多麻烦。 (你今天可能已经注意到了…… 我知道。不过,这是我无法控制的遗留问题。我正在努力让它可以忍受。我自己永远不会那样做。 顺便说一句:感谢复制'n'可粘贴的测试场景和预期的输出。值得一票 如果你想要一个答案,最好提供最好的输入来处理。如果您需要帮助,请确保帮助者可以专注于实际问题,而不是设置。 :-) 【参考方案1】:这对你有用。但是:你真的应该改变你的数据设计!
;WITH Splitted AS
(
SELECT v.Language
,v.ValData
,v.ValId
,k.KeyData
,k.KeyId
,CAST('<x>' + REPLACE(v.ValData,'|','</x><x>') + '</x>' AS XML) AS ValData_XML
,CAST('<x>' + REPLACE(k.KeyData,'|','</x><x>') + '</x>' AS XML) AS KeyData_XML
FROM tblValues AS v
INNER JOIN tblKeys AS k ON v.KeyId=k.KeyId
)
,NumberedValData AS
(
SELECT ROW_NUMBER() OVER(PARTITION BY KeyId,Language ORDER BY (SELECT NULL)) AS RowNumber
,KeyId
,Language
,A.B.value('.','varchar(max)') AS ValD
FROM Splitted
CROSS APPLY Splitted.ValData_XML.nodes('/x') AS A(B)
)
,NumberedKeyData AS
(
SELECT ROW_NUMBER() OVER(PARTITION BY KeyId,Language ORDER BY (SELECT NULL)) AS RowNumber
,KeyId
,Language
,A.B.value('.','varchar(max)') AS KeyD
FROM Splitted
CROSS APPLY Splitted.KeyData_XML.nodes('/x') AS A(B)
)
,Combined AS
(
SELECT nk.KeyId
,nk.KeyD
,nk.Language
,nv.ValD
FROM NumberedKeyData AS nk
INNER JOIN NumberedValData AS nv ON nk.KeyId=nv.KeyId AND nk.Language=nv.Language AND nk.RowNumber=nv.RowNumber
)
SELECT Combined.KeyId
,Combined.KeyD AS KeyData
,Splitted.Language
,Combined.ValD AS ValData
FROM Splitted
INNER JOIN Combined ON Splitted.KeyId=Combined.KeyId AND Splitted.Language=Combined.Language
ORDER BY Splitted.KeyId,Splitted.
结果
KeyId KeyData Language ValData
1 1 en Apple
1 2 en Orange
1 3 en Pear
1 1 sv-se Äpple
1 2 sv-se Apelsin
1 3 sv-se Päron
2 1 en Milk
2 2 en Butter
[...]
【讨论】:
是的 - 数据库设计很垃圾。不过,不是我的电话。在我加入公司之前,这是一款非常古老的自制软件。最初的数据库“设计者”似乎对数据库设计一无所知。通过分析数据库设计,我猜测它起源于一些业务经理的 Excel 电子表格,该电子表格刚刚被移植到 SQL Server 中。 @CBDuRietz,可怜的家伙 :-) 这样的“遗留垃圾”可能是一场噩梦......我将编辑我的答案,将PARTITION BY KeyId,Language
添加到 ROW_NUMBER()
中
我应该指出,所涉及的原始表更糟糕。我在问题中提出的那些已经过清理和简化,以专注于手头的问题。 :-)
@CBDuRietz 祝你好运,编码愉快!如果您喜欢我的答案,请投票,如果它解决了您的问题,请 - 另外将其标记为已接受的答案,谢谢!
似乎很有魅力。不过,在将其标记为可接受的答案之前,我会尝试将其移植回真实数据库。我可能有一两个后续问题。数据库是一个雷区。以上是关于如何将两个单独表中的两列拆分为视图中的多行?的主要内容,如果未能解决你的问题,请参考以下文章
两个表,一个表中的两列关联另一个表的id,如何将这个表中的两列显示为另一个表id对应的内容
如何在 Visual Studio 2019 的 Datagrid 视图中连接 MySQL DataTable 中的两列?