Reputation:
I have two tables: Phrase and PhraseCategory
They are linked with Phrase.CategoryId
== PhraseCategory.PhraseCategoryShortId
CREATE TABLE [dbo].[Phrase] (
[PhraseId] UNIQUEIDENTIFIER DEFAULT (newid()) NOT NULL,
[English] NVARCHAR (250) NOT NULL,
[CategoryId] INT NULL,
PRIMARY KEY CLUSTERED ([PhraseId] ASC)
);
CREATE TABLE [dbo].[PhraseCategory] (
[PhraseCategoryShortId] INT IDENTITY (1, 1) NOT NULL ,
[Name] VARCHAR (100) NOT NULL,
PRIMARY KEY CLUSTERED ([PhraseCategoryShortId] ASC)
);
Can someone help give me some advice on how I can join these so that I get a report looking like this:
PhraseCategory.Name Qty
Here's what I have so far:
SELECT PhraseCategory.name, count(*) AS qty
FROM Phrase
LEFT OUTER JOIN PhraseCategory
ON Phrase.CategoryId = PhraseCategory.PhraseCategoryShortId
GROUP BY PhraseCategory.name
ORDER BY PhraseCategory.name
The problem for me is that I want it to show the Phrase Category name and a 0 if there are no rows with that category. So far I cannot get this to work.
Upvotes: 0
Views: 37
Reputation: 38023
If you want to see counts for both phrases that do not have a category, and categories that do not have phrases, you can use a full outer join.
Since Phrase.CategoryId
can be null
, you might want to return a string instead of null
, e.g. 'No Category'.
select
Category = coalesce(pc.name,'No Category')
, qty = count(p.PhraseId)
from Phrase p
full outer join PhraseCategory pc
on p.CategoryId = pc.PhraseCategoryShortId
group by pc.name
order by pc.name;
returns:
+-------------+-----+
| Category | qty |
+-------------+-----+
| No Category | 1 |
| ColumnNames | 3 |
| Functions | 0 |
| KeyWords | 2 |
| Users | 1 |
+-------------+-----+
test setup: http://rextester.com/FUBSY27286
insert into phrase (English, CategoryId) values
('who',null) /* no category */
,('select',1)
,('from',1)
,('PhraseId',2)
,('English',2)
,('CategoryId',2)
,('Anne',3);
insert into PhraseCategory (Name) values
('KeyWords')
,('ColumnNames')
,('Users')
,('Functions') /*no phrases */;
Upvotes: 0
Reputation: 82474
Your join is in the wrong order, and you need to count only the records in Phrase, not both tables:
SELECT PhraseCategory.name, count(Phrase.*) AS qty
FROM PhraseCategory
LEFT OUTER JOIN Phrase
ON Phrase.CategoryId = PhraseCategory.PhraseCategoryShortId
GROUP BY PhraseCategory.name
ORDER BY PhraseCategory.name
Of course, you could change the left join
to a right join
and keep the same order of tables:
SELECT PhraseCategory.name, count(Phrase.*) AS qty
FROM Phrase
RIGHT OUTER JOIN PhraseCategory
ON Phrase.CategoryId = PhraseCategory.PhraseCategoryShortId
GROUP BY PhraseCategory.name
ORDER BY PhraseCategory.name
Upvotes: 1
Reputation: 71
try IFNULL
SELECT PhraseCategory.name, IFNULL(count(*),0) AS qty
FROM Phrase
LEFT OUTER JOIN PhraseCategory
ON Phrase.CategoryId = PhraseCategory.PhraseCategoryShortId
GROUP BY PhraseCategory.name
ORDER BY PhraseCategory.name
Upvotes: 0
Reputation: 1269773
I think you have the LEFT JOIN
backwards:
SELECT pc.name, count(p.CategoryId) AS qty
FROM PhraseCategory pc LEFT JOIN
Phrase p
ON p.CategoryId = pc.PhraseCategoryShortId
GROUP BY pc.name
ORDER BY pc.name;
You seem to want everything in PhraseCategory
, so it should be the first table in the LEFT JOIN
. Also note that the COUNT()
changed, so it counts the matches in the second table (this is how it returns 0
).
Upvotes: 1