Попробуйте добавить индекс на пару полей (plu, sku). При выборке сортируйте по ним на упровне БД.
Используйте itertools.groupby для группировки сперва по plu, а потом и по sku.
Обходите группировки двумя циклами (один вложен в другой).
Внутри внутреннего цикла уже можно использовать литерал генератора списка, чтобы получить список pack_type_id.
Это позволит сразу собирать последовательно элементы словарей и не возвращаться к уже собранным.
Если вашу структуру нужно поместить в какой-то внешний json-файл или отдавать в тело http-запроса в json-формате, то можно попробовать писать json с помощью какой-нибудь потоковой библиотеки вроде этой:
https://pypi.org/project/jsonstreams/
Это позволит не ждать по отдельности извлечение данных из БД, сборку структуры а пвмяти, а затем сериализацию ее в файл или в поток сокета. Всё будет делаться в рамках одного конвейера, что может оказаться более эффективным по времени.
Однако вам и ваш способ стоило бы проверить на предмет "бутылочных горлышек" и понять что именно занимает у вас основное время работы вашего алгоритма.
Возможно радикально ускорить процедуру и не удастся ввиду очень медленной БД или гиганского размера получающейся структуры.
Расскажите подробнее куда вам нужна такая структура, какой у нее получается объём, куда вы её потом пихаете, как измеряете скорость, почему вам так критична эта скорость?
Может быть проблема решается и иначе? К примеру, может оказаться, что вам не требуется синхронно возвращать всю структуру, или можно запрос и ответ развести в отдельные запросы, сделав интерфейс более отзывчивым.
В общем опишите вашу задачу подробнее. Ну и есл что наисал не понятно, спрашивайте. Буду разьяснять детальнее.