user187291 ответ очень хороший. Я хотел бы отметить, что вы можете напечатать свой список, не просматривая набор результатов более одного раза. Это может быть полезно, если у вас много предметов.
Для этого убедитесь, что результат запроса упорядочен по категориям. Создайте переменную для отслеживания «текущей категории» и задайте для нее значение, которое не будет идентификатором категории, например «x». Зацикливайтесь на результате, и каждый раз, когда CatID не соответствует значению вашей текущей категории, вы знаете, что изменили категории, и вам нужно напечатать заголовок категории. Сброс текущего значения категории до нового значения CatID.
Этого достаточно в некоторых ситуациях списка. В вашем случае вам также необходимо выяснить, когда заканчивается категория, чтобы вы могли правильно завершить свои списки. Вы можете сделать это, заглянув в массив результатов, чтобы увидеть, соответствует ли следующая категория вашей текущей категории и действует ли она соответствующим образом.
Имея всего несколько категорий, действительно не имеет значения, как вы это делаете. Если у вас есть тысячи строк с большим количеством данных, этот метод может быть немного быстрее и использовать меньше памяти. Это немного сложнее понять логически, чем метод user187291. Но он просматривает данные только один раз и не создает дубликата данных, что может быть полезно, если у вас есть мегабайты содержимого для циклического прохождения.