【问题标题】:Sql Join Tables Partial Join Concatenate Results?Sql连接表部分连接连接结果?
【发布时间】:2012-08-15 22:20:49
【问题描述】:

我有两张桌子。一列有 2 列 - ID、标签。 tags 列是一个以逗号分隔的标签值列表。

我是第二张表,也有两列。一个是标签列表,每个标签都在自己的行中,第二列是该标签的标签。

我需要做的是为每个 ID 创建一个输出查询,使用分号分隔的标签标签列表,为在其列表中具有相应标签值的每个 id。

这是两个表的简短示例:

Table1 - 
ID---Tags
1---tag1,tag2,tag3,tag6
2---tag2,tag4,tag5
3---tag3,tag8,tag9,tag10
4---tag1,tag2,tag6
5---tag1,tag4,tag5

Table2 - 
tagname---taglabel
tag1--- Nice Name Tag1
tag2--- Nice Name Tag2
tag3--- Nice Name Tag3
tag4--- Nice Name Tag4
tag5--- Nice Name Tag5
tag6--- Nice Name Tag6
tag7--- Nice Name Tag7
tag8--- Nice Name Tag8
tag9--- Nice Name Tag9
tag10--- Nice Name Tag10

想要的结果集 -

ID---TagsNames
1---Nice Name Tag1,Nice Name Tag2,Nice Name Tag3,Nice Name Tag6
2---Nice Name Tag2,Nice Name Tag4,Nice Name Tag5
3---Nice Name Tag3,Nice Name Tag8,Nice Name Tag9,Nice Name Tag10
4---Nice Name Tag1,Nice Name Tag2,Nice Name Tag6
5---Nice Name Tag1,Nice Name Tag4,Nice Name Tag5

我尝试为初始连接设置查询,认为我可以获取该结果集并将行连接起来,但我似乎无法让部分连接工作。这是那个查询:

 SELECT gt.id as g
      ,gt.tags as tg
      ,tt.HitTagName as ht
      ,tt.termlabel as tl 
FROM tblidtags gt (nolock)
JOIN tbltaglabels tt (Nolock) ON gt.tg like '%' + tt.ht + '%'

非常感谢任何建议!

【问题讨论】:

  • 我认为这实际上应该是 JOIN tbltaglabels tt (Nolock) ON tt.ht like '%' + gt.tg + '%' ,但它仍然不起作用
  • 我收到此错误,只是为了提供详细信息:消息 207,级别 16,状态 1,第 7 行无效的列名称“ht”。消息 207,级别 16,状态 1,第 7 行无效的列名称“tg”。
  • 那个tags列是多值列,是SQL中万恶之源之一。不要使用它们。在处理该列时,您将遇到比其他任何事情更多的问题。
  • 我别无选择。这是我接收数据的方式
  • 我必须将标签列从 ntext 转换为 nvarchar。现在我有这个 SELECT gt.id as g ,gt.tags as tg ,tt.HitTagName as ht ,tt.termlabel as tl FROM tblidtags gt (nolock) left JOIN tbltaglabels tt (Nolock) ON tt.HitTagName like '%' + gt.tags + '%' 。它开始运行,但随后我收到消息:Msg 8152, Level 16, State 10, Line 2 字符串或二进制数据将被截断。

标签: sql sql-server-2008 join concatenation partial


【解决方案1】:
;WITH cte AS (
    SELECT a.ID, b.taglabel FROM
        (SELECT c.ID, Split.a.value('.', 'VARCHAR(100)') AS Tags FROM 
            (SELECT ID, CAST('<M>' + REPLACE(Tags,  ',' , '</M><M>') + '</M>' AS XML) AS Tag 
                FROM dbo.Table1) c
        CROSS APPLY Tag.nodes('/M') Split(a)) a
    LEFT JOIN dbo.Table2 b ON b.tagname = a.Tags)

SELECT ID, STUFF(
    (SELECT DISTINCT ',' + NULLIF(taglabel,'') FROM cte t2
        WHERE t1.id = t2.id FOR XML PATH(''), TYPE).value('.','VARCHAR(MAX)'),1,2,'') AS tags
FROM cte t1 GROUP BY ID ORDER BY ID

架构

CREATE TABLE dbo.Table1 (
    ID tinyint PRIMARY KEY,
    Tags varchar(25))

CREATE TABLE dbo.Table2(
    tagname varchar(5) PRIMARY KEY,
    taglabel varchar(15))

INSERT INTO dbo.Table1 VALUES
(1, 'tag1,tag2,tag3,tag6'),
(2, 'tag2,tag4,tag5'),
(3, 'tag3,tag8,tag9,tag10'),
(4, 'tag1,tag2,tag6'),
(5, 'tag1,tag4,tag5')

INSERT INTO dbo.Table2 VALUES
('tag1', 'Nice Name Tag1'),
('tag2', 'Nice Name Tag2'),
('tag3', 'Nice Name Tag3'),
('tag4', 'Nice Name Tag4'),
('tag5', 'Nice Name Tag5'),
('tag6', 'Nice Name Tag6'),
('tag7', 'Nice Name Tag7'),
('tag8', 'Nice Name Tag8'),
('tag9', 'Nice Name Tag9'),
('tag10', 'Nice Name Tag10')

结果

ID 标签
---- ---------------------------------------------- --------------------
1 好名牌1,好名牌2,好名牌3,好名牌6
2 好名牌2,好名牌4,好名牌5
3 好名牌10,好名牌3,好名牌8,好名牌9
4 好名字标签1,好名字标签2,好名字标签6
5 好名牌1,好名牌4,好名牌5

编辑 1

要规范化,请创建另一个这样的表:

CREATE TABLE tags (
    id mediumint,
    tag varchar(5),
    PRIMARY KEY (id, tag))

INSERT INTO tags (id, tag) VALUES
SELECT c.ID, Split.a.value('.', 'VARCHAR(100)') AS Tags FROM 
    (SELECT ID, CAST('<M>' + REPLACE(Tags,  ',' , '</M><M>') + '</M>' AS XML) AS Tag 
        FROM dbo.Table1) c
CROSS APPLY Tag.nodes('/M') Split(a)

然后您可以在每个tagJOIN 您的tags

编辑 2

查看this fiddle 以了解标准化架构的外观。

【讨论】:

  • 我很乐意对此进行规范化,但除非有编程方式,否则我不确定如何合理地做到这一点并将此列表放在一起。我不知道“标签”列中有多少标签。我该怎么做呢?
  • 感谢您询问如何创建一个规范化的新表。我只是在查找它,但它变得有点混乱。我会尝试通过你的例子来工作。我感谢大家的回应。对不起,如果我的反应有点慢。试图消化一切。我是自学成才,知道我所知道的,那就是开始正常化。不过,当我遇到这样的混乱时,我很难过。
  • 好的,我已经把表格标准化了。现在是这样的: ID---tag 1--- Tag1 2--- Tag2 3--- Tag3 4--- Tag4 5--- Tag5 6--- Tag6 7--- Tag7 8--- Tag8 9--- Tag9 10---Tag10 现在,我需要将它链接到一个表格,该表格列出了每个可能的标签和一个标签标签。看着像东西和组之类的想法,但它们有点复杂。
  • @missscripty 查看我的回答中的编辑 2,了解标准化架构和查询如何工作。如果这是可以接受的,请接受我的回答。
  • njk,我不明白这部分:从 cte t2 WHERE t1.id = t2.id 我的两个表需要链接标签名称,而不是 ID,就像它们在您的解决方案的第一部分 b.HitTagName = a.tag - 另外,我是否需要将 cte 更改为表名?对不起,如果我错过了明显的。我一直在研究类似的解决方案,但无济于事。
【解决方案2】:

首先,如果你可以,normalize 表而不是多值列。这是糟糕的数据库设计,很难查询,接下来您将看到。

现在,如果您真的无法修改数据库架构,那么您必须这样做:

  1. 将每个多值列转换为行,即tag1,tag2,tag3转换为三行;
  2. 对于每一行,从Table2 获取其描述;
  3. 最后,连接所有描述,按 id 分组。

这是结果查询和工作sqlfiddle

WITH tmp (id, tag, tags) AS (
SELECT id, LEFT(tags, CHARINDEX(',',tags+',')-1),
    STUFF(tags, 1, CHARINDEX(',',tags+','), '')
FROM tbl1
UNION ALL
SELECT id, LEFT(tags, CHARINDEX(',',tags+',')-1),
    STUFF(tags, 1, CHARINDEX(',',tags+','), '')
FROM tmp
WHERE tag > ''
)
SELECT id, 
 STUFF((SELECT ',' + tbl2.descr FROM tmp INNER JOIN tbl2 ON (tmp.tag = tbl2.tag) 
        WHERE id = a.id
        FOR XML PATH ('')), 1, 1, '') tag_names
FROM tmp a
WHERE tag != ''
GROUP BY id
ORDER BY id;

要规范化您的架构,您需要创建一个中间表,例如id_tag(id, tag),其中(id, tag) 将构成主键。然后,您可以使用上述查询的第一部分(tmp 表)来填充此表。在此之后,您可以删除Table1

【讨论】:

  • 请不要使用隐式连接语法(逗号分隔的FROM 子句)——一段时间以来,它一直被认为是一种反模式。我同意递归查询可能是要走的路(拆分标签),但我不确定您是否需要STUFF() 来控制您已经遇到的标签 - 实际上可以传入各种索引(从CHARINDEX()) 到下一次迭代,这应该有助于提高性能(这是我在缺少STUFF() 的DB2 版本中必须做的)
  • @X-Zero:我同意,这是一种习惯,在我的工作场所编写的许多查询都使用这种语法。我已经编辑了我的答案。我不确定性能优势,我必须对其进行测试,但在生产环境中,我真的会尝试规范化此架构,而不是实际运行此查询。
  • 哦,不,我同意,数据应该被规范化,最好使用标签的数字 id,并且可能使用描述表进行规范化。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-04-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-15
  • 2014-10-17
相关资源
最近更新 更多