I have a current query which returns all the values from the parameters queried.
However it returns older values when i only want the latest line for each to show, I have had a go at using max() and DISTINCT but i cannot get the output i want
Current Code
SELECT
IME.TP AS [Time],
IU.FS07 AS [LID],
IU.I AS [LNum],
IME.TPL AS [Location],
(SELECT CASE
WHEN IME.TPL = 'Y'
THEN 'GTG'
ELSE
(SELECT CASE
WHEN IME.TPL = 'V'
THEN 'WAIT'
ELSE 'WAIT'
END)
END) AS [Go To]
FROM
IU
INNER JOIN IUFV ON IU.G = IUFV.UG
INNER JOIN IME ON IUFV.G = IME.UFVG
Current Output
________________________________________________________________
|Time LID LNum Location Go To|
|¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|
|2012-07-08 10:54:57.837 CCC CCC111 V WAIT |
|2012-07-08 12:15:07.000 CCC CCC111 Y GTG |
|2012-07-17 06:58:33.417 CCC CCC111 T WAIT |
|2012-08-09 03:51:20.750 BBB BBB222 Y GTG |
|2012-08-09 04:06:13.473 BBB BBB222 Y GTG |
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Desired Output
________________________________________________________________
|Time LID LNum Location Go To|
|¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|
|2012-07-17 06:58:33.417 CCC CCC111 T WAIT |
|2012-08-09 04:06:13.473 BBB BBB222 Y GTG |
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
SELECT
IME.TP AS [Time],
IU.FS07 AS [LID],
IU.I AS [LNum],
IME.TPL AS [Location],
(SELECT CASE
WHEN IME.TPL = 'Y'
THEN 'GTG'
ELSE 'WAIT'
END) AS [Go To]
FROM
IU
INNER JOIN IUFV ON IU.G = IUFV.UG
INNER JOIN IME ON IUFV.G = IME.UFVG
INNER JOIN (Select Max(time) MTime, UFVG from IME
GROUP BY UFVG) B
on B.UFVG=IUFV.G
and B.MTime = IME.Time
Without a Deeper dive, I'm not sure if I could eliminate the additional join and just get the results from IME in the first place. but the need for TPL may necessitate the separate join
SELECT
IME.TP AS [Time],
IU.FS07 AS [LID],
IU.I AS [LNum],
IME.TPL AS [Location],
(SELECT CASE
WHEN IME.TPL = 'Y'
THEN 'GTG'
ELSE 'WAIT'
END) AS [Go To]
FROM
IU
INNER JOIN IUFV ON IU.G = IUFV.UG
INNER JOIN (Select Max(time) MTime, UFVG from IME
GROUP BY UFVG) B
on B.UFVG=IUFV.G
and B.MTime = IME.Time
Here's the basic approach. I'm assuming you never have duplicate timestamps within a group.
select *
from T
inner join (select GrpId, max(TimeStmp) from T group by GrpId) as MaxT
on MaxT.GrpId = T.GrpId and T.TimeStmp = MaxT.TimeStmp
Related
I use Spring Boot with Hibernate. Currently I have 3 separate requests to a database:
fetch all specific (with some WHERE conditions) data from table aaa
fetch all specific (with some WHERE conditions) data from table bbb
fetch max date of record that is found by WHERE clause with the same conditions from points 1 and 2.
Statement #1
SELECT count(a.id) as dateTo
from (
SELECT a.date_to
FROM aaa a
JOIN ramp r on a.ramp_id = r.id
JOIN warehouse w on r.warehouse_id = r.warehouse_id
WHERE w.id = 222
AND a.date_from >= '2022-08-20T00:00'
) allDates
Statement #2
SELECT count(b.id) as dateTo
from (
SELECT b.date_to
FROM bbb b
WHERE tw.warehouse.id = :warehouseId
AND tw.status = 'AVAILABLE'
) allDates
Statement #3
SELECT MAX(date_to) as dateTo
from (
SELECT a.date_to
FROM aaa a
JOIN ramp r on a.ramp_id = r.id
JOIN warehouse w on r.warehouse_id = r.warehouse_id
WHERE w.id = 222
AND a.date_from >= '2022-08-20T00:00'
UNION
SELECT b.valid_to as date_to
FROM bbb b
WHERE b.warehouse_id = 222
AND tw.status = 'AVAILABLE'
) allDates
Is it possible to do all this with one statement? I use MySql 5.7 so CTE is not available.
My code in Spring:
final long numberOfa = ...
final long numberOfB = ...
final LocalDate maxDate = ...
Expected result:
final MyObjectWithAllThreeValues myObject = repository.getAllDataWithOneQuery
You can store a flag in the subqueries (called "which_tab"), then use a CASE expression within a SUM aggregation function to count your rows.
SELECT MAX(date_to) AS dateTo,
SUM(CASE WHEN which_tab = 'a' THEN 1 END) AS count_a_id,
SUM(CASE WHEN which_tab = 'b' THEN 1 END) AS count_b_id
from (
SELECT 'a' AS which_tab, a.date_to
FROM aaa a
JOIN ramp r on a.ramp_id = r.id
JOIN warehouse w on r.warehouse_id = r.warehouse_id
WHERE w.id = 222
AND a.date_from >= '2022-08-20T00:00'
UNION ALL
SELECT 'b' AS which_tab, b.valid_to AS date_to
FROM bbb b
WHERE b.warehouse_id = 222
AND tw.status = 'AVAILABLE'
) allDates
Note: if your rows from the two subqueries you apply the UNION on do not overlap, it's better to use UNION ALL as it avoids an additional unnecessary aggregation.
All queries return a single row, because you aggregate all rows without any GROUP BY. You can hence do both aggregations and then cross join the two result rows:
SELECT
a_agg.cnt AS count_a,
b_agg.cnt AS count_b,
GREATEST(a.max_date, b.max_date) AS max_date
FROM
(
SELECT COUNT(*) AS cnt, MAX(a.date_to) AS max_date
FROM aaa a
JOIN ramp r ON r.id = a.ramp_id
JOIN warehouse w ON w.warehouse_id = r.warehouse_id
WHERE w.id = 222
AND a.date_from >= TIMESTAMP '2022-08-20 00:00:00'
) a_agg
CROSS JOIN
(
SELECT COUNT(*) AS cnt, MAX(b.date_to) AS max_date
FROM bbb b
WHERE b.warehouse.id = :warehouseId
AND b.status = 'AVAILABLE'
) b_agg;
I am pretty new to MYSQL and in general writing queries.
the most important things in this query is cycle_count_id and location. all information should be based on this two factors.
now, how can I improve this query:
SELECT c2.code_cycle_count, c2.location, c2.last_cyclecount, c2.second_recent_cyclecount ,i.uid,c2.po_number, i.cost, i.uid
, (SELECT
CASE WHEN MAX(cc.updated_at) = MAX(ccc.updated_at) THEN u.username ELSE NULL END
from oms_live_ir.wms_cycle_count ccc
INNER JOIN oms_live_ir.ims_user u ON u.id_user=ccc.fk_user
WHERE ccc.fk_location = cc.fk_location AND ccc.id_cycle_count=cc.id_cycle_count) AS cycle_count_user
, (SELECT COUNT(cci.fk_cycle_count_item_status)
from oms_live_ir.wms_cycle_count_item cci
WHERE cci.fk_cycle_count_item_status = 1 AND cc.id_cycle_count=cci.fk_cycle_count) AS location_updated
, (SELECT COUNT(cci.fk_cycle_count_item_status)
from oms_live_ir.wms_cycle_count_item cci
WHERE cci.fk_cycle_count_item_status = 2 AND cc.id_cycle_count=cci.fk_cycle_count) AS status_updated
, (SELECT COUNT(cci.fk_cycle_count_item_status)
from oms_live_ir.wms_cycle_count_item cci
WHERE cci.fk_cycle_count_item_status = 4 AND cc.id_cycle_count=cci.fk_cycle_count) AS found
, (SELECT COUNT(cci.fk_cycle_count_item_status)
from oms_live_ir.wms_cycle_count_item cci
WHERE cci.fk_cycle_count_item_status = 6 AND cc.id_cycle_count=cci.fk_cycle_count) AS lost
, c5.id_pick, c5.date_pick
FROM oms_live_ir.wms_cycle_count cc
INNER JOIN oms_live_ir.wms_inventory i on i.fk_location=cc.fk_location
INNER join
(SELECT l.description AS location
,MAX(cccc.id_cycle_count) AS code_cycle_count
,MAX(cccc.updated_at) AS last_cyclecount, i.po_number
,(SELECT MAX(c1.updated_at) FROM oms_live_ir.wms_cycle_count c1 WHERE c1.updated_at<>MAX(cccc.updated_at)
AND c1.fk_location=cccc.fk_location) as second_recent_cyclecount
FROM oms_live_ir.wms_cycle_count cccc
INNER JOIN oms_live_ir.wms_inventory i ON i.fk_location=cccc.fk_location
INNER JOIN oms_live_ir.ims_location l ON l.id_location=cccc.fk_location
WHERE year(cccc.updated_at)>=2018 AND month(cccc.updated_at)>=1
AND LEFT(i.po_number,2) LIKE 'M1%' or LEFT(i.po_number,2) LIKE 'S1%'
GROUP by cccc.fk_location
) c2 on c2.code_cycle_count= cc.id_cycle_count
INNER JOIN
(SELECT cc5.fk_location,
(SELECT CASE WHEN ih2.updated_at=MIN(ih2.updated_at) THEN ih2.sales_order_item_id ELSE NULL END
FROM oms_live_ir.wms_cycle_count cc2
INNER JOIN oms_live_ir.wms_inventory_history ih2 ON ih2.fk_location=cc2.fk_location
WHERE ih2.sales_order_item_id=ih5.sales_order_item_id AND ih2.fk_location=cc5.fk_location AND cc2.id_cycle_count=cc5.id_cycle_count
AND year(ih2.updated_at)>=2018 AND MONTH(ih2.updated_at)>=1 AND ih2.sales_order_item_id IS NOT NULL
GROUP BY ih2.id_inventory) id_pick
,(SELECT CASE WHEN ih2.updated_at=MIN(ih2.updated_at) THEN ih5.updated_at ELSE NULL END
FROM oms_live_ir.wms_cycle_count cc2
INNER JOIN oms_live_ir.wms_inventory_history ih2 ON ih2.fk_location=cc2.fk_location
WHERE ih2.sales_order_item_id=ih5.sales_order_item_id AND ih2.fk_location=cc5.fk_location AND cc2.id_cycle_count=cc5.id_cycle_count
AND year(ih2.updated_at)>=2018 AND MONTH(ih2.updated_at)>=1 AND ih2.sales_order_item_id
GROUP BY ih2.id_inventory) date_pick
FROM oms_live_ir.wms_cycle_count cc5
INNER JOIN oms_live_ir.wms_inventory_history ih5 ON ih5.fk_location=cc5.fk_location
WHERE year(ih5.updated_at)>=2018 AND MONTH(ih5.updated_at)>=1 AND ih5.sales_order_item_id IS NOT NULL) c5 ON c5.fk_location=cc.fk_location
WHERE year(cc.updated_at)>=2017 AND month(cc.updated_at)>=1
AND LEFT(i.po_number,2) LIKE 'M1%' or LEFT(i.po_number,2) LIKE 'S1%'
group by c2.code_cycle_count, c2.location;
(Note: I do not have access to create temporary tables or create index for tables.)
Thanks in advance.
I wrote and would like to combine these 2 sql, one is based on results of another. I checked this post, but looks like its not results based. How could I achieve it ?
First sql:
SELECT
`potential`.*,
`customer`.`ID` as 'FID_customer'
FROM
`os_potential` as `potential`,
`os_customer` as `customer`
WHERE `potential`.`FID_author` = :randomID
AND `potential`.`converted` = 1
AND `potential`.`street` = `customer`.`street`
AND `potential`.`zip` = `customer`.`zip`
AND `potential`.`city` = `customer`.`city`;
Second sql:
SELECT
sum(`order`.`price_customer`) as 'Summe'
FROM
`os_order` as `order`,
`RESUTS_FROM_PREVIOUS_SQL_STATEMENT` as `results`
WHERE `order`.`FID_status` = 10
AND `results`.`FID_customer` = `order`.`FID_customer`;
I would like to get everything from first sql + the 'Summe' from second sql.
TABLES
1.Potentials:
+----+------------+-----------+--------+-----+------+
| ID | FID_author | converted | street | zip | city |
+----+------------+-----------+--------+-----+------+
2.Customers:
+----+--------+-----+------+
| ID | street | zip | city |
+----+--------+-----+------+
3.Orders:
+----+--------------+----------------+
| ID | FID_customer | price_customer |
+----+--------------+----------------+
SELECT p.*
, c.ID FID_customer
, o.summe
FROM os_potential p
JOIN os_customer c
ON c.street = p.street
AND c.zip = p.zip
AND c.city = p.city
JOIN
( SELECT FID_customer
, SUM(price_customer) Summe
FROM os_order
WHERE FID_status = 10
GROUP
BY FID_customer
) o
ON o.FID_customer = c.ID
WHERE p.FID_author = :randomID
AND p.converted = 1
;
You would just write a single query like this:
SELECT sum(o.price_customer) as Summe
FROM os_order o JOIN
os_potential p JOIN
os_customer c
ON p.street = c.street AND p.zip = c.zip AND p.city = c.city JOIN
os_order o2
ON o2.FID_customer = c.FID_customer
WHERE p.FID_author = :randomID AND p.converted = 1 AND
o2.FID_status = 10 ;
Notes:
Never use commas in the FROM clause. Always use explicit JOIN syntax with conditions in an ON clause.
Table aliases are easier to follow when they are short. Abbreviations for the table names is commonly used.
Backticks are only necessary when the table/column name needs to be escaped. Yours don't need to be escaped.
If the 1st query return 1 record per customer, then just simply join the 3 tables, keep the sum and use the group by clause:
SELECT
`potential`.*,
`customer`.`ID` as 'FID_customer',
sum(`order`.`price_customer`) as Summe
FROM
`os_potential` as `potential`
INNER JOIN
`os_customer` as `customer`
ON `potential`.`street` = `customer`.`street`
AND `potential`.`zip` = `customer`.`zip`
AND `potential`.`city` = `customer`.`city`
LEFT JOIN
`os_order` as `order`
ON `results`.`FID_customer` = `order`.`FID_customer`
AND `order`.`FID_status` = 10
WHERE `potential`.`FID_author` = :randomID
AND `potential`.`converted` = 1
GROUP BY `customer`.`ID`, <list all fields from potential table>
If the 1st query may return multiple records per customer, then you need to do the summing in a subquery:
SELECT
`potential`.*,
`customer`.`ID` as 'FID_customer',
`order`.Summe
FROM
`os_potential` as `potential`
INNER JOIN
`os_customer` as `customer`
ON `potential`.`street` = `customer`.`street`
AND `potential`.`zip` = `customer`.`zip`
AND `potential`.`city` = `customer`.`city`
LEFT JOIN
(SELECT FID_customer, sum(price_customer) as Summe
FROM `os_order`
WHERE FID_status=10
GROUP BY FID_customer
) as `order`
ON `results`.`FID_customer` = `order`.`FID_customer`
WHERE `potential`.`FID_author` = :randomID
AND `potential`.`converted` = 1
I think you should use a subselect, but be careful with the number of results, it's not the best for performance.
You can do something like this:
SELECT n1, n2, (select count(1) from whatever_table) as n3, n4 from whatever_table
note that the subselect must return just 1 result, in other case you'll have an error
I have an issue where I need to aggregate and concatenate multiple row data into single row output. I understand the tables are a problem in that their is no unique index, but I need to do this at the query level instead of the scripting level and I can't touch the database structure.
Here we go:
table: characteristics
id code_a code_b
-------------------------------
2201 CHAU AIRS
2201 CHAU PELC
2201 PROX AUTO
2201 PROX HOP`
table: characteristics_types
code description
-------------------------------
CHAU Heating System
PROX Nearby
table: characteristics_sub_types
code_a code description
-------------------------------
CHAU AIRS Forced Air
CHAU PELC Baseboard
PROX AUTO Highway
PROX HOP Hospital
Result required:
id Heating System Nearby
--------------------------------------------------------
2201 Forced Air, Baseboard Highway, Hospital
Not working:
SELECT id,
(case when C.code_a='CHAU' THEN GROUP_CONCAT(STC.description) ELSE NULL END) AS Heating System,
(case when C.code_a='PROX' THEN GROUP_CONCAT(STC.description) ELSE NULL END) AS Nearby
from characteristics C
inner join characteristics_types TC on C.code_a=TC.`code`
inner join characteristics_sub_types STC on C.code_a=STC.code_a and C.code_b=STC.`code`
GROUP BY C.id,C.code_a
I am getting the following results:
id Heating System Nearby
--------------------------------------------------------
2201 Forced Air, Baseboard NULL
2201 NULL Highway, Hospital
Any direction would be greatly appreciated!
GROUP_CONCAT accepts a DISTINCT keyword which is useful in fan-out-queries. You can also use ORDER BY within a GROUP_CONCAT if you need your results to be ordred. See Documentation. Using this, we can write your intended query like below:
SELECT
c.id,
GROUP_CONCAT(DISTINCT ct.description) as 'Heating System',
GROUP_CONCAT(DISTINCT cst.description) as 'Nearby'
FROM characteristics c
LEFT JOIN characteristics_types ct ON ct.id = c.id
LEFT JOIN characteristics_sub_types cst
ON cst ON cst.code_a = c.code_a AND cst.code = c.code_b
GROUP BY 1
I would write separate subqueries, each one preforming the group_concat that you need, and join them together. Individually, I wrote them like this:
SELECT c.id, GROUP_CONCAT(cst.description) AS 'Heating System'
FROM characteristics c
JOIN characteristics_sub_types cst ON cst.code_a = c.code_a AND cst.code = c.code_b AND c.code_a = 'CHAU'
GROUP BY c.id;
And the join like this:
SELECT c.id, t1.`Heating System`, t2.`Nearby`
FROM(
SELECT c.id, GROUP_CONCAT(cst.description) AS 'Heating System'
FROM characteristics c
JOIN characteristics_sub_types cst ON cst.code_a = c.code_a AND cst.code = c.code_b AND c.code_a = 'CHAU'
GROUP BY c.id) t1
JOIN(
SELECT c.id, GROUP_CONCAT(cst.description) AS 'Nearby'
FROM characteristics c
JOIN characteristics_sub_types cst ON cst.code_a = c.code_a AND cst.code = c.code_b AND c.code_a = 'PROX'
GROUP BY c.id) t2 ON t1.id = t2.id;
Here is a working SQL Fiddle.
You can try this way:
SELECT c.id,
GROUP_CONCAT(STC.description_heating) AS `Heating System`,
GROUP_CONCAT(STC.description_nearby) AS Nearby
from characteristics C
inner join characteristics_types TC on C.code_a=TC.`code`
inner join (
SELECT code_a, code,
CASE WHEN code_a = 'CHAU' THEN description ELSE null END as descriptio_heating,
CASE WHEN code_a = 'PROX' THEN description ELSE null END as descriptio_nearby,
FROM characteristics_sub_types
) as STC on C.code_a=STC.code_a and C.code_b=STC.`code`
GROUP BY C.id
SELECT c.id,
GROUP_CONCAT(STC.description_heating) AS `Heating System`,
GROUP_CONCAT(STC.description_nearby) AS Nearby
from characteristics C
inner join characteristics_types TC on C.code_a=TC.`code`
inner join (
SELECT code_a, code,
CASE WHEN code_a = 'CHAU' THEN description ELSE null END as description_heating,
CASE WHEN code_a = 'PROX' THEN description ELSE null END as description_nearby
FROM characteristics_sub_types
) as STC on C.code_a=STC.code_a and C.code_b=STC.`code`
GROUP BY C.id
I have
users
------------------------
id | name | other_stuff.....
.
engagement
------------------------
user_id | type_code |
type_code is a varchar, but either A, B, C or NULL
[ EDIT for clarity: Users can have many engagements of each type code. SO I want to count how many they have of each. ]
I want to return ALL user rows, but with a count of A, B and C type engagements. E.g.
users_result
------------------------
user_id | user_name | other_stuff..... | count_A | count_B | count_C |
I've done quite a bit of searching, but found the following issues with other solutions:
The "other_stuff..." is actually grouped / concatenated results from a dozen other joins, so it's a bit of a monster already. So I need to be able to just add the additional fields to the pre-existing "SELECT ...... FROM users..." query.
The three additional required bits of data all come from the same engagement table, each with their own condition. I havent found anything to allow me to use the three conditions on the same related table.
Thanks
[edit]
I tried to simplify the question so people didn't have to look through loads of unnecessary stuff, but seems I might not have given enough info. Here is 'most' of the original query. I've taken out a lot of the selected fields as there are loads, but I've left most of the joins in so you can see basically what is actually going on.
SELECT
user.id,
user.first_name,
user.second_name,
GROUP_CONCAT(DISTINCT illness.id ORDER BY illness.id SEPARATOR ',' ) AS reason_for_treatment,
IF(ww_id=1000003, 1,'') as user_refused_program,
Group_CONCAT(DISTINCT physical_activity.name SEPARATOR ', ') AS programme_options,
COUNT(CASE WHEN engagement_item.type_code LIKE 'wm6%' THEN 1 ELSE NULL END) as count_A,
COUNT(CASE WHEN engagement_item.type_code LIKE 'wm12%' THEN 1 ELSE NULL END) as count_B,
COUNT(CASE WHEN engagement_item.type_code LIKE 'wm6%' THEN 1 ELSE NULL END) as count_C
FROM `user`
LEFT JOIN session AS session_induction ON (user.id = session_induction.user_id AND session_induction.session_type_id = 3)
LEFT JOIN stats AS stats_induction ON session_induction.id = stats_induction.session_id
LEFT JOIN session AS session_interim ON (user.id = session_interim.user_id AND session_interim.session_type_id = 4)
LEFT JOIN stats AS stats_interim ON session_interim.id = stats_interim.session_id
LEFT JOIN session AS session_final ON (user.id = session_final.user_id AND session_final.session_type_id = 5)
LEFT JOIN stats AS stats_final ON session_final.id = stats_final.session_id
LEFT JOIN user_has_illness ON user.ID = user_has_illness.user_id
LEFT JOIN illness ON user_has_illness.illness_id = illness.id
LEFT JOIN user_has_physical_activity ON user.ID = user_has_physical_activity.user_id
LEFT JOIN physical_activity ON user_has_physical_activity.physical_activity_id = physical_activity.id
LEFT JOIN engagement_item ON user.ID = engagement_item.user_ID
WHERE (user.INDUCTION_DATE>='2010-06-09' AND user.INDUCTION_DATE<='2011-06-09' AND user.archive!='1' )
GROUP BY user.id, engagement_item.user_id
It's worth mentioning that it works fine - returns all users with all details required. Except for the count_A B and C cols.
[edit added slightly more simplified query below]
Stripped out the unrelated joins and selects.
SELECT
user.id,
user.first_name,
COUNT(CASE WHEN engagement_item.type_code LIKE 'wm6%' THEN 1 ELSE NULL END) as count_A,
COUNT(CASE WHEN engagement_item.type_code LIKE 'wm12%' THEN 1 ELSE NULL END) as count_B,
COUNT(CASE WHEN engagement_item.type_code LIKE 'wm6%' THEN 1 ELSE NULL END) as count_C
FROM `user`
LEFT JOIN engagement_item ON user.ID = engagement_item.user_ID
GROUP BY user.id, engagement_item.user_id
SELECT e.user_id, u.name,
COUNT(CASE type_code WHEN 'A' THEN 1 ELSE NULL END) as count_A,
COUNT(CASE type_code WHEN 'B' THEN 1 ELSE NULL END) as count_B,
COUNT(CASE type_code WHEN 'C' THEN 1 ELSE NULL END) as count_C
FROM engagement e join users u on (e.user_id = u.id)
GROUP BY e.user_id, u.name
I would use COUNT instead of SUM just because that is what it is made for, counting things when not NULL.
SELECT
user.id,
user.first_name,
user.second_name,
GROUP_CONCAT(DISTINCT illness.id ORDER BY illness.id SEPARATOR ',' ) AS reason_for_treatment,
IF(ww_id=1000003, 1,'') as user_refused_program,
Group_CONCAT(DISTINCT physical_activity.name SEPARATOR ', ') AS programme_options,
ei.count_A, ei.count_B, ei.count_C
FROM `user`
LEFT JOIN ( SELECT user_id
, COUNT(CASE WHEN engagement_item.type_code LIKE 'wm6%' THEN 1 ELSE NULL END) as count_A
, COUNT(CASE WHEN engagement_item.type_code LIKE 'wm12%' THEN 1 ELSE NULL END) as count_B
, COUNT(CASE WHEN engagement_item.type_code LIKE 'wm6%' THEN 1 ELSE NULL END) as count_C
FROM engagement_item
GROUP BY userid ) ei
LEFT JOIN session AS session_induction ON (user.id = session_induction.user_id AND session_induction.session_type_id = 3)
LEFT JOIN stats AS stats_induction ON session_induction.id = stats_induction.session_id
LEFT JOIN session AS session_interim ON (user.id = session_interim.user_id AND session_interim.session_type_id = 4)
LEFT JOIN stats AS stats_interim ON session_interim.id = stats_interim.session_id
LEFT JOIN session AS session_final ON (user.id = session_final.user_id AND session_final.session_type_id = 5)
LEFT JOIN stats AS stats_final ON session_final.id = stats_final.session_id
LEFT JOIN user_has_illness ON user.ID = user_has_illness.user_id
LEFT JOIN illness ON user_has_illness.illness_id = illness.id
LEFT JOIN user_has_physical_activity ON user.ID = user_has_physical_activity.user_id
LEFT JOIN physical_activity ON user_has_physical_activity.physical_activity_id = physical_activity.id
LEFT JOIN engagement_item ON user.ID = engagement_item.user_ID
WHERE (user.INDUCTION_DATE>='2010-06-09' AND user.INDUCTION_DATE<='2011-06-09' AND user.archive!='1' )
GROUP BY user.id, engagement_item.user_id, ei.count_A, ei.count_B, ei.count_C
Something like this perhaps?
select e.user_id, u.name,
sum(case e.type_code when 'A' then 1 else 0 end) as count_A,
sum(case e.type_code when 'B' then 1 else 0 end) as count_B,
sum(case e.type_code when 'C' then 1 else 0 end) as count_C
from engagement e join users u on (e.user_id = u.id)
group by e.user_id, u.name
The interesting part is the use of CASE inside the SUM to split the counting into three chunks.