How about smashing them by CTE?
with
TableA as
(Select
Count (*) as TotalA,
z.zname
From CTable c, STable s, SLTable sl, ZTable z
Where c.ID=s.sID
and s.sID=sl.ID
and sl.zID=z.zID
Group by z.zname),
TableB as
(Select
Count (*) as TotalB,
z.zname
From STable s, SLTable sl, ZTable z
Where s.sID=sl.sID
and sl.zID=z.zID
Group By z.zname)
/* --I've tested with this sample to avoid filling CTable,STable,SLTable,ZTable:
create table TableA(zname varchar(100),TotalA int)
create table TableB(zname varchar(100),TotalB int)
insert TableA(zname,TotalA) select 'a',10 union all select 'b',null
insert TableB(zname,TotalB) select 'a',20 union all select 'c',10 union all select 'd',50
select * from TableA
select * from TableB
*/
select COALESCE(TableA.zname,TableB.zname) as zname,TableA.TotalA,TableB.TotalB
from TableA full join TableB on TableA.zname=TableB.zname
if your SQL Server is quite old to use CTEs, just create temp tables and operate with them. Like this one:
Select
Count (*) as TotalA,
z.zname
into #TableA
From CTable c, STable s, SLTable sl, ZTable z
Where c.ID=s.sID
and s.sID=sl.ID
and sl.zID=z.zID
Group by z.zname