Вопрос

I have 3 tables:

  1. StuddentMaster
  2. CourseMster
  3. Enroll_Master

The data is about students enrolling for courses at an institute.

I need a query which shows the names of courses where every enrolled student is taking the class.

CREATE TABLE [dbo].[StudentMaster]
(
    [sid] [tinyint] NOT NULL,
    [NAME] [varchar](40) NOT NULL,
    [ORIGIN] [char](1) NULL,
    [TYPE] [char](2) NULL
);

CREATE TABLE [dbo].[CourseMaster]
(
    [CID] [int] NOT NULL,
    [NAME] [varchar](20) NOT NULL,
    [CATEGORY] [char](1) NULL,
    [FEE] [smallmoney] NOT NULL
);

CREATE TABLE [dbo].[ENROLL_MASTER]
(
    [CID] [int] NULL,
    [SID] [tinyint] NULL,
    [DOENROOL] [datetime] NOT NULL,
    [FREEWAIVERFIAG] [bit] NOT NULL,
    [GRADE] [char](1) NULL
);

Example data:

INSERT INTO dbo.StudentMaster ([sid], [name], ORIGIN, [TYPE])
VALUES (1, 'SIVA', 'L', 'UG')  
     , (2, 'SHARIFF', 'F', 'UG')  
     , (3, 'PRAVEEN', 'L', 'G')
     , (4, 'VENKEY', 'L', 'UG')
     , (5, 'BASHAR', 'F', 'G')  
     , (6, 'PRAKASH', 'F', 'G')
     , (7, 'DURGA', 'L', 'G')
     , (8, 'LAKSHMI', 'L', 'G')
     , (9, 'MANI', 'F', 'UG')
     , (10, 'MANIKA', 'F', 'UG')
     , (11, 'MOHHAN', 'F', 'UG')
     , (12, 'MUTHU', 'F', 'UG')
     , (13, 'TOOD', 'F', 'UG')
     , (15, 'sam', 'l', 'g')
     , (16, 'rahul', 'l', 'ug');

INSERT INTO dbo.CourseMaster (CID, 'NAME', 'CATEGORY', FEE)
VALUES (101, 'MSBI', 'B', 10000.00)
     , (102, 'TESTING TOOLS', 'A', 5500.00)
     , (103, 'ORACLE', 'M', 5000.00)
     , (104, 'SQL SERVER', 'B', 2000.00)
     , (105, 'JAVA', 'A', 6000.00)
     , (106, 'SQL SERVER', 'A', 6000.00)
     , (107, 'COGNOS', 'A', 5500.00)
     , (108, 'SQL SERVER', 'M', 4000.00)
     , (109, 'SHAREPOINT', 'M', 12000.00)
     , (110, 'TESTING TOOLS', 'B', 3000.00)
     , (111, 'MSBI', 'A', 12022.00)
     , (112, 'MSBI', 'M', 11000.00)
     , (113, 'TESTING TOOLS', 'M', 4000.00)
     , (114, 'SALESFORCE', 'A', 12022.00);

INSERT INTO dbo.ENROLL_MASTER (CID, SID, DOENROOL, FREEWAIVERFIAG, GRADE)
VALUES (107, 5, '2017-06-01 00:00:00.000', 0, 'A')
     , (104, 3, '2011-11-04 00:00:00.000', 1, 'C')
     , (103, 6, '2017-06-01 00:00:00.000', 0, 'B')
     , (105, 2, '2006-05-02 00:00:00.000', 1, 'B')
     , (102, 7, '2012-04-03 00:00:00.000', 1, 'C')
     , (101, 4, '2017-05-02 00:00:00.000', 0, 'A')
     , (109, 8, '2012-03-24 00:00:00.000', 1, 'C')
     , (108, 7, '2010-02-19 00:00:00.000', 0, 'C')
     , (106, 9, '2011-08-04 00:00:00.000', 1, 'B')
     , (105, 1, '2012-01-21 00:00:00.000', 1, 'A')
     , (104, 9, '2010-12-30 00:00:00.000', 0, 'B')
     , (102, 7, '2010-09-01 00:00:00.000', 1, 'C')
     , (109, 3, '2012-03-27 00:00:00.000', 1, 'A')
     , (101, 4, '2017-05-02 00:00:00.000', 0, 'B')
     , (112, 12, '2017-04-02 00:00:00.000', 0, 'A')
     , (113, 10, '2017-04-02 00:00:00.000', 0, 'A')
     , (106, 1, '2017-04-02 00:00:00.000', 0, 'C')
     , (101, 1, '2017-05-02 00:00:00.000', 0, 'B')
     , (104, 1, '2012-05-22 00:00:00.000', 0, 'B')
     , (106, 1, '2012-05-22 00:00:00.000', 0, 'B')
     , (101, 2, '2018-05-02 00:00:00.000', 0, 'a')
     , (101, 3, '2017-05-02 00:00:00.000', 0, 'B')
     , (101, 5, '2017-06-01 00:00:00.000', 0, 'c')
     , (101, 6, '2017-06-01 00:00:00.000', 0, 'a')
     , (101, 7, '2017-05-02 00:00:00.000', 0, 'B')
     , (101, 8, '2017-05-02 00:00:00.000', 0, 'c')
     , (101, 9, '2017-05-02 00:00:00.000', 0, 'B')
     , (101, 10, '2017-05-02 00:00:00.000', 0, 'B')
     , (101, 11, '2017-05-02 00:00:00.000', 0, 'B')
     , (101, 12, '2017-05-02 00:00:00.000', 0, 'B')
     , (101, 14, '2017-06-01 00:00:00.000', 0, 'B')
     , (113, 11, '2017-04-02 00:00:00.000', 0, 'A')
     , (113, 12, '2017-04-02 00:00:00.000', 0, 'A')
     , (104, 14, '2017-06-01 00:00:00.000', 0, 'A')
     , (104, 6, '2017-06-01 00:00:00.000', 0, 'A')
     , (104, 5, '2017-06-01 00:00:00.000', 0, 'A')
     , (104, 3, '2012-05-22 00:00:00.000', 0, 'B')
     , (101, 3, '2017-05-02 00:00:00.000', 0, 'B')
     , (106, 3, '2012-05-22 00:00:00.000', 0, 'B')
     , (101, 9, '2017-05-02 00:00:00.000', 0, 'c')
     , (104, 10, '2012-05-22 00:00:00.000', 0, 'c')
     , (109, 3, '2012-03-27 00:00:00.000', 1, 'O')
     , (104, 3, '2012-03-27 00:00:00.000', 0, 'O')
     , (101, 15, '2017-05-02 00:00:00.000', 0, 'a')
     , (104, 16, '2011-10-05 00:00:00.000', 1, 'c')
     , (108, 15, '2006-05-05 00:00:00.000', 1, 'b')
     , (105, 13, '2017-04-27 15:07:58.820', 0, 'A')
     , (110, 4, '2017-05-08 12:10:31.227', 0, 'C')
     , (101, 13, '2017-05-08 12:52:19.530', 1, 'C')
     , (101, 16, '2017-05-08 12:52:38.703', 0, 'b');

These are my efforts to solve the query so far:

SELECT C.NAME, COUNT(distinct S.SID) 
  FROM CourseMaster AS C 
  JOIN ENROLL_MASTER AS E 
    ON E.CID = C.CID 
  JOIN StudentMaster AS S 
    ON E.SID = S.sid 
 GROUP BY C.NAME 
HAVING COUNT(DISTINCT S.SID) = (SELECT COUNT(NAME) 
                                FROM StudentMaster)

and

WITH B AS (SELECT COUNT(CID) CNT, CID 
           FROM ENROLL_MASTER 
           GROUP BY CID HAVING COUNT(SID) = ( SELECT COUNT (SID) 
                                              FROM STUDENTMASTER) ) 
SELECT C.NAME 
  FROM COURSEMASTER C 
  JOIN B
    ON C.CID = B.CID 
Это было полезно?

Решение

First, you need to obtain the count of the rows in StudentMaster. This statement will do that:

declare @studentCount int = (select count(*) from StudentMaster);

Then, you use a WHERE clause to filter the ClassMaster rows that have the same number of rows for each class. Use GROUP BY [cid] to aggregate the output into a single row per cid. Use the HAVING clause to limit the output to only those cid rows that have the required rows:

select [CID] 
from classMaster 
group by [CID]  
having count(*) = @studentCount;

For reference, look at the Microsoft Docs pages for GROUP BY and HAVING.

Другие советы

The COUNT technique that was suggested here should work, but the problem is that your table structure and data mess it up.

You did not define any keys, and you have duplicates in the demo data - for example, course 101 enrollment by student 4 appears twice, on the same date, with different grades. Proper declaration of keys would have not allowed it. Changing the predicate to COUNT (SID) >= (SELECT COUNT(SID)...) would also give you the result that you were looking for, taking into account that there may be multiple registrations.

The generalized problem you are facing is called 'relational division'. Search here and you will find many discussions on the topic.

Typically, the most efficient way to solve relational division problems is to use a negative formulation of the question. It seems weird at first, but once you get the hang of it, you can't go back... In your case the question is:

"Show me the name of the courses, for which there doesn't exist a single student, who is not enrolled in it".

This wording lends itself to a direct translation to SQL:

SELECT  [Name]
FROM    [CourseMaster] AS [CM]
WHERE   NOT EXISTS  (
                        SELECT  NULL
                        FROM    [StudentMaster] AS [SM]
                        WHERE   NOT EXISTS  (
                                                SELECT  NULL
                                                FROM    [Enroll_Master] AS [EM]
                                                WHERE   [EM].[CID] = [CM].[CID]
                                                        AND
                                                        [SM].[SID] = [EM].[SID]
                                            )
                    );

An added bonus, is that it also works around the COUNT issue you experienced earlier. Check the execution plans with large data sets, and you will see that this one is easy to index, and will typically perform better than the COUNT technique.

The reason is that EXISTS doesn't need to complete the operation - once a row is found, the predicate is TRUE and there is no need to keep processing. With the COUNT, all aggregates have to be calculated.

Hope that helps~!

Лицензировано под: CC-BY-SA с атрибуция
Не связан с dba.stackexchange
scroll top