Если бы категории были фиксированными (3, 4 и 5), вы могли бы использовать этот запрос:
select
reqid,
group_concat(distinct if(catid = 3, itemid, null)) as catid3itemid,
group_concat(distinct if(catid = 3, item, null)) as catid3item,
group_concat(distinct if(catid = 3, `desc`, null)) as catid3desc,
group_concat(distinct if(catid = 3, amount, null)) as catid3amount ,
group_concat(distinct if(catid = 4, itemid, null)) as catid4itemid,
group_concat(distinct if(catid = 4, item, null)) as catid4item,
group_concat(distinct if(catid = 4, `desc`, null)) as catid4desc,
group_concat(distinct if(catid = 4, amount, null)) as catid4amount ,
group_concat(distinct if(catid = 5, itemid, null)) as catid5itemid,
group_concat(distinct if(catid = 5, item, null)) as catid5item,
group_concat(distinct if(catid = 5, `desc`, null)) as catid5desc,
group_concat(distinct if(catid = 5, amount, null)) as catid5amount
from tab
group by reqid;
Если он должен быть динамическим, можно использовать процедуру.Ниже я использовал структуру повторения, чтобы сгенерировать запрос, который возвращает желаемую структуру для каждого catid
(от минимума до максимума) и выполняет его с подготовленным комментарием :
-- only to avoid problem with only_full_group_by
set global sql_mode = "";
create table tab (reqid int, catid int, itemid int, item varchar(10), `desc` varchar(20), amount int);
insert into tab values
(1, 3, 16, 'food', 'food', 200),
(1, 3, 17, 'water', 'wtr', 50),
(1, 3, 18, 'film', 'film', 20),
(1, 5, 30, 'room', 'room', 500),
(1, 5, 31, 'chair', 'chair', 150),
(2, 3, 16, 'food', 'food', 200),
(2, 3, 17, 'water', 'wtr', 50),
(3, 3, 18, 'film', 'film', 20),
(3, 5, 30, 'room', 'room', 500),
(3, 5, 31, 'chair', 'chair', 150);
delimiter $$
CREATE PROCEDURE result()
BEGIN
DECLARE i INT DEFAULT (select min(catid) from tab);
DECLARE iEnd INT DEFAULT (select max(catid) from tab);
SET @sQuery = 'select reqid';
WHILE i <= iEnd DO
set @sQuery = CONCAT(@sQuery,
', group_concat(distinct if(catid = ',i,', itemid, null)) as catid',i,'itemid,
group_concat(distinct if(catid = ',i,', item, null)) as catid',i,'item,
group_concat(distinct if(catid = ',i,', `desc`, null)) as catid',i,'desc,
group_concat(distinct if(catid = ',i,', amount, null)) as catid',i,'amount'
);
SET i = i + 1;
END WHILE;
SET @sQuery = CONCAT(@sQuery, ' from tab group by reqid');
PREPARE stmt FROM @sQuery;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END $$
call result();
Примечание. Поскольку вы не указали слишком много подробностей и, как уже было сказано в комментариях, может быть лучше выполнить логику отображения в приложении.