选择每个参数的剩余行

时间:2013-05-10 10:00:10

标签: sql sql-server

我有一个SQL Server数据库,我需要从两个视图中选择剩余的行。

背后的想法是这样的:我将游戏分区存储在一个表中,将部族存储在另一个表中。

  • vwGetGameDivisions获得所有可以进行分组的游戏。
  • vwGetClanDivisions获得该部族订阅的所有当前游戏分区。

目前,使用

SELECT dbo.vwGetGameDivisions.name, dbo.vwGetClanDivisions.clanName
FROM   dbo.vwGetClanDivisions 
RIGHT OUTER JOIN
       dbo.vwGetGameDivisions
    ON dbo.vwGetClanDivisions.gameName = dbo.vwGetGameDivisions.name

获取所有'divisions'和'clans'注册到它们。

我想显示剩余的分区(某个战队未注册的其余分区),所以我可以将它绑定到dropDownList为一个战队看看他们是什么仍然可以订阅。

我仍然是SQL查询的新手,甚至不知道如何解决这个问题。

我尝试了WHERE (dbo.vwGetClanDivisions.clanName = NULL),但这只会返回没有战队的分区。

编辑 - 结构:

CREATE TABLE [dbo].[tblSiteClanGameDivision](
    [id] [int] IDENTITY(1,1) NOT NULL,
    [clanId] [int] NOT NULL,
    [gameId] [int] NOT NULL,
    [removed] [tinyint] NOT NULL,
    [dateAdded] [datetime] NOT NULL,
 CONSTRAINT [PK_tblSiteClanGameDivision] PRIMARY KEY CLUSTERED 

CREATE TABLE [dbo].[tblSiteGame](
    [id] [int] IDENTITY(1,1) NOT NULL,
    [name] [nvarchar](200) NOT NULL,
    [description] [nvarchar](700) NULL,
    [thumbnailLocation] [nvarchar](100) NULL,
    [genreId] [int] NOT NULL,
    [isDivision] [tinyint] NOT NULL,
 CONSTRAINT [PK__tblGame__3213E83F03317E3D] PRIMARY KEY CLUSTERED 

CREATE VIEW [dbo].[vwGetClanDivisions]
AS
SELECT dbo.tblSiteClanDetail.clanId, dbo.tblSiteClanDetail.clanName, dbo.tblSiteGame.id AS gameId, dbo.tblSiteGame.name AS gameName, 
                         dbo.tblSiteClanGameDivision.removed, dbo.tblSiteClanGameDivision.dateAdded
FROM   dbo.tblSiteClanDetail 
INNER JOIN  dbo.tblSiteClanGameDivision ON dbo.tblSiteClanDetail.id = dbo.tblSiteClanGameDivision.clanId 
INNER JOIN dbo.tblSiteGame ON dbo.tblSiteClanGameDivision.gameId = dbo.tblSiteGame.id

GO

CREATE VIEW [dbo].[vwGetGameDivisions]
AS
SELECT  id, name, thumbnailLocation, isDivision
FROM    dbo.tblSiteGame
WHERE   (isDivision = 1)

GO

2 个答案:

答案 0 :(得分:2)

最好使用ID字段而不是名称字段进行匹配。

SELECT dbo.vwGetGameDivisions.name
FROM dbo.vwGetGameDivisions
WHERE dbo.vwGetGameDivisions.name NOT IN (
    SELECT dbo.vwGetGameDivisions.name
    FROM   dbo.vwGetClanDivisions 
        INNER JOIN dbo.vwGetGameDivisions ON dbo.vwGetClanDivisions.gameName = dbo.vwGetGameDivisions.name

        )   

答案 1 :(得分:2)

关键是使用INNER JOIN来获取部族注册的分区,并使用LEFT JOIN来获取不属于的部门。即使部落未与他们签约,LEFT JOIN也将返回分部,然后您过滤掉他们所在的部分,如下所示:

-- Get matching divisions
SELECT gd.ID, gd.name, cd.clanName
FROM dbo.vwGetClanDivisions cd
INNER JOIN dbo.vwGetGameDivisions gd ON cd.gameId = gd.id

-- Get remaining divisions
SELECT gd.ID, gd.name, cd.clanName
FROM dbo.vwGetClanDivisions cd
LEFT JOIN dbo.vwGetGameDivisions gd ON cd.gameId = gd.id
WHERE gd.gameName IS NULL

请注意,第二个查询使用LEFT JOIN来获取连接两侧的所有行,而WHERE子句仅显示该族没有匹配游戏分区的行。您还可以使用表名别名(如上所述)来缩短查询。

修改 我应该使用RIGHT OUTER JOIN,但在测试中甚至没有用。但是,@ Vincent James使用NOT IN给出的方法确实如此(需要注意的是,你必须指定你要搜索的是哪个氏族,否则你只会获得一个空的分区列表)。这是一个非常基本的SQL示例:

CREATE TABLE #A (ID int, bID int)
CREATE TABLE #B (ID int)

INSERT INTO #B (ID) SELECT 1
INSERT INTO #B (ID) SELECT 2
INSERT INTO #B (ID) SELECT 3
INSERT INTO #B (ID) SELECT 4
INSERT INTO #B (ID) SELECT 5

INSERT INTO #A (ID, bID) SELECT 1,1
INSERT INTO #A (ID, bID) SELECT 1,2
INSERT INTO #A (ID, bID) SELECT 1,3
INSERT INTO #A (ID, bID) SELECT 2,1
INSERT INTO #A (ID, bID) SELECT 2,2
INSERT INTO #A (ID, bID) SELECT 3,3
INSERT INTO #A (ID, bID) SELECT 3,4

SELECT DISTINCT #B.ID 
FROM #B 
WHERE ID NOT IN(SELECT bID FROM #A WHERE ID = 1)

DROP TABLE #A
DROP TABLE #B

根据这一点,ID为1的#A的条目链接到#B,ID为1,2或3(不是4或5)。运行上面的查询显示NOT IN方法返回4和5,这是正确的。我不完全确定为什么RIGHT OUTER JOIN不起作用,因为这样做的目的是从#B返回每一行,无论它是否在#A中找到。

编辑2: 删除了右外连接方法,因为我有两个冲突的WHERE子句会阻止返回任何行。使用具有给定表结构的外连接是不可能的,因为您需要通过左侧的ID进行选择,但只能选择左侧为空的位置。 NOT IN是正确的方法,如上所示。

编辑3 这是您需要创建的存储过程。这比对查询进行硬编码更好,因为它可以保护您免受SQL injection攻击。

CREATE PROCEDURE [dbo].[GetEmptyClanDivisions]
(
    @ClanID INT
)
AS
BEGIN
    -- Get the clan name
    DECLARE @ClanName nvarchar(255)
    SET @ClanName = SELECT TOP 1 clanName FROM tblSiteClanDetail WHERE clanId = @ClanID 

    -- Get a list of divisions the clan is registered for
    SELECT DISTINCT ID, name, @ClanName 
    FROM vwGetGameDivisions
    WHERE ID NOT IN(
        SELECT gameId 
        FROM tblSiteClanGameDivision 
        WHERE clanId = @ClanID
    )
END