SQLSTATE [42601]: синтаксическая ошибка: 7 ОШИБКА: подзапрос должен возвращать только один столбец Использование функции - PullRequest
0 голосов
/ 01 июня 2018

Я получаю сообщение об ошибке «подзапрос должен возвращать только один столбец», но я пытался использовать разные, чтобы вернуть первую запись, когда я выбираю curProd.

Я использую эту функцию, но яполучить ошибку, насколько я знаю в:

curProd := (
        SELECT "KeysForSale".*
        FROM "KeysForSale"
        WHERE row_STab.product_id = "KeysForSale".product_id AND (("KeysForSale".begin_date < payment_date AND "KeysForSale".end_date > payment_date) OR ("KeysForSale".discounted_price IS NULL))
        ORDER BY "KeysForSale".discounted_price ASC NULLS LAST
        LIMIT 1
    );

Вся функция:

CREATE FUNCTION "paymentRun"(buyer_id integer, payment_date DATE, payMethod paymentMethod, paid_amount double precision, payDetails text) RETURNS VOID AS
$$
DECLARE
row_STab "SearchTable"%rowtype;
curProd  "KeysForSale"%rowtype;
totalPrice double precision;
returnedPID integer;
BEGIN

--For each entry in the search table
FOR row_STab IN 
(
    SELECT *
    FROM "SearchTable"
)
LOOP

    --We retrieve the associated product info, together with an available key
    curProd := (
        SELECT "KeysForSale".*
        FROM "KeysForSale"
        WHERE row_STab.product_id = "KeysForSale".product_id AND (("KeysForSale".begin_date < payment_date AND "KeysForSale".end_date > payment_date) OR ("KeysForSale".discounted_price IS NULL))
        ORDER BY "KeysForSale".discounted_price ASC NULLS LAST
        LIMIT 1
    );

    --Either there is no such product, or no keys for it
    IF curProd IS NULL THEN
        RAISE EXCEPTION 'Product is not available for purchase.';
    END IF;

    --Product's seller is the buyer - we can't let that pass
    IF curProd.user_id = buyer_id THEN
        RAISE EXCEPTION 'A Seller cannot purchase their own product.';
    END IF;

    --Fill in the rest of the data to prepare the purchase
    UPDATE "SearchTable"
    SET "SearchTable".price = (
        CASE curProd.discounted_price IS NOT NULL -- if there was a discounted price, use it
        WHEN TRUE THEN curProd.discounted_price
        ELSE curProd.price
        END
        ), "SearchTable".sk_id = curProd.sk_id 
    WHERE "SearchTable".product_id = curProd.product_id;

END LOOP;

--Get total cost
totalPrice := (
    SELECT SUM("SearchTable".price)
    FROM "SearchTable"
);

--The given price does not match the actual cost?
IF totalPrice <> paid_amount THEN
        RAISE EXCEPTION 'Payment does not match cost!';
END IF;

--Create a purchase while keeping it's ID for register
INSERT INTO "Purchases" (purchase_id, final_price, user_id, paid_date, payment_method, details)
VALUES (DEFAULT, totalPrice, buyer_id, payment_date, payMethod, payDetails)
RETURNING purchase_id INTO returnedPID;

--For each product we wish to purchase
FOR row_STab IN 
(
    SELECT *
    FROM "SearchTable"
)
LOOP

    INSERT INTO "PurchasedKeys"(sk_id, purchase_id, price)
    VALUES (row_STab.sk_id, returnedPID, row_STab.price);

    UPDATE "SerialKeys"
    SET "SerialKeys".user_id = buyer_id
    WHERE row_STab.sk_id = "SerialKeys".sk_id;

END LOOP;



END
$$
LANGUAGE 'plpgsql' ;

Заранее спасибо

Ответы [ 2 ]

0 голосов
/ 03 июня 2018

В этом случае вам не следует использовать синтаксис:

var := (SELECT ..).

Предпочтительным должно быть SELECT INTO:

SELECT * INTO curProd FROM ...

Синтаксис SELECT tabname FROM tabname является проприетарным для PostgreSQL, и хотя онработает хорошо, лучше не использовать, из-за нечитаемости для всех без более глубокого знания PostgreSQL.

Поскольку PL / pgSQL не чувствителен к регистру, верблюжий случай не рекомендуется (лучше использовать случай змеи).

Если это возможно, не используйте стиль ISAM:

FOR _id IN
  SELECT id FROM tab1
LOOP
  SELECT * INTO r FROM tab2 WHERE tab2.id = _id

Это значительно медленнее, чем соединение (для большего количества итераций)

FOR r IN
  SELECT tab2.*
    FROM tab1 JOIN tab2 ON tab1.id = tab2.id
LOOP
  ..

Циклы ухудшают производительность.Эта часть не очень приятна:

FOR row_STab IN 
(
    SELECT *
    FROM "SearchTable"
)
LOOP

    INSERT INTO "PurchasedKeys"(sk_id, purchase_id, price)
    VALUES (row_STab.sk_id, returnedPID, row_STab.price);

    UPDATE "SerialKeys"
    SET "SerialKeys".user_id = buyer_id
    WHERE row_STab.sk_id = "SerialKeys".sk_id;

END LOOP;

Возможные решения:

Вместо этого используйте групповые команды:

INSERT INTO "PurchasedKeys"(sk_id, purchase_id, price)
   SELECT sk_id, returnedPID, price
     FROM "SearchTable"; -- using case sensitive identifiers is way to hell

UPDATE "SerialKeys"
  SET "SerialKeys".user_id = buyer_id
  FROM "SearchTable"
  WHERE "SearchTable".sk_id = "SerialKeys".sk_id;

Меньшая производительность стиля ISAM зависит от количества итераций,Для низкой итерации это не важно, для более высокого числа это смерть.

0 голосов
/ 01 июня 2018

Поскольку вопрос имеет неправильный ответ, я даю ответ за пределы комментария.Код, который вам нужен:

curProd := (
    SELECT "KeysForSale"
    FROM "KeysForSale"
    WHERE row_STab.product_id = "KeysForSale".product_id AND (("KeysForSale".begin_date < payment_date AND "KeysForSale".end_date > payment_date) OR ("KeysForSale".discounted_price IS NULL))
    ORDER BY "KeysForSale".discounted_price ASC NULLS LAST
    LIMIT 1
);

Разница заключается в отсутствии .*.Ваша версия возвращает кучу столбцов - это ошибка, которую вы получаете.Вы хотите вернуть одну запись .Имя таблицы обеспечивает это.

Я также думаю, что скобки будут иметь тот же эффект:

    SELECT ("KeysForSale".*)
...