根据收集区别

时间:2014-09-02 00:36:20

标签: sql tsql

是否有人知道如何区分子表中的项目?
我不确定是否可能,但也许有人可以澄清一下。

例如我有2个表:A和B(1到多个)。
如果B对应的A行具有相同的项目,我需要加入它们并从A中选择一行。

我需要在SQL服务器端执行此操作,因为我希望使用ROW_NUMBER进行分页。

更新:

CREATE TABLE [dbo].[A] ([ID] int NOT NULL, [Name] varchar(Max) NOT NULL)
CREATE TABLE [dbo].[B] ([ID] int NOT NULL, [A_ID] int NOT NULL, [Name] varchar(Max) NOT NULL)

INSERT INTO A VALUES (1, 'A1')
INSERT INTO A VALUES (2, 'A2')
INSERT INTO B VALUES (1, 1, 'B1')
INSERT INTO B VALUES (2, 1, 'B2')
INSERT INTO B VALUES (3, 2, 'B1')
INSERT INTO B VALUES (4, 2, 'B2')

这应该只返回A1,B1,B2,认为A1和A2等于它们的B1和B2。

请告诉我现在是否清楚。

http://sqlfiddle.com/#!3/897704/1/0

2 个答案:

答案 0 :(得分:3)

这应该可以解决问题。我投入了一些额外的CHECKSUM_AGG魔法,所以如果你有一个非常大的桌子,它仍然会表现得很好。

WITH Grouped AS (
   SELECT
      A_ID,
      GroupID = Checksum_Agg(Checksum(B.Name))
   FROM
      B
   GROUP BY
      B.A_ID
), DistinctA AS (
   SELECT G1.A_ID
   FROM
      Grouped G1
   WHERE
      NOT EXISTS (
         SELECT *
         FROM Grouped G2
         WHERE
            G1.GroupID = G2.GroupID
            AND G1.A_ID > G2.A_ID
            AND NOT EXISTS (
               SELECT *
               FROM
                  (SELECT * FROM B B1 WHERE G1.A_ID = B1.A_ID) B1
                  FULL JOIN (SELECT * FROM B B2 WHERE G2.A_ID = B2.A_ID ) B2
                     ON B1.Name = B2.Name
               WHERE
                    B1.A_ID IS NULL
                    OR B2.A_ID IS NULL
            )
      )
)
SELECT
   A_ID = A.ID,
   AName = A.Name,
   B_ID = B.ID,
   BName = B.Name
FROM
   DistinctA DA
   INNER JOIN A
      ON DA.A_ID = A.ID
   INNER JOIN B
      ON A.ID = B.A_ID
;

<强> See this query live in a SQL Fiddle

请注意,CHECKSUM_AGG本身并不能保证正确性。查询的FULL JOIN部分保证实际正确性,该部分检查Name表中不匹配的任何B(该组中的一个或另一个为NULL) A_ID S)。但是CHECKSUM_AGG充当散列函数,因此FULL JOIN只需要比较极少数可能的重复项,而不是整个A_ID中{1}}的所有其他B组。表

我看到的一些事情:

  • 仅列ID列是不好的。然后你必须在整个地方别名。每个表中的列A_ID应该相同,包括A
  • 我意识到这只是一个例子,但是下划线是专业SQL编写者的祸根。它们输入的密钥与PascalCase一样多,但需要将最弱的手指从主行移开,将两个键移到右侧。 WhateverID远远优于WHATEVER_ID。即使WHATEVERID也可以容忍。
  • 听起来您的B表可以使用另一个表加入,以查找Name值。如果B中具有相同名称的两行意味着相同的东西,那么其中一个拼写错误的机会成为一个非常真实的命题。相反,B可能需要加入另一个具有所有唯一名称并提供ID的表。
  • 您的B表格可能不需要ID列(如本例所示)。在许多情况下,即使要通过密钥访问单个B行,也可以通过A_IDName(或者更好,代表名称的ID)来访问它们。 。当然,我不知道这些表中的数据究竟是什么,并且很多时候代理键是合适的。如果B实际上是一个多对多连接表,即使它有一些额外的列,大多数情况下这些表都有一个代理键。

还有一件事:如果B表中除了Name之外还需要多个列来了解该行是否与另一个A的{​​{1}}相比是重复的行,然后就可以完成了。如果是这种情况,请告诉我。

如果您的数据在现实世界中有意义,那将会很有帮助。暴露一些关于业务对象的东西很难揭示真正私密的东西,当你的示例数据更具体时,它可以帮助那些回答你的人更好地回答。它实际上也可以帮助你更好,更快地理解你得到的答案 - 因为我上面的查询是100%抽象的,所以很难理解。

另一个注意事项:我确信希望SQL Server提供B来补充DISUNIONUNIONINTERSECT。那么这将是我使用EXCEPT的查询部分的另一个选项。

答案 1 :(得分:1)

替代查询。比ErikE短,但在尝试添加其他列时可能会中断。

基本上,内部查询将B.Name的所有值连接成一个字符串(即“B1,B2”等)。如果B的所有部分都相同,那么您可以在外部查询中使用GROUP BY。如上所述,A.Name的选择在这个阶段是任意的,所以我做了MIN来获得第一个。

SELECT MIN(sub1.Name), sub1.conc
FROM (SELECT A.Name, (SELECT [Name]+','
                      FROM [b]
                      WHERE B.A_ID = A.ID
                      FOR XML PATH('')) conc
      FROM [A]) sub1
GROUP BY sub1.conc