Home > Software engineering >  CASE statement leaving NULL
CASE statement leaving NULL

Time:12-29

I am trying to make it so there are no NULLs in the JobTitle column. When I do it without at the CASE, I get two JobTitle columns (one for males and one for females) and some have NULL. I want to make it so there is just one column listing all the job titles then listing the total number of males/females next to that column in their own columns. (This is using the AdventureWorks db)

USE AdventureWorks2019 
GO 

select count(hre.gender) AS NumberOfFemales, JobTitle
into #FemalesPerJobTitle
from HumanResources.employee as hre                     
group by JobTitle, Gender
having gender = 'F';


SELECT COUNT(HRE.Gender) AS NumberOfMales, JobTitle
INTO #MalesPerJobTitle
FROM HumanResources.Employee AS HRE
GROUP BY JobTitle, Gender
HAVING gender = 'M';

SELECT FPJ.NumberOfFemales AS Females
    , MPJ.NumberOfMales AS Males
    , 
CASE
    WHEN MPJ.JobTitle IS NULL THEN FPJ.JobTitle
END AS JobTitle
FROM #FemalesPerJobTitle AS FPJ
FULL OUTER JOIN #MalesPerJobTitle AS MPJ
ON FPJ.JobTitle = MPJ.JobTitle

CodePudding user response:

SELECT 
  JobTitle,
  SUM(CASE WHEN gender = ‘M’ THEN 1 ELSE 0 END) AS Males,
  SUM(CASE WHEN gender = ‘F’ THEN 1 ELSE 0 END) AS Females
FROM HumanResources.Employee
GROUP BY JobTitle

You could probably put a COALESCE around the SUMs if it is returning any nulls and you want zeros instead

CodePudding user response:

It sounds like you want to produce a result set that

  • list all job titles, with
  • the count of men and women holding each title

If you have a table holding the complete list of job titles (which the database should since job title is/would seem to be a proper entity), I would use that as the source for the job title column.

But, if you don't have that, I'd do something like the following and use a derived table to give the the distinct set of job titles in both source tables:

select coalesce( f.NumberOfFemales , 0 ) Females  ,
       coalesce( m.NumberOfMales   , 0 ) Males    ,
       jt.JobTitle                       JobTitle 
from (       select JobTitle from #FemalesPerJobTitle
       UNION select JobTitle from #MalesPerJobTitle
     )                        jt
left join #FemalesPerJobTitle f  on f.JobTitle = jt.JobTitle
left join #MalesPerJobTitle   m  on m.JobTitle = jt.JobTitle

Another way to go about it (and probably easier for others to understand) would be to do something like this:

select JobTitle        = t.JobTitle,
       NumberOfFemales = sum( t.NumberOfFemales ) ,
       NumberOfMales   = sum( t.NumberOfMales   )
from (   select JobTitle,
                NumberOfFemales = NumberOfFemales,
                NumberOfMales   = 0
         from #FemalesPerJobTitle
       UNION ALL
         select JobTitle,
                NumberOfFemales = 0 ,
                NumberOfMales   = NumberOfMales
         from #MalesPerJobTitle
     ) t
group by t.JobTitle

Here, the derived table uses UNION ALL to not eliminate duplicates because (1) there shouldn't be any, (2) the query will be more efficient, and (3) the group by clause will take care of the roll-up.

CodePudding user response:

Here you go, I used CTE which makes more sense than temp tables here.

WITH Fcount AS 
(
    select count(hre.gender) AS NumberOfFemales, JobTitle
    from HumanResources.employee as hre
    WHERE gender = 'F'                     
    group by JobTitle
), Mcount AS 
(
    SELECT COUNT(HRE.Gender) AS NumberOfMales, JobTitle
    FROM HumanResources.Employee AS HRE
    WHERE gender = 'M'
    GROUP BY JobTitle
), titles CASE
(
   SELECT DISTINCT JobTitle FROM Fcount
   UNION ALL
   SELECT DISTINCT JobTitle FROM MCount
)
SELECT titles.JobTitle,
       FPJ.NumberOfFemales AS Females,
       MPJ.NumberOfMales AS Males
FROM titles 
LEFT JOIN Fcount AS FPJ ON titles.JobTitle = FPJ.JobTitle
LEFT JOIN Mcount AS MPJ ON titles.JobTitle = MPJ.JobTitle
  • Related