SQLSTATE [42601]:语法错误:7错误:子查询必须只返回一列使用Function

时间:2018-06-01 10:04:55

标签: sql postgresql plpgsql

我收到错误“子查询必须只返回一列”,但是当我选择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 :(得分:0)

由于问题答案不正确,我提供的答案超出了评论范围。您想要的代码是:

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".*)

答案 1 :(得分:0)

对于这种情况,您不应该使用语法:

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样式的性能较低取决于迭代次数。对于低迭代,它并不重要,因为更高的数字是死亡。