在 SELECT 期间用 JOIN 操作替换子记录列

Posted

技术标签:

【中文标题】在 SELECT 期间用 JOIN 操作替换子记录列【英文标题】:Replacing child record columns during SELECT with JOIN operation 【发布时间】:2015-09-02 09:50:09 【问题描述】:

我正在尝试通过将两个表连接在一起来创建一个视图,这绝不是困难的。但是有一个但是。

我基本上有两张桌子。其中一个存储项目和标签之间的关联,另一个以父子形式存储标签(8 个父母,每个父母有 4-6 个孩子)。

请允许我从我的项目关联表开始说明:

AssociationId | ItemId | TagId
------------------------------
1             | 2      | 1
2             | 10     | 2
3             | 3      | 1
4             | 5      | 7
...

还有我的标签表:

TagId | ParentId | Name
------------------------------
1     | NULL     | abc sds
2     | 1        | sjdksd as
3     | 1        | djfsd dfs d
4     | NULL     | ujkjsd as
...

如您所见,可以通过使用ParentId IS NULL 装饰查询来找到每个父级。

SELECT ITA.AssociationId, ITA.ItemId, ITA.TagId, T.Name as TagName
FROM Item_Tag_Association AS ITA
INNER JOIN Tags AS T
ON T.TagId = ITA.TagId
WHERE T.ParentId IS NULL

如果我要将TagIdParentId IS NULL 上的两个表连接起来,我会找到所有带有父标签的项目。

如果我要将TagId 上的两个表与ParentId IS NOT NULL 连接起来,我会找到所有带有子标签的项目。

但我真正想要的是一个项目和标签列表,其中子标签被它们的父标签替换。

使用上面的示例,结果将如下所示:

AssociationId | ItemId | TagId | TagName
----------------------------------------
1             | 2      | 1     | abc sds
2             | 10     | 1     | abc sds
3             | 3      | 1     | abc sds
4             | 5      | 7     | ysdasjdhas
...

至于我想这样做的原因是,我可以计算特定父标签(或该父标签的子标签)与项目相关联的次数。正如您现在所猜到的那样,每个孩子都应该为其父母做出贡献。

SELECT DISTINCT ItemId, TagId, TagName, COUNT(ItemId) OVER (PARTITION BY TagId) AS Count
FROM vw_My_View
ORDER BY Count DESC

我正在使用 SQL Server 2008 R2。欢迎所有建设性的建议。

【问题讨论】:

@Tanner 根据我的问题,最后一张表格说明了所需的结果。 好的,我认为这是计数部分之前的中间步骤。 【参考方案1】:

只要您的父/子层次结构中只有一个级别,您就可以从标签到自身进行额外的左连接以找到父级(如果存在的话):

SELECT ita.AssociationId, ita.ItemId, ISNULL(parent.TagId, t.TagId), ISNULL(parent.Name, t.Name) AS TagName
FROM Item_Tags_Association ita
  JOIN Tags t ON ita.TagId = t.TagId
  LEFT JOIN Tags parent ON t.ParentId = parent.TagId

第一个(内部)连接可以连接到子标签或父标签。左连接将包括父标签 if 关联的标签有父标签。 如果不为空,ISNULL 将确保使用来自第二个(左)连接的值,否则使用来自第一个连接的值。

如果您不想与ISNULL(或COALESCE)打交道,您可以像这样使用UNION 和CTE:

WITH ParentTags AS (SELECT * FROM Tags WHERE ParentId IS NULL)
SELECT ita.AssociationId, ita.ItemId, t.TagId, t.Name AS TagName
FROM ParentTags t
  JOIN Item_Tags_Association ita ON ita.TagId = t.TagId
UNION ALL 
SELECT ita.AssociationId, ita.ItemId, t.TagId, t.Name AS TagName
FROM ParentTags t
  JOIN Tags child ON t.TagId = child.ParentId
  JOIN Item_Tags_Association ita ON ita.TagId = child.TagId 

【讨论】:

@Iarsts 您认为父/子层次结构只有一个级别是正确的。 我可以确认ISNULL 解决方案正在工作,但我还不明白如何/为什么。但是ISNULL()的使用还是蛮巧妙的。 尝试在帖子中添加解释。基本上,左连接包含来自父标签的信息 if 它存在(如果关联中的标签定义了 parentid)。 isnull 用于使用来自此父标记的值(如果找到),否则使用来自第一个连接的值(这将是一个没有 parentid -> 的标记,这意味着它是开始的父标记)。这个解释有点繁琐,希望大家理解。 union/cte 方法也应该有效,并且可能更容易阅读:) @Iarsts union/cte 方法确实更容易阅读,我确认它是一个可行的解决方案。

以上是关于在 SELECT 期间用 JOIN 操作替换子记录列的主要内容,如果未能解决你的问题,请参考以下文章

JOIN 替代 SELECT 子查询

SELECT 中的子查询或 JOIN 中的子查询?

Oracle - 用正则表达式列表替换记录的子字符串

oracle的full join关联的表限制条件在on后面与限制在子查询的结果是不一样

数据库的SQL语句中,嵌套查询和连接查询有啥区别,说的详细的

通过 LEFT JOIN 优化 SQL 子查询