Group by when joining the same table twice - sql

I'm writing a query to summarize some data. I have a flag in the table that is basically boolean, so I need some sums and counts based on one value of it, and then the same thing for the other value, like so:
select
location
,count(*)
,sum(duration)
from my.table
where type = 'X'
and location = #location
and date(some_tstamp) = #date
group by location
And then the same for another value of the type column. If I join this table twice, how do I still group so I can only get aggregation for each table, i.e. count(a.*) instead of count(*)...
Would it be better to write two separate queries?
EDIT
Thanks everybody, but that's not what I meant. I need to get a summary where type = 'X' and a summary where type = 'Y' separately...let me post a better example. What I meant was a query like this:
select
a.location
,count(a.*)
,sum(a.duration)
,count(b.*)
,sum(b.duration)
from my.table a, my.table b
where a.type = 'X'
and a.location = #location
and date(a.some_tstamp) = #date
and b.location = #location
and date(b.some_tstamp) = #date
and b.type = 'Y'
group by a.location
What do I need to group by? Also, DB2 doesn't like count(a.*), it's a syntax error.

select
location
,Sum(case when type = 'X' then 1 else 0 end) as xCount
,Sum(case when type = 'Y' then 1 else 0 end) as YCount
,Sum(case when type = 'X' then duration else 0 end) as xCountDuration
,Sum(case when type = 'Y' then duration else 0 end) as YCountDuration
from my.table
where
location = #location
and date(some_tstamp) = #date
group by location
This should work in SQL Server. I guess db2 should have something similar.
Edit: Add a where condition to limit the records to select type = X or type = Y, if "type" can have value other than X and Y.

Your example with the join doesn't make a lot of sense. You're doing a Cartesian product between A and B. Is this really what you want?
The following will find count(*) and sum(duration) for each pair that satisfies the WHERE clause. Based on your description, this sounds like what you're looking for:
select
type
,location
,count(*)
,sum(duration)
from my.table
where type IN ('X', 'Y')
and location = #location
and date(some_tstamp) = #date
group by type, location

To make the counts work, instead of count(a.*) just do count(a.location), or any other not-null column (the PK would be ideal).
As to the main question, either of the answers given by shahkalpesh or George Eadon above would work. There is no reason in this example to join the table twice.

Related

Sum only when they have the same date in SQL Server

I am writing a query in SQL Server where I have to show the sum of the records of a column that have the same date but for now it only adds all the records regardless of date.
How can I make it so that it only adds the repeated dates of the column and not all together?
SELECT
FechaHoraReg,
(SELECT SUM(CantidaIngLamina)
FROM MovimientoMaterial Produccion
WHERE IdTipoMov = '1') AS FilminaI,
(SELECT SUM(CantidaIngresa)
FROM MovimientoMaterial Produccion
WHERE IdTipoMov = '1') AS PapelI
FROM
MovimientoMaterial_Produccion
GROUP BY
FechaHoraReg
The following query will give you a sum of the 2 columns independently
WITH CTE_FILMINAI
AS
(
SELECT
FECHAHORAREG,
SUM(CANTIDAINGLAMINA) AS FILMINAI
FROM MOVIMIENTOMATERIAL_PRODUCCION
WHERE 1=1
AND IDTIPOMOV = '1'
GROUP BY FECHAHORAREG
), CTE_PAPELI
AS
(
SELECT
FECHAHORAREG,
SUM(CANTIDAINGRESA) AS PAPELI
FROM MOVIMIENTOMATERIAL_PRODUCCION
WHERE 1=1
AND IDTIPOMOV = '1'
GROUP BY FECHAHORAREG
)
SELECT
MP.FECHAHORAREG,
CF.FILMINAI,
CP.PAPELI
FROM MOVIMIENTOMATERIAL_PRODUCCION MP
LEFT JOIN CTE_FILMINAI CF ON CF.FECHAHORAREG = MP.FECHAHORAREG
LEFT JOIN CTE_PAPELI CP ON CP.FECHAHORAREG = MP.FECHAHORAREG
This should allow you to independently find a sum of the two columns and change the where however you need it to be. Additionally, it is a left join on the main table in the event that somehow there is FECHAHORAREG in one of the CTEs. This also could be changed depending on what you are needed it for.
Just filter out the values to ignore via a case expression. There's no reason to involve other joins or subqueries:
select FechaHoraReg,
sum(case when IdTipoMov = '1' -- should this actually be a numeric comparison?
then CantidaIngLamina else 0 end) as FilminaI,
sum(case when IdTipoMov = '1' -- should this actually be a numeric comparison?
then CantidaIngresa else 0 end) as PapelI
from MovimientoMaterial_Produccion
group by FechaHoraReg
The subquery needs filter by id and the date, but the date is the same of the origin query.
Try it:
SELECT
origin.FechaHoraReg,
(SELECT SUM(CantidaIngLamina)
FROM MovimientoMaterial_Produccion AS sub
WHERE sub.IdTipoMov = '1'
AND sub.FechaHoraReg = origin.FechaHoraReg) AS FilminaI,
(SELECT SUM(CantidaIngresa)
FROM MovimientoMaterial_Produccion AS subdos
WHERE subdos.IdTipoMov = '1'
AND subdos.FechaHoraReg = origin.FechaHoraReg) AS PapelI
FROM
MovimientoMaterial_Produccion AS origin
GROUP BY
origin.FechaHoraReg

How to create an alias that counts Ws, Ls, and Ds to create a record

I have a table with sports results with a column labeled 'Result' where the values in that column are either W, L, or D. I would like to create an alias column that will quickly count the Ws, Ls, and Ds from the whole table in that columns and display it as 'Count W-Count L-Count D'.
I'm very new to SQL and I haven't figured this specific of a request out, nor can I find the correct search terms in Google to discover a video or forum result for the situation I am looking for.
If you want the values in separate columns, use conditional aggregation:
select sum(case when result = 'W' then 1 else 0 end) as w_cnt,
sum(case when result = 'L' then 1 else 0 end) as l_cnt,
sum(case when result = 'T' then 1 else 0 end) as t_cnt
from t;
Best option go for group by
Select result, count(*) from table
where column IN ('W' , 'L' , 'D' )
group by result

How to take the total sum of all objects within an object?

I am trying to get the total sum of all parts in a container. The way I am doing now, sum(weight), will only grab the first weight of the first part in the container. I want to grab all part weights where the container number is the same. There are many different container numbers in the table. I want the statement to work with different container numbers, and only insert the value in the row of the first occurrence of the container number.
http://s33.postimg.org/3t63t83hr/sumweight.png
Each part has a weight in the above. I want to tally those weights for each container number and sum it up on the first row like shown.
,(case when mu.master_unit_no is null
then c.Gross_weight
when mu.master_unit_no is not null
then sum(c.Gross_weight)+mut.tare_weight
end)
as 'Weight in LBS'
Right now I have this query but it returns just the first part weight + the tare weight. I want to grab the sum of all the parts for the container.
/* I-Dashboards Shipping Report */
/* ROTW 11-21-2015 */
select
p.part_no AS 'Part_Number'
,p.name AS 'Description'
,c.serial_no as 'S#'
,c.quantity AS 'Qty'
,cp.customer_part_No as 'F_NUMBER'
--,cast(mut.length AS varchar) + 'X' + Cast(mut.width as varchar) + 'X' + Cast(mut.Height as varchar) as 'dim MU'
,(CASE when mut.length is null
then 0
else cast(mut.length as int) end) as 'M_LEN'
,(CASE when mut.width is null
then 0
else cast(mut.width as int) end) As 'M_WD'
,(CASE when mut.height is null
then 0
else cast(mut.Height as int) end) AS 'M_HT'
,cast(pct.cube_length AS INT) as 'S_LEN'
,cast(pct.cube_width AS INT) AS 'S_WD'
,cast(pct.cube_height AS INT) AS 'S_HT'
,mut.tare_Weight as 'M_Tare_lbs'
,c.Gross_weight as 'Net_Wt_lbs'
,mu.master_unit_no as 'M Number'
,g.Booking_No as 'Booking_HAWB_Num'
,concat(g.cargo_container_no, '-', g.dock_code) as 'Container_ID'
,g.outbound_scac_code AS 'Carrier'
,concat(cast(pct.cube_length as int), 'x', cast(pct.cube_width as int), 'x', cast(pct.cube_height as int)) as 'BOX_DIMS_INCHES'
,(case when row_number() over (partition by mu.master_unit_no order by mu.master_unit_no) = 1
then concat(cast(mut.length as int), 'x', cast(mut.width as int), 'x', cast(mut.Height as int))
when mu.master_unit_no is null
then ''
end)
as 'PALLET_DIMS_INCHES'
,(case when g.booking_container_type_key = 6 THEN
'DIRECT'
when g.booking_container_type_key = 5 THEN
'AIR'
else 'CEVA-Ocean'
end) as 'Shipment Type'
,CASE
--WHEN(ROW_NUMBER() OVER (PARTITION BY mu.master_unit_no ORDER BY mu.master_unit_no)) = 1
--then (select sum((pct.cube_length*0.0254)*(pct.cube_width*0.0254)* (pct.cube_height*0.0254))
--from part_v_container c where c.master_unit_key = mu.master_unit_key)
when mu.master_unit_no is null
then (pct.cube_length*0.0254)*(pct.cube_width*0.0254)* (pct.cube_height*0.0254)
end as 'CBM'
,select c.*, CASE
WHEN(ROW_NUMBER() OVER (PARTITION BY mu.master_unit_no ORDER BY mu.master_unit_no)) = 1
THEN **(**select SUM(c.Gross_weight)+mut.tare_weight
from part_v_container c where c.master_unit_no = mu.master_unit_no**)** END AS 'Total Weight'
from part_v_container c
I'm trying to take the total sum of all the parts gross weight in a m number + the tare weight for that m number and store is as total weight.
Like Siyual said, add tables to help better our understanding. Until then I believe I have most of what you want.
Your table probably looks something like...
part_id container_ id Weight
------- ------------- ------
1 a 5
2 a 5
3 b 99
4 a 3
5 c 99
And you probably want a result like (example using container_id = a)...
Weight
------
13
Try this...
SELECT SUM(Weight) FROM someTable WHERE container_id = someContainer
In the case of the result example I gave I would do...
SELECT SUM(Weight) FROM someTable WHERE container_id = 'a'
I am not fully sure on what you mean by your last part "only insert the value in the row of the first occurrence of the container number". Why would you want this specifically?
EDIT 1
The final result should not have multiple container_id though. I did the following...
My table...
SELECT t1.container_id, SUM(t1.weight) FROM table_1 t1 JOIN table_1 t2 ON t1.part_id = t2.part_id GROUP BY t1.container_id
Result was...
EDIT 2
It took me a while but I think I got it :)
Table:
Query:
SELECT t.*, CASE
WHEN(ROW_NUMBER() OVER (PARTITION BY t.Container ORDER BY t.Container)) = 1
THEN (SELECT SUM(t2.Weight) FROM table1 t2 WHERE t2.Container = t.Container)
ELSE 0 END AS 'Total Weight'
FROM table1 t GROUP BY t.Container, t.Part, t.Weight
Results:
EDIT 3
This was your original...
select c.*, CASE
WHEN(ROW_NUMBER() OVER (PARTITION BY mu.master_unit_no ORDER BY mu.master_unit_no)) = 1
THEN select SUM(c.Gross_weight)+mut.tare_weight
from part_v_container c where c.master_unit_no = mu.master_unit_no END AS 'Total Weight'
This is what I would change (surrounded by two asterix on both sides EX: ** A **)...
select c.*, CASE
WHEN(ROW_NUMBER() OVER (PARTITION BY mu.master_unit_no ORDER BY mu.master_unit_no)) = 1
THEN **(**select SUM(c.Gross_weight)+mut.tare_weight
from part_v_container c where c.master_unit_no = mu.master_unit_no**)** END AS 'Total Weight'
You need the parenthesis because the code doesn't know where the end belongs to otherwise. The parenthesis allows SQL to know that the end belongs to the case statement. I also am not sure where the mu. and mut. come from. It seems like they belong to a different table that you never reference here?
I am not sure if you added it but after 'Total Weight' you are missing
from someTable group by (all things that are in your select aka things that will be output need to be here...see my previous example for a better understanding)
If you want, on your original question you can post screen shots of exactly what your tables look like (or manually create it) so I can use the names you use accurately and make it more easily understandable by you :)
EDIT 4
/* I-Dashboards Shipping Report */
/* ROTW 11-21-2015 */
select
p.part_no AS 'Part_Number'
,p.name AS 'Description'
,c.serial_no as 'S#'
,c.quantity AS 'Qty'
,cp.customer_part_No as 'F_NUMBER'
--,cast(mut.length AS varchar) + 'X' + Cast(mut.width as varchar) + 'X' + Cast(mut.Height as varchar) as 'dim MU'
,(CASE when mut.length is null
then 0
else cast(mut.length as int) end) as 'M_LEN'
,(CASE when mut.width is null
then 0
else cast(mut.width as int) end) As 'M_WD'
,(CASE when mut.height is null
then 0
else cast(mut.Height as int) end) AS 'M_HT'
,cast(pct.cube_length AS INT) as 'S_LEN'
,cast(pct.cube_width AS INT) AS 'S_WD'
,cast(pct.cube_height AS INT) AS 'S_HT'
,mut.tare_Weight as 'M_Tare_lbs'
,c.Gross_weight as 'Net_Wt_lbs'
,mu.master_unit_no as 'M Number'
,g.Booking_No as 'Booking_HAWB_Num'
,concat(g.cargo_container_no, '-', g.dock_code) as 'Container_ID'
,g.outbound_scac_code AS 'Carrier'
,concat(cast(pct.cube_length as int), 'x', cast(pct.cube_width as int), 'x', cast(pct.cube_height as int)) as 'BOX_DIMS_INCHES'
,(case when row_number() over (partition by mu.master_unit_no order by mu.master_unit_no) = 1
then concat(cast(mut.length as int), 'x', cast(mut.width as int), 'x', cast(mut.Height as int))
when mu.master_unit_no is null
then ''
end)
as 'PALLET_DIMS_INCHES'
,(case when g.booking_container_type_key = 6 THEN
'DIRECT'
when g.booking_container_type_key = 5 THEN
'AIR'
else 'CEVA-Ocean'
end) as 'Shipment Type'
,(case when row_number() over (partition by mu.master_unit_no order by mu.master_unit_no) = 1
then (pct.cube_length*0.0254)*(pct.cube_width*0.0254)*(pct.cube_height*0.0254)
when mu.master_unit_no is null
then (pct.cube_length*0.0254)*(pct.cube_width*0.0254)* (pct.cube_height*0.0254)
end)
as 'CBM'
,CASE
WHEN(ROW_NUMBER() OVER (PARTITION BY mu.master_unit_no ORDER BY mu.master_unit_no)) = 1
THEN (SELECT SUM(c.Gross_weight) + mut.tare_weight
from part_v_container c where c.master_unit_no = mu.master_unit_no) END AS 'Total Weight'
from part_v_container c
So this should have fixed my part. I do have an extra comment though. You have all these different prefixs (p., c., mut., mu., g., pct.). Where do you reference all of these? I can see where you reference c (it is right after the final from). Even in my part you use mut. but I don't know how you reference it. For example, c is useable because of from part_v_container c. c represents part_v_container. You can look into joins to help you get the other tables in there. If you want you can edit your original question and add all your tables to it (whether they are actual or examples). I just need to know the different table names and column names. I don't care about the actual data. I wish I personally knew you because this would be much easier in real time xD
EDIT 5
Using this table...
I used this query...
;WITH mult AS (SELECT (m.length*0.0254)*(m.width*0.0254)*(m.height*0.0254) AS multiply, m.container FROM measurement m)
, sumMult AS (SELECT SUM((m.length*0.0254)*(m.width*0.0254)*(m.height*0.0254)) AS sumMultiply, m.container FROM measurement m GROUP BY m.container)
, combine AS (SELECT s.sumMultiply AS sumMultiply, m.multiply AS multiply, m.container FROM mult m JOIN sumMult s ON m.container = s.container)
SELECT c.container, CASE WHEN (ROW_NUMBER() OVER (PARTITION BY c.container ORDER BY c.container)) = 1
THEN (SELECT c.sumMultiply)
ELSE (SELECT c.multiply)
END AS 'Cubic Meters'
FROM combine c GROUP BY c.container, c.sumMultiply, c.multiply
It SUMS all of the volumes for all parts in a container and displays it only in the first row (first part). The rest of the parts have their volume.
I can't completely convert it for you. I trust, since you have done it successfully in my previous queries, that you can convert it properly. I tried to keep the names for the table and columns as bland and recognizable as much as I could. It appears to be working how you want it. Incase you don't know what the ;WITH mult.... is...you can think of it like a function. Put the entire with statement (that is, mult, sumMult, combine) before your gigantic query. You can see in my query that my ;WITH is comes first (above) my SELECT query that produces the actual results.

SQL using CASE in SELECT with GROUP BY. Need CASE-value but get row-value

so basicially there is 1 question and 1 problem:
1. question - when I have like 100 columns in a table(and no key or uindex is set) and I want to join or subselect that table with itself, do I really have to write out every column name?
2. problem - the example below shows the 1. question and my actual SQL-statement problem
Example:
A.FIELD1,
(SELECT CASE WHEN B.FIELD2 = 1 THEN B.FIELD3 ELSE null FROM TABLE B WHERE A.* = B.*) AS CASEFIELD1
(SELECT CASE WHEN B.FIELD2 = 2 THEN B.FIELD4 ELSE null FROM TABLE B WHERE A.* = B.*) AS CASEFIELD2
FROM TABLE A
GROUP BY A.FIELD1
The story is: if I don't put the CASE into its own select statement then I have to put the actual rowname into the GROUP BY and the GROUP BY doesn't group the NULL-value from the CASE but the actual value from the row. And because of that I would have to either join or subselect with all columns, since there is no key and no uindex, or somehow find another solution.
DBServer is DB2.
So now to describing it just with words and no SQL:
I have "order items" which can be divided into "ZD" and "EK" (1 = ZD, 2 = EK) and can be grouped by "distributor". Even though "order items" can have one of two different "departements"(ZD, EK), the fields/rows for "ZD" and "EK" are always both filled. I need the grouping to consider the "departement" and only if the designated "departement" (ZD or EK) is changing, then I want a new group to be created.
SELECT
(CASE WHEN TABLE.DEPARTEMENT = 1 THEN TABLE.ZD ELSE null END) AS ZD,
(CASE WHEN TABLE.DEPARTEMENT = 2 THEN TABLE.EK ELSE null END) AS EK,
TABLE.DISTRIBUTOR,
sum(TABLE.SOMETHING) AS SOMETHING,
FROM TABLE
GROUP BY
ZD
EK
TABLE.DISTRIBUTOR
TABLE.DEPARTEMENT
This here worked in the SELECT and ZD, EK in the GROUP BY. Only problem was, even if EK was not the designated DEPARTEMENT, it still opened a new group if it changed, because he was using the real EK value and not the NULL from the CASE, as I was already explaining up top.
And here ladies and gentleman is the solution to the problem:
SELECT
(CASE WHEN TABLE.DEPARTEMENT = 1 THEN TABLE.ZD ELSE null END) AS ZD,
(CASE WHEN TABLE.DEPARTEMENT = 2 THEN TABLE.EK ELSE null END) AS EK,
TABLE.DISTRIBUTOR,
sum(TABLE.SOMETHING) AS SOMETHING,
FROM TABLE
GROUP BY
(CASE WHEN TABLE.DEPARTEMENT = 1 THEN TABLE.ZD ELSE null END),
(CASE WHEN TABLE.DEPARTEMENT = 2 THEN TABLE.EK ELSE null END),
TABLE.DISTRIBUTOR,
TABLE.DEPARTEMENT
#t-clausen.dk: Thank you!
#others: ...
Actually there is a wildcard equality test.
I am not sure why you would group by field1, that would seem impossible in your example. I tried to fit it into your question:
SELECT FIELD1,
CASE WHEN FIELD2 = 1 THEN FIELD3 END AS CASEFIELD1,
CASE WHEN FIELD2 = 2 THEN FIELD4 END AS CASEFIELD2
FROM
(
SELECT * FROM A
INTERSECT
SELECT * FROM B
) C
UNION -- results in a distinct
SELECT
A.FIELD1,
null,
null
FROM
(
SELECT * FROM A
EXCEPT
SELECT * FROM B
) C
This will fail for datatypes that are not comparable
No, there's no wildcard equality test. You'd have to list every field you want tested individually. If you don't want to test each individual field, you could use a hack such as concatenating all the fields, e.g.
WHERE (a.foo + a.bar + a.baz) = (b.foo + b.bar + b.az)
but either way, you're listing all of the fields.
I might tend to solve it something like this
WITH q as
(SELECT
Department
, (CASE WHEN DEPARTEMENT = 1 THEN ZD
WHEN DEPARTEMENT = 2 THEN EK
ELSE null
END) AS GRP
, DISTRIBUTOR
, SOMETHING
FROM mytable
)
SELECT
Department
, Grp
, Distributor
, sum(SOMETHING) AS SumTHING
FROM q
GROUP BY
DEPARTEMENT
, GRP
, DISTRIBUTOR
If you need to find all rows in TableA that match in TableB, how about INTERSECT or INTERSECT DISTINCT?
select * from A
INTERSECT DISTINCT
select * from B
However, if you only want rows from A where the entire row matches the values in a row from B, then why does your sample code take some values from A and others from B? If the row matches on all columns, then that would seem pointless. (Perhaps your question could be explained a bit more fully?)

SQL Nested Select statements with COUNT()

I'll try to describe as best I can, but it's hard for me to wrap my whole head around this problem let alone describe it....
I am trying to select multiple results in one query to display the current status of a database. I have the first column as one type of record, and the second column as a sub-category of the first column. The subcategory is then linked to more records underneath that, distinguished by status, forming several more columns. I need to display every main-category/subcategory combination, and then the count of how many of each sub-status there are beneath that subcategory in the subsequent columns. I've got it so that I can display the unique combinations, but I'm not sure how to nest the select statements so that I can select the count of a completely different table from the main query. My problem lies in that to display the main category and sub category, I can pull from one table, but I need to count from a different table. Any ideas on the matter would be greatly appreciated
Here's what I have. The count statements would be replaced with the count of each status:
SELECT wave_num "WAVE NUMBER",
int_tasktype "INT / TaskType",
COUNT (1) total,
COUNT (1) "LOCKED/DISABLED",
COUNT (1) released,
COUNT (1) "PARTIALLY ASSEMBLED",
COUNT (1) assembled
FROM (SELECT DISTINCT
(t.invn_need_type || ' / ' || s.code_desc) int_tasktype,
t.task_genrtn_ref_nbr wave_num
FROM sys_code s, task_hdr t
WHERE t.task_genrtn_ref_nbr IN
(SELECT ship_wave_nbr
FROM ship_wave_parm
WHERE TRUNC (create_date_time) LIKE SYSDATE - 7)
AND s.code_type = '590'
AND s.rec_type = 'S'
AND s.code_id = t.task_type),
ship_wave_parm swp
GROUP BY wave_num, int_tasktype
ORDER BY wave_num
Image here: http://i.imgur.com/JX334.png
Guessing a bit,both regarding your problem and Oracle (which I've - unfortunately - never used), hopefully it will give you some ideas. Sorry for completely messing up the way you write SQL, SELECT ... FROM (SELECT ... WHERE ... IN (SELECT ...)) simply confuses me, so I have to restructure:
with tmp(int_tasktype, wave_num) as
(select distinct (t.invn_need_type || ' / ' || s.code_desc), t.task_genrtn_ref_nbr
from sys_code s
join task_hdr t
on s.code_id = t.task_type
where s.code_type = '590'
and s.rec_type = 'S'
and exists(select 1 from ship_wave_parm p
where t.task_genrtn_ref_nbr = p.ship_wave_nbr
and trunc(p.create_date_time) = sysdate - 7))
select t.wave_num "WAVE NUMBER", t.int_tasktype "INT / TaskType",
count(*) TOTAL,
sum(case when sst.sub_status = 'LOCKED' then 1 end) "LOCKED/DISABLED",
sum(case when sst.sub_status = 'RELEASED' then 1 end) RELEASED,
sum(case when sst.sub_status = 'PARTIAL' then 1 end) "PARTIALLY ASSEMBLED",
sum(case when sst.sub_status = 'ASSEMBLED' then 1 end) ASSEMBLED
from tmp t
join sub_status_table sst
on t.wave_num = sst.wave_num
group by t.wave_num, t.int_tasktype
order by t.wave_num
As you notice, I don't know anything about the table with the substatuses.
You can use inner join, grouping and count to get your result:
suppose tables are as follow :
cat (1)--->(n) subcat (1)----->(n) subcat_detail.
so the query would be :
select cat.title cat_title ,subcat.title subcat_title ,count(*) as cnt from
cat inner join sub_cat on cat.id=subcat.cat_id
inner join subcat_detail on subcat.ID=am.subcat_detail_id
group by cat.title,subcat.title
Generally when you need different counts, you need to use the CASE statment.
select count(*) as total
, case when field1 = "test' then 1 else 0 end as testcount
, case when field2 = 'yes' then 1 else 0 endas field2count
FROM table1