独家或在SQL中有多对多的关系

时间:2017-06-29 14:34:02

标签: sql sql-server tsql

我有一个场景,我希望通过使用异或关系类型的多对多关系来查找与另一个表连接的表中的记录列表。鉴于下面的人为举例,我需要一个分配给至少一篇文章的类别列表,但不是所有文章。我可以通过循环遍历所有类别来强制执行此操作,但这种效率非常低。在MS SQL Server上的T-SQL中有一个很好的干净方法吗?

    CREATE TABLE [dbo].[ArticleCategories](
    [ArticleId] [int] NOT NULL,
    [CategoryId] [int] NOT NULL,
 CONSTRAINT [PK_ArticleCategories] PRIMARY KEY CLUSTERED 
(
    [ArticleId] ASC,
    [CategoryId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]


CREATE TABLE [dbo].[Articles](
    [Id] [int] NOT NULL,
    [Title] [nvarchar](100) NOT NULL,
 CONSTRAINT [PK_Articles] PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]


CREATE TABLE [dbo].[Categories](
    [Id] [int] NOT NULL,
    [CategoryName] [nvarchar](100) NULL,
 CONSTRAINT [PK_Categories] PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]


insert into Articles ( Id, Title ) values ( 1, 'Jon Snow')
insert into Articles ( Id, Title ) values ( 2, 'Joffry Baratheon')
insert into Articles ( Id, Title ) values ( 3, 'Cercei Lanister')
insert into Articles ( Id, Title ) values ( 4, 'Sansa Stark')
insert into Articles ( Id, Title ) values ( 5, 'Khal Drogo')
insert into Articles ( Id, Title ) values ( 6, 'Ramsey Bolton')
insert into Articles ( Id, Title ) values ( 7, 'Melisandre')

insert into Categories ( Id, CategoryName ) values ( 1, 'Orange')
insert into Categories ( Id, CategoryName ) values ( 2, 'Blue')
insert into Categories ( Id, CategoryName ) values ( 3, 'Purple')
insert into Categories ( Id, CategoryName ) values ( 4, 'Green')
insert into Categories ( Id, CategoryName ) values ( 5, 'Violet')
insert into Categories ( Id, CategoryName ) values ( 6, 'Yellow')
insert into Categories ( Id, CategoryName ) values ( 7, 'Black')

insert into ArticleCategories (ArticleId, CategoryId) values (1, 1 )
insert into ArticleCategories (ArticleId, CategoryId) values (2, 1 )
insert into ArticleCategories (ArticleId, CategoryId) values (3, 1 )
insert into ArticleCategories (ArticleId, CategoryId) values (4, 1 )
insert into ArticleCategories (ArticleId, CategoryId) values (5, 1 )
insert into ArticleCategories (ArticleId, CategoryId) values (6, 1 )
insert into ArticleCategories (ArticleId, CategoryId) values (7, 1 )
insert into ArticleCategories (ArticleId, CategoryId) values (2, 2 )
insert into ArticleCategories (ArticleId, CategoryId) values (3, 2 )
insert into ArticleCategories (ArticleId, CategoryId) values (5, 3 )
insert into ArticleCategories (ArticleId, CategoryId) values (7, 3 )

在这种情况下,查询不会返回类别' Orange'因为它被分配给所有文章。它会回归' Blue'和'紫色'因为他们被分配到至少一篇文章,但不是全部。其他类别根本不会返回,因为它们根本没有分配。

预期结果将是:

2|Blue
3|Purple 

已更新,包含样本数据和预期输出。

2 个答案:

答案 0 :(得分:2)

可以在没有连接的情况下测试条件。只有结果

中的类别名称才需要连接
zgen load zsh-users/zsh-syntax-highlighting

SELECT AC.CategoryId, C.CategoryName FROM ArticleCategories AC INNER JOIN Categories C ON AC.CategoryId = C.CategoryId GROUP BY AC.CategoryID HAVING Count(*) < (SELECT Count(*) FROM Articles) 仅包含至少一次分配给文章的组的信息,因此不需要额外的条件。

由于ArticleCategories的主键包含两列(ArticleCategoriesArticleId),因此每个类别不得包含重复文章。因此,每个类别的计数等于此类别已分配给的文章数。

请注意,我使用的是HAVING子句,而不是WHERE子句。 WHERE子句在分组之前应用。 HAVING子句在>分组后应用,并且可以引用聚合结果。

答案 1 :(得分:1)

使用您的示例:http://rextester.com/THCJ13143

和使用group by和具有的查询:

SELECT AC.CategoryID, c.categoryName
FROM ArticleCategories AC
LEFT JOIN Categories C
  on C.ID = AC.CategoryID
GROUP BY AC.CategoryID, c.Categoryname
HAVING count(AC.ArticleID) < (SELECT count(*) FROM Articles)

我们得到:

CategoryID  categoryName
 2          Blue
 3          Purple