You could unpivot the three 'test' columns into a single one, which is essentially what your union is doing; and then pivot that back to the columns you want (assuming you're on 11g or higher); with your sample data in a CTE:
with orders (ID, DT, CLIENT, TEST0, TEST1, TEST2) as (
select 1, date '2017-04-12', 123, 'CBC', 'LIPID', null from dual
union all select 2, date '2017-04-12', 345, 'LIPID', null, null from dual
union all select 3, date '2017-04-13', 123, 'BMP', 'CBC', null from dual
union all select 4, date '2017-04-13', 345, 'TSH', 'LIPID', null from dual
)
select * from (
select client, test
from orders
unpivot (test for num in (test0 as '0', test1 as '1', test2 as '2'))
)
pivot (count(test) as cnt for (test)
in ('CBC' as cbc, 'LIPID' as lipid, 'BMP' as bmp, 'TSH' as tsh));
CLIENT CBC_CNT LIPID_CNT BMP_CNT TSH_CNT
---------- ---------- ---------- ---------- ----------
123 2 1 1 0
345 0 2 0 1
To filter by date, say, change from orders
to another subquery that restircts the rows picked from the table. To join the result to another table, either join within that inner query, or make the whole thing another level of inner query, something like:
select c.client_name, o.cbc_cnt, o.lpid_cnt, o.dmp_cnt, o.tsh_cnt
from (
select * from (
select client, test
from (
select *
from orders
where dt = date '2017-04-12'
)
unpivot (test for num in (test0 as '0', test1 as '1', test2 as '2'))
)
pivot (count(test) as cnt for (test)
in ('CBC' as cbc, 'LIPID' as lipid, 'BMP' as bmp, 'TSH' as tsh))
) o
join your_client_table c on c.client = o.client;
If a single value can only appear in on column at a time in a row - so you couldn't have a row with both test0 and test1 both set to CBC, for instance - then you could more simply do:
select client,
count(case when test0 = 'CBC' or test1 = 'CBC' or test2 = 'CBC' then client end) as cbc,
count(case when test0 = 'LIPID' or test1 = 'LIPID' or test2 = 'LIPID' then client end) as lipid,
count(case when test0 = 'BMP' or test1 = 'BMP' or test2 = 'BMP' then client end) as bmp,
count(case when test0 = 'TSH' or test1 = 'TSH' or test2 = 'TSH' then client end) as tsh
from orders
group by client;
CLIENT CBC LIPID BMP TSH
---------- ---------- ---------- ---------- ----------
123 2 1 1 0
345 0 2 0 1
but it isn't clear that is the case.