【问题标题】:SQLSTATE[42601]: Syntax error: 7 ERROR: subquery must return only one column Using FunctionSQLSTATE [42601]:语法错误:7 错误:子查询必须只返回一列使用函数
【发布时间】:2018-06-01 10:04:55
【问题描述】:

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

all 函数是:

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' ;

提前谢谢你

【问题讨论】:

  • 试试select "KeysForSale" 而不是select "KeysForSale".*

标签: sql postgresql plpgsql


【解决方案1】:

由于该问题的答案不正确,因此我提供了评论之外的答案。你想要的代码是:

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

【讨论】:

  • 是的,括号也可以!非常感谢
【解决方案2】:

在这种情况下,你不应该使用语法:

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

比 join 慢很多(迭代次数更多)

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

循环不利于性能。这部分不是很好:

FOR row_STab IN ( 选择 * 来自“搜索表” ) 环形 插入“PurchasedKeys”(sk_id,purchase_id,价格) 值(row_STab.sk_id,returnedPID,row_STab.price); 更新“序列号” SET "SerialKeys".user_id = Buyer_id WHERE row_STab.sk_id = "SerialKeys".sk_id; 结束循环;

可能的解决方案:

改用批量命令:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-11-08
    • 2019-12-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多