views:

840

answers:

5

Give the following simple table structure:

Departments
PK - DeptID    DeptName
--------------------------
1              Department 1
2              Department 2
3              Department 3
4              Department 4

Groups
PK - GroupdID    DeptID
--------------------------
1                1
2                1
3                3 
4                4
5                2
6                3
7                1
8                3

Inventory
PK - ItemID    GroupID
--------------------------
1              2
2              3
3              8
4              1
5              4
6              5
7              1
8              2
9              2
10             3
11             7

Is there a way without using subqueries (which is easy) where I could get a listing of the departments, the count of the groups in each department, and the count of the inventory in each department?

Example Output:

DeptID    DeptName          GroupCount      ItemCount
-----------------------------------------------------
1         Department 1      3               6
2         Department 2      1               1
3         Department 1      3               3
4         Department 4      1               1

My gut is telling me it's just a simple matter of getting the GROUP BY statements correct, but so far I'm drawing a blank. If it does require the use of subqueries, this isn't a problem. I just wanted to confirm for future reference.

NOTE: Using SQL Server 2000 for this particular problem

A: 

What quassnoi said.

Arnshea
A: 

Sorry I am not sitting in front of my SQL Server. Here is a first attempt for you. I might not understand you desired result correctly but maybe you can use this as a starting point?

SELECT 
  Department.DeptId,
  Department.DeptName,
  Group.GroupId,
  COUNT (Inventory.GroupId) as TotalItems
FROM
  Department
  INNER JOIN Groups
    On (Department.DeptId = Groups.DeptId)
  INNER JOIN Inventory
    On (Inventory.GroupId = Groups.GroupId)
GROUP BY
  Department.DeptId,
  Department.DeptName
  Group.GroupId,
  Inventory.GroupId
uriDium
+1  A: 

Following is at least one way to get the results.

SELECT d.DeptID, d.DeptName, ISNULL(g.Groups, 0), ISNULL(t.TotalItems, 0)
FROM 
  Departments d
  LEFT OUTER JOIN (
    SELECT d.DeptID, Groups = COUNT(*)
    FROM Departments d
         INNER JOIN Groups g ON g.DeptID = d.DeptID
    GROUP BY d.DeptID
  ) g ON g.DeptID = d.DeptID
  LEFT OUTER JOIN (
    SELECT d.DeptID, TotalItems = COUNT(*)
    FROM Departments d
         INNER JOIN Groups g ON g.DeptID = d.DeptID
         INNER JOIN Inventory i ON i.GroupID = g.GroupID
    GROUP BY d.DeptID
  ) t ON t.DeptID = d.DeptID
Lieven
On the sample data your query will work, but it will select noting for a department that has no groups.
Quassnoi
@Quassnoi: answer adjusted to take your remark into account.
Lieven
+5  A: 
SELECT  d.deptID,
        COUNT(DISTINCT g.GroupID) AS Groups,
        COUNT(DISTINCT i.ItemID) AS Items
FROM    Departments d
LEFT JOIN 
        Groups g
ON      g.deptID = d.deptID
LEFT JOIN
        Items i
ON      i.GroupID = g.GroupID
GROUP BY
        d.deptID

The results produced are:

deptID  Groups  Items
-----   ------  -----
1       3       6 
2       1       1
3       3       3
4       1       1

This will also produce correct 0's for the Departments that have no Groups, or having only Groups without Items.

Quassnoi
Very nice. Next time I'll wait until SQL Express finishes installing before answering.
Arnshea
Cartesian join is almost impossible here. Most probably it will be a HASH JOIN between Groups and Departments, then a HASH JOIN between the resultset and Items. The resultset will have exactly same number of rows as Departments, provided that GroupID and DeptID are primary keys.
Quassnoi
Yeah, my thinking was messed up on this - I was thinking items were joining to Departments, not groups. Doh.
Damien_The_Unbeliever
great answer, but use COUNT(DISTINCT ISNULL(xxxxx,0)) to eliminate the Warning: Null value is eliminated by an aggregate or other SET operation.
KM
Using ISNULL will return 1 instead of 0 for Departments that have no Groups
Quassnoi
@Quassnoi, correct you are! forgot about the COUNT( ) around it!
KM
+1  A: 

Here's my try...

declare @Depatments table
(
DeptID  int
,DeptName  varchar(15)
)

declare @Groups table
(
GroupID  int
,DeptID  int
)

declare @Inventory table
(
ItemID    int
,GroupID  int
)

INSERT INTO @Depatments VALUES (1,'Department 1')
INSERT INTO @Depatments VALUES (2,'Department 2')
INSERT INTO @Depatments VALUES (3,'Department 3')
INSERT INTO @Depatments VALUES (4,'Department 4')

INSERT INTO @Groups VALUES (1,1)
INSERT INTO @Groups VALUES (2,1)
INSERT INTO @Groups VALUES (3,3)
INSERT INTO @Groups VALUES (4,4)
INSERT INTO @Groups VALUES (5,2)
INSERT INTO @Groups VALUES (6,3)
INSERT INTO @Groups VALUES (7,1)
INSERT INTO @Groups VALUES (8,3)

INSERT INTO @Inventory VALUES (1 ,2)
INSERT INTO @Inventory VALUES (2 ,3)
INSERT INTO @Inventory VALUES (3 ,8)
INSERT INTO @Inventory VALUES (4 ,1)
INSERT INTO @Inventory VALUES (5 ,4)
INSERT INTO @Inventory VALUES (6 ,5)
INSERT INTO @Inventory VALUES (7 ,1)
INSERT INTO @Inventory VALUES (8 ,2)
INSERT INTO @Inventory VALUES (9 ,2)
INSERT INTO @Inventory VALUES (10,3)
INSERT INTO @Inventory VALUES (11,7)


--works with derived tables
SELECT
    d.DeptName,dt_g.CountOf AS GroupCount, dt_i.CountOf AS InventotyCount
    FROM @Depatments  d
        LEFT OUTER JOIN (SELECT
                             COUNT(*) AS CountOf,DeptID
                             FROM @Groups
                             GROUP BY DeptID
                        ) dt_g ON d.DeptID=dt_g.DeptID
        LEFT OUTER JOIN (SELECT
                             COUNT(*) AS CountOf,g.DeptID
                             FROM @Groups               g
                                 INNER JOIN @Inventory  i ON g.GroupID=i.GroupID
                             GROUP BY DeptID
                        ) dt_i ON d.DeptID=dt_i.DeptID
KM
Using ISNULL will return 1 instead of 0 for Departments that have no Groups. NULL elimination is intended here.
Quassnoi
I initially tried to do it with SUM(CASE WHEN NULL THEN 0 ELSE 1 END), but it turned out that mere COUNT is simplier, and the warning does not hurt actually :)
Quassnoi
@Quassnoi, correct you are! I forgot about how the COUNT( ) around it would count the 0!
KM