简体   繁体   中英

SQL Many to Many relationship

I'm having difficulties writing a SQL query. This is the structure of 3 tables, table Race_ClassificationType is many-to-many table.

Table Race
---------------------------- 
RaceID
Name

Table Race_ClassificationType
----------------------------
Race_ClassificationTypeID
RaceID
RaceClassificationID

Table RaceClassificationType
----------------------------
RaceClassificationTypeID
Name

What I'm trying to do is get the races with certain classifications. The results are returned by a store procedure that has a table-value parameter which holds the desired classifications:

CREATE TYPE [dbo].[RaceClassificationTypeTable]
AS TABLE
(
  RaceClassificationTypeID INT NULL
);
GO

CREATE PROCEDURE USP_GetRaceList
    (@RaceClassificationTypeTable AS [RaceClassificationTypeTable] READONLY,
     @RaceTypeID INT = NULL,
     @IsCompleted BIT = NULL,
     @MinDateTime DATETIME = NULL,
     @MaxDateTime DATETIME = NULL,
     @MaxRaces INT = NULL)
     WITH RECOMPILE
AS
BEGIN
    SET NOCOUNT ON;

    SELECT   DISTINCT
             R.[RaceID]
            ,R.[RaceTypeID]
            ,R.[Name]
            ,R.[Abbreviation]
            ,R.[DateTime]
            ,R.[IsCompleted]
    FROM    [Race] R,[Race_ClassificationType] R_CT, [RaceClassificationType] RCT
    WHERE   (R.[RaceTypeID] = @RaceTypeID OR @RaceTypeID IS NULL)
    AND     (R.[IsCompleted] = @IsCompleted OR @IsCompleted IS NULL)
    AND     (R.[DateTime] >= @MinDateTime OR @MinDateTime IS NULL)
    AND     (R.[DateTime] <= @MaxDateTime OR @MaxDateTime IS NULL)
    AND     (R.RaceID = R_CT.RaceID)
    AND     (R_CT.RaceClassificationTypeID = RCT.RaceClassificationTypeID)
    AND     (RCT.RaceClassificationTypeID IN (SELECT DISTINCT T.RaceClassificationTypeID FROM @RaceClassificationTypeTable T))
    ORDER BY [DateTime] DESC
    OFFSET 0 ROWS FETCH NEXT @MaxRaces ROWS ONLY
END
GO

As it is this stored procedure doesnt work correctly because it returns all races that have at least one classification type ID in the table-value parameter of classification type IDs (because of the IN clause). I want that the store procedure returns only races that have all the classifications supplied in the table-valued parameter.

Example:

RaceClassificationTypeID    RaceID
3   92728
3   92729
8   92729
29  92729
12  92729
2   92729
3   92730
8   92730
8   92731
1   92731

RaceClassificationTypeIDs in RaceClassificationTypeTable parameter: 3 and 8

OUTPUT: all the races with RaceClassificationID 3 and 8 and optionally any other (2, 29, 12)

That means only races 92729 and 92730 should be returned, as it is all the races in the example are returned.

This is an example of a "set-within-sets" subquery. One way to solve this is with aggregation and a having clause. Here is how you get the RaceId s:

select RaceID
from RaceClassification rc
group by RaceID
having sum(case when RaceClassificationTypeId = 3 then 1 else 0 end) > 0 and
       sum(case when RaceClassificationTypeId = 8 then 1 else 0 end) > 0;

Each condition in the having clause is counts how many rows have each type. Only races with each (because of the > 0 ) are kept.

You can get all the race information by using this as a subquery:

select r.*
from Races r join
     (select RaceID
      from RaceClassification rc
      group by RaceID
      having sum(case when RaceClassificationTypeId = 3 then 1 else 0 end) > 0 and
             sum(case when RaceClassificationTypeId = 8 then 1 else 0 end) > 0
     ) rc
     on r.RaceID = rc.RaceId;

Your stored procedure seems to have other conditions. These can also be added in.

I've set up two tables, one stores your result set and the other represents the values in the table valued parameter of your stored procedure. See below.

CREATE TABLE ABC
(
RCTID INT,
RID INT
)
INSERT INTO ABC VALUES (3,92728)
INSERT INTO ABC VALUES (3,92729)
INSERT INTO ABC VALUES (8,92729)
INSERT INTO ABC VALUES (29,92729)
INSERT INTO ABC VALUES (12,92729)
INSERT INTO ABC VALUES (2,92729)
INSERT INTO ABC VALUES (3,92730)
INSERT INTO ABC VALUES (8,92730)
INSERT INTO ABC VALUES (8,92731)
INSERT INTO ABC VALUES (1,92731)
GO
CREATE TABLE TABLEVALUEPARAMETER
(
VID INT
)
INSERT INTO TABLEVALUEPARAMETER VALUES (3)
INSERT INTO TABLEVALUEPARAMETER VALUES (8)
GO
SELECT RID FROM ABC WHERE RCTID IN (SELECT VID FROM TABLEVALUEPARAMETER) GROUP BY
RID HAVING COUNT(RID) = (SELECT COUNT(VID) FROM TABLEVALUEPARAMETER)
GO

If you run this on your machine you'll notice it produces the two IDs that you're after.

Because you have a stored procedure with a lot of columns selected it would be necessary to use a CTE (Common Table Expression). This is because if you were to try to group all the columns in the current select statement you would have to group by all the columns and you would then get duplication.

If the first CTE delivers the result set and then you uses a version of the select above you should be able to produce only the IDs you want.

If you don't know CTE's let me know!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM