Я пытаюсь выполнить выбор, используя уже созданную функцию, которая возвращает JSON, который объединяется в массив JSON, но у меня возникает проблема в строке, где я выполняю предложение SELECT. Таблицы, при запросе которых возникают проблемы:
create table orders (
order_id bigserial not null, -- this is Primary Key
total double precision,
order_date timestamp,
user_id bigint -- references `users` table
);
create table order_item
(
order_item_id bigserial not null, --primary key
amount integer,
book_id bigint, -- FK which references `book` table
order_id bigint -- FK which references `orders` table
);
Мой запрос выглядит так в PL / pg SQL:
create or replace function public.get_order_by_order_id(o_id bigint) returns json as
$BODY$
DECLARE
order_items json;
found_order "vertx-jooq-cr".public.orders;
found_user json;
begin
-- other queries left out for code brevity
select json_agg(x) INTO order_items
from (select public.get_orderitem_by_oi_id(
select oi.order_item_id -- this is where PROBLEM occurs !!!
from public.order_item AS oi
where oi.order_id = o_id)
) x;
return (select json_build_object(
'order_id', found_order.order_id,
'total_price', found_order.total,
'order_date', found_order.order_date,
'user', found_user,
'order_items', order_items
));
end
$BODY$
language 'plpgsql';
... а вот функция get_orderitem_by_oi_id(order_item_id::bigint)
(которая работает правильно и возвращает JSON): В строке, где помещается комментарий « - здесь ПРОБЛЕМА !!! », я получаю сообщение об ошибке / предупреждение в DataGrip , в котором говорится:
')' или ORDER ожидался, получил 'select'
Вот как мой JSON должен выглядеть:
{
"order_id": 21,
"total_price": 89.92,
"order_date": "2020-05-03 00:00:00",
"order_items": [
{
"order_item_id": 32,
"amount": 3,
"book": {
"book_id": 2,
"title": "Murder on the Orient Express",
"price": 19.98,
"amount": 151,
"deleted": false,
"authors": [
{
"author_id": 1,
"first_name": "Agatha",
"last_name": "Christie"
}
],
"categories": [
{
"category_id": 9,
"name": "Crime",
"deleted": false
}
]
},
"order_id": 21,
"total_order_item_price": 59.94
},
{
"order_item_id": 31,
"amount": 2,
"book": {
"book_id": 5,
"title": "Harry Potter and the Prisoner of Azkaban",
"price": 14.99,
"amount": 85,
"deleted": false,
"authors": [
{
"author_id": 4,
"first_name": "JK",
"last_name": "Rowling"
}
],
"categories": [
{
"category_id": 3,
"name": "Tragedy",
"deleted": false
}
]
},
"order_id": 21,
"total_order_item_price": 29.98
}
],
"user": {
"user_id": 1,
"username": "test"
}
}
My вопрос в том, можно ли запросить адекватный order_item_id ( from order_item
table) с использованием функции get_orderitem_by_oi_id(order_item_id::bigint)
, передав адекватный order_id в запросе, и если нет, есть ли другой подходящий способ добиться этого? Приветствуется любая помощь в решении этой проблемы.
PS Версия PostgreSQL - 11,8
UPDATE1 :
Я отредактировал свою функцию Pl / pg SQL и теперь выглядит так:
create or replace function public.get_order_by_order_id(o_id bigint) returns json as $BODY$
DECLARE
order_items json;
found_order "vertx-jooq-cr".public.orders;
found_user json;
_item_id bigint; -- left it here from @Adrian Klaver's 1st version answer
_oitems_ids bigint[];
item_recs RECORD;
begin
-- other necessary queries left out for code brevity
FOR item_recs IN SELECT oi.order_item_id into _item_id -- gives an error
FROM public.order_item AS oi WHERE oi.order_id = o_id
LOOP
--- Will need to modify to get your final JSON structure.
SELECT json_agg(x) INTO order_items
FROM (SELECT public.get_orderitem_by_oi_id(item_recs.order_item_id)) x;
END LOOP;
return (select json_build_object(
'order_id', found_order.order_id,
'total_price', trunc(found_order.total::double precision::text::numeric, 2),
'order_date', found_order.order_date,
'user', found_user,
'order_items', item_recs -- updated value from 'order_items'
));
end
$BODY$
language 'plpgsql';
Ошибка, которую я получаю при выполнении функции:
ОШИБКА: невозможно открыть запрос SELECT в качестве курсора КОНТЕКСТ: PL / pg SQL функция get_order_by_order_id (bigint) строка 21 в FOR по строкам SELECT SQL state: 42P11
К сожалению, у меня нет опыта работы с курсорами в PostgreSQL и PL / pg SQL (начал изучать PL / pg SQL менее чем через неделю go). Есть идеи, на что указывает эта ошибка и как ее исправить? Заранее спасибо.
UPDATE2: Я отредактировал свой запрос (точнее, FOR-L OOP часть), выполнил его и получил в результате:
{
"order_id": 1069,
"total_price": 136.94,
"order_date": "2020-06-10T19:57:40.562",
"user": 3,
"order_items": {
"order_item_id": 2042
}
}
Кстати, у order_id есть два элемента order_items с идентификатором order_item_id 2041 и 2042, что означает, что только второй элемент order_item «пойман». Также неполный объект JSON строится из функции public.get_orderitem_by_oi_id(oi_id **bigint**)
. Любой совет, как это исправить?
UPDATE3 : В комментариях я должен подчеркнуть, что public.get_orderitem_by_oi_id(_item_id)
function RETURNS JSON type, и вот моя обновленная функция, которая теперь выглядит так:
create or replace function public.get_order_by_order_id6(o_id bigint) returns json as $BODY$
DECLARE
order_items json;
found_order "vertx-jooq-cr".public.orders;
found_user json;
_item_id bigint;
_oitems_ids bigint[];
item_recs RECORD;
begin
select * into found_order
from "vertx-jooq-cr".public.orders
where order_id = o_id;
-- other queries left out for code brevity
FOR _item_id IN SELECT DISTINCT oi.order_item_id
FROM public.order_item AS oi WHERE oi.order_id = o_id
LOOP
--- Will need to modify to get your final JSON structure.
SELECT json_agg(x) INTO order_items
FROM (SELECT public.get_orderitem_by_oi_id(_item_id)) x;
END LOOP;
return (select json_build_object(
'order_id', found_order.order_id,
'total_price', trunc(found_order.total::double precision::text::numeric, 2),
'order_date', found_order.order_date,
'user', found_user,
'order_items', order_items
));
end
$BODY$
language 'plpgsql';
... и это результат Я получаю (в JSON, конечно):
{
"order_id": 1069,
"total_price": 136.93,
"order_date": "2020-06-10T19:57:40.562",
"user": {
"user_id": 3,
"username": "mica"
},
"order_items": [
{
"get_orderitem_by_oi_id": { -- for some reason it INSERTS function name HERE!!!
"order_item_id": 2042,
"amount": 2,
"book": {
"book_id": 8,
"title": "The Lord of the Rings",
"price": 23.5,
"amount": 298,
"is_deleted": false,
"authors": [
{
"author_id": 3,
"first_name": "JRR",
"last_name": "Tolkien"
}
],
"categories": [
{
"category_id": 9,
"name": "Crime",
"is_deleted": false
}
]
},
"order_id": 1069,
"total_order_item_price": 59.96
}
}
]
}
По какой-то причине он продолжает получать ТОЛЬКО ПОСЛЕДНЮЮ запись выбранного order_itemS
и продолжает вставлять имя функции, полученное в результате (указано под " - почему-то ВСТАВЛЯЕТ имя функции ЗДЕСЬ !!! " комментарий в коде JSON). Есть идеи, как агрегировать / собирать ВСЕ записи в order_items
json переменную?
PS Я экспериментировал с отдельной функцией с FOR-L OOP где переменные одинаковы (кроме order_items
, который имеет тип json [] ):
FOR _item_id IN SELECT DISTINCT oi.order_item_id FROM public.order_item AS oi WHERE oi.order_id = o_id
LOOP
order_items := order_items || json_build_object('order_item_id', _item_id);
END LOOP;
.. и он дал ВСЕ идентификаторы для order_item (2041 и 2042, НЕ только 2042 как get_order_by_order_id6()
функция).
UPDATE4: Вот функция get_orderitem_by_oi_id(order_item_id::bigint)
(которая работает правильно и возвращает JSON):
create or replace function get_orderitem_by_oi_id(oi_id bigint) returns json
language plpgsql
as
$FUNCTION$
declare
found_oi "vertx-jooq-cr".public.order_item;
book_json json;
total_oi_price decimal;
book_price double precision;
begin
select * into found_oi
from public.order_item AS oi2
where oi2.order_item_id = oi_id;
select public.get_book_by_book_id(public.order_item.book_id::bigint) into book_json
from public.order_item
where public.order_item.order_item_id = oi_id;
select price into book_price
from book AS b
inner join public.order_item AS oi USING (book_id);
total_oi_price = found_oi.amount * book_price;
return (select json_build_object(
'order_item_id', found_oi.order_item_id,
'amount', found_oi.amount,
'book', book_json,
'order_id', found_oi.order_id,
'total_order_item_price', trunc(total_oi_price::double precision::text::numeric, 2)
));
end
$FUNCTION$;