【问题标题】:Inner Join between table and a subquery on the same table表和同一表上的子查询之间的内部联接
【发布时间】:2015-04-06 17:49:04
【问题描述】:

SQL 服务器 2012。

编辑: 我的原始查询比应有的更复杂,因为我尝试对表中的字段子集进行 Distinct 查询并将其连接到表本身以获取其他(文本)字段。以下查询也可以解决问题:

SELECT DISTINCT
    p1.id
    ,p1.Name
    ,CAST( p1.[Description] AS nvarchar(max)) AS Description
    ,( SELECT [Category] + ', '
           FROM [dbo].[Company] AS p2
          WHERE p2.Id = p1.Id
          ORDER BY Name
            FOR XML PATH('') ) AS Categories
  FROM [dbo].[Company] AS p1
  ORDER BY p1.Id

我有一个表格,其中包含与此类似的数据(每个公司有多个相同的记录,除了类别字段):

+----+------+-----------------+----------+
| Id | Name | Description     | Category |
+----+------+-----------------+----------+
| 1  | AAA  | <loads of text> | cat1     |
| 1  | AAA  | <loads of text> | cat2     |
| 2  | BBB  | <even more text>| cat1     |
| 2  | BBB  | <even more text>| cat3     |
+----+------+-----------------+----------+

我正在尝试进行查询以获得此结果(每个公司的唯一记录和汇总到 1 个字段中的类别):

| 1  | AAA  | <loads of text> | cat1, cat2 |
| 2  | BBB  | <even more text>| cat1, cat3 |

使用关于 SO 的各种主题的信息,我想出了这个:

SELECT 
    t1.Id
    ,t2.Name
    ,t2.[Description]
    ,t1.Category
FROM  [dbo].[Company] AS t2 
INNER JOIN (SELECT DISTINCT p1.Id
      ,( SELECT [Category] + ', '
           FROM [dbo].[Company] AS p2
          WHERE p2.Id = p1.Id
          ORDER BY Name
            FOR XML PATH('') ) AS Category
  FROM [dbo].[Company] AS p1
  ) AS t1 ON t1.Id = t2.Id
  ORDER BY t1.Id

查询结果对 Company 表中的每条记录都有一条记录,类别聚合到类别字段中:

+----+------+-----------------+------------+
| Id | Name | Description     | Category   |
+----+------+-----------------+------------+
| 1  | AAA  | <loads of text> | cat1, cat2 |
| 1  | AAA  | <loads of text> | cat1, cat2 |
| 2  | BBB  | <even more text>| cat1, cat3 |
| 2  | BBB  | <even more text>| cat1, cat3 |
+----+------+-----------------+------------+

我认为如果两个表都匹配,INNER JOIN 只会选择行。 子查询自己产生预期结果(每个 Id 一条记录,聚合类别)。我在整个查询中尝试了另一个 group by 子句,但失败了,因为我无法在 group 子句中包含 Description 字段,因为它是文本类型字段。

我错过了什么?

【问题讨论】:

  • 如果描述相同,则使用 distinct 并丢弃描述或聚合描述
  • 我需要查询结果中的描述,我无法聚合,我会在描述字段中得到10倍的相同文本。
  • 只需添加 GROUP BY Id、Name、Description。
  • Group by 失败,因为 Description 是一个文本字段,它们不能包含在 Group by 子句中。并且我不能在 SELECT 子句中包含不在 Group by 子句中的字段。
  • @tanner:很遗憾,我无法使用服务器功能(公司政策)

标签: sql-server subquery inner-join


【解决方案1】:
SELECT  *
FROM    ( SELECT    t1.Id ,
                    t2.Name ,
                    t2.[Description] ,
                    t1.Category ,
                    ROW_NUMBER() OVER ( PARTITION BY t1.Id, t2.Name,
                                        t1.Category ORDER BY t1.id ) row_num
          FROM      [dbo].[Company] AS t2
                    INNER JOIN ( SELECT DISTINCT
                                        p1.Id ,
                                        ( SELECT    [Category] + ', '
                                          FROM      [dbo].[Company] AS p2
                                          WHERE     p2.Id = p1.Id
                                          ORDER BY  Name
                                        FOR
                                          XML PATH('')
                                        ) AS Category
                                 FROM   [dbo].[Company] AS p1
                               ) AS t1 ON t1.Id = t2.Id
        ) t1
ORDER BY t1.Id

【讨论】:

  • 已经尝试过:Msg 421, Level 16, State 1, Line 2 文本数据类型不能选择为 DISTINCT,因为它不可比较。
  • 更新查询。它有点难看,但应该可以工作
【解决方案2】:

我会在外部查询中尝试 DISTINCT。这应该可以解决您的问题,除非某些行的描述/名称不同,这可能是完全可能的,因为您的数据库表可能应该是两个表,并且您可能从未编写任何代码来确保每个行的描述/名称保持相同ID。如果你有一个关于 id、name 和 description 的复合唯一索引,那么你可能没问题。

如果您确实有多个描述的问题,您将需要使用聚合来解决外部查询中的问题。或者您需要修复数据并添加唯一索引以防止将来再次发生。

就您遇到此问题的原因而言,连接工作正常,但您拥有的是派生表和另一个表之间的一对多关系。这就是为什么您会获得多条记录以及为什么 distinct 应该修复它,除非 id 的数据对于名称和描述不同。

试试这个:

SELECT DISTINCT
    t1.Id
    ,t2.Name
    ,cast(t2.[Description] as nvarchar(max))
    ,t1.Category
FROM  [dbo].[Company] AS t2 
INNER JOIN (SELECT DISTINCT p1.Id
      ,( SELECT [Category] + ', '
           FROM [dbo].[Company] AS p2
          WHERE p2.Id = p1.Id
          ORDER BY Name
            FOR XML PATH('') ) AS Category
  FROM [dbo].[Company] AS p1
  ) AS t1 ON t1.Id = t2.Id
  ORDER BY t1.Id

或者,您可以修复不良的表格设计。

【讨论】:

  • 已经试过了:Msg 421, Level 16, State 1, Line 2 文本数据类型不能选择为 DISTINCT,因为它不可比较。
  • 糟糕,没有注意到它是文本。它应该是文本吗?您不应该在 SQL Server 2012 中使用文本数据类型。您应该使用 varchar(max) 或 nvarchar(max)。如果可能的话,我会转换为那些,或者您可以转换为查询中的其中一个,看看是否有效。
  • 具有相同 ID 的所有记录的描述字段相同。事实上,该表是组合多个表的更复杂查询的结果。一旦我按照应有的方式工作,我计划将该查询集成到上述查询中。
  • 表格和字段是很久以前制作的(不是我制作的),我无法更改其中任何一个。我可以尝试转换生成 Company 表的查询中的文本字段,但恐怕有些可能会被截断(我们在这里谈论的是 1000 条记录)
  • VARchar(max) 和 nvarchar(max) 不会截断数据。它们可以存储与文本字段一样多的信息。您确实应该重构数据库以使用它们,但我意识到这并不总是可能的。但是,文本是一种已弃用的数据类型,使用它替换字段应该是优先事项。
【解决方案3】:

要解决使用文本作为数据类型的讨厌问题,您可以在拉取该列时强制转换该列。如果可能的话,我会将列永久更改为 varchar(max)。

类似这样的:

SELECT 
    t1.Id
    , t2.Name
    , cast(t2.[Description] as varchar(max)) as Description
    , t1.Category
FROM  [dbo].[Company] AS t2 
INNER JOIN (SELECT DISTINCT p1.Id
      ,( SELECT [Category] + ', '
           FROM [dbo].[Company] AS p2
          WHERE p2.Id = p1.Id
          ORDER BY Name
            FOR XML PATH('') ) AS Category
  FROM [dbo].[Company] AS p1
  ) AS t1 ON t1.Id = t2.Id
  GROUP BY Id
    , Name
    , cast(t2.[Description] as varchar(max))
  ORDER BY t1.Id

【讨论】:

  • 您的回答与 HLGEM 之前的建议重复。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-07-28
  • 2013-07-19
  • 2013-12-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多