Làm thế nào để vượt qua một kiểu bảng với một trường mảng cho một hàm trong postgresql


8

tôi có một cái bàn gọi là sách

CREATE TABLE book
(
  id smallint NOT NULL DEFAULT 0,       
  bname text,       
  btype text,
  bprices numeric(11,2)[],
  CONSTRAINT key PRIMARY KEY (id )
)

và một hàm save_book

CREATE OR REPLACE FUNCTION save_book(thebook book)
  RETURNS text AS
$BODY$
DECLARE 
myoutput text :='Nothing has occured';
BEGIN

    update book set 
    bname=thebook.bname,
    btype=thebook.btype,bprices=thebook.bprices  WHERE id=thebook.id;

    IF FOUND THEN
        myoutput:= 'Record with PK[' || thebook.id || '] successfully updated';
        RETURN myoutput;
    END IF;

    BEGIN
        INSERT INTO book values(thebook.id,thebook.bname,thebook.btype,
        thebook.bprices);
        myoutput:= 'Record successfully added';           
    END;
 RETURN myoutput;

    END;
$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;

bây giờ khi tôi gọi hàm

SELECT save_book('(179,the art of war,fiction,{190,220})'::book);

tôi nhận được lỗi

ERROR: malformed array literal: "{190"
SQL state: 22P02
Character: 18

Tôi không hiểu vì tôi không thấy bất kỳ lỗi nào trong định dạng của mảng, có giúp được gì không?


Chức năng của bạn có vẻ hơi phức tạp đối với tôi, nhưng anways. Một mảng bằng chữ phải được đặt trong dấu ngoặc đơn, vì vậy hãy thử như sau : ave_book((179,the art of war,fiction,'{190,220}')::book. Các hàng được xây dựng không cần báo giá.
dezso

Khi tôi chạy mà tôi gặp lỗi ERROR: syntax error at or near "art"
indago

1
Các chuỗi ký tự riêng lẻ vẫn phải được đặt trong dấu ngoặc kép.
Andriy M

Xin lỗi, một trong những chính xác là ave_book((179, 'the art of war', 'fiction', '{190,220}')::book, giống như cách nói của Andriy.
dezso

@dezso: Có vẻ như một câu trả lời cho tôi. :)
Andriy M

Câu trả lời:


7

Điều này trở nên phức tạp. Tôi đang làm việc trên một số dự án liên quan ngay bây giờ. Điều chỉnh cơ bản là PostgreSQL sử dụng định dạng sử dụng dấu ngoặc kép trong nội bộ trong biểu diễn tuple để biểu thị các giá trị bằng chữ, vì vậy:

SELECT save_book('(179,the art of war,fiction,"{190,220}")'::book);

nên làm việc. Về bản chất, một thủ thuật gọn gàng là tạo ra một csv và kèm theo các định danh tuple hoặc mảng. Vấn đề lớn là bạn phải đối phó với việc trốn thoát (nhân đôi báo giá ở mọi cấp độ khi cần thiết). Vì vậy, sau đây là chính xác tương đương:

SELECT save_book('(179,"the art of war","fiction","{""190"",""220""}")'::book);

Cách tiếp cận thứ hai là sử dụng một hàm tạo hàng:

SELECT save_book(row(179,'the art of war','fiction', array[190,220])::book);

Giải pháp đầu tiên có lợi thế rõ ràng là có thể tận dụng các khung lập trình hiện có để tạo và thoát CSV. Thứ hai là sạch nhất trong SQL. Chúng có thể được trộn lẫn và kết hợp.


+1 để sử dụng một hàm tạo mảng, thoát có thể là một cơn ác mộng
Jack nói hãy thử topanswers.xyz

@Chris, Giải pháp đầu tiên có vẻ tốt và đối với tôi tôi nghĩ là tốt nhất!
indago

@JackDoureb, một điều chúng tôi đang xem xét là đi tuyến đầu tiên trong LSMB cụ thể vì chúng tôi có thể sử dụng các khung CSV đã hoạt động.
Chris Travers

6

Nếu bạn tự hỏi về cú pháp chính xác cho một loại hàng, hãy hỏi Postgres. Nó nên biết:

SELECT b FROM book b LIMIT 1;  -- or: WHERE id = 179;

Điều này sẽ trả về một đại diện văn bản của hàng của bạn ở định dạng hợp lệ:

(179,"the art of war",fiction,"{190,220}")
  • Các giá trị của các cột được biểu diễn dưới dạng một danh sách không được trích dẫn, được phân tách bằng dấu phẩy, được đặt trong các parethes.

  • Dấu ngoặc kép được sử dụng xung quanh các giá trị, nếu có thể có sự mơ hồ - bao gồm văn bản có khoảng trắng. Mặc dù trong trường hợp cụ thể này, dấu ngoặc kép xung quanh "the art of war"là tùy chọn, dấu ngoặc kép xung quanh "{190,220}"là cần thiết cho một mảng.

Đính kèm chuỗi trong dấu ngoặc đơn, sửa đổi và kiểm tra:

SELECT '(333,the art of war,fiction,"{191,220,235}")'::book

Chức năng được xem xét

Xem xét những gì chúng ta đã thảo luận dưới câu hỏi liên quan, trước đó:
Vấn đề với loại hỗn hợp trong chức năng UPSERT

Một riêng khối ( BEGIN .. END;) chỉ hữu ích nếu bạn muốn bắt EXCEPTIONmột INSERTtăng sức mạnh. Vì một khối có ngoại lệ mang một số chi phí, nên có một khối riêng biệt có thể không bao giờ được nhập:

CREATE OR REPLACE FUNCTION save_book(thebook book)
  RETURNS text AS
$BODY$
BEGIN
   UPDATE book
   SET    bname =   thebook.bname
         ,btype =   thebook.btype
         ,bprices = thebook.bprices
   WHERE  id = thebook.id;

   IF FOUND THEN
      RETURN format('Record with PK[%s] successfully updated', thebook.id);
   END IF;

   BEGIN
      INSERT INTO book SELECT (thebook).*;
      RETURN format('Record with PK[%s] successfully inserted', thebook.id);

   EXCEPTION WHEN unique_violation THEN
      UPDATE book
      SET    bname =   thebook.bname
            ,btype =   thebook.btype
            ,bprices = thebook.bprices
      WHERE  id = thebook.id;
   END;

   RETURN format('Record with PK[%s] successfully updated', thebook.id);
END
$BODY$  LANGUAGE plpgsql

Khác, đơn giản hóa :

CREATE OR REPLACE FUNCTION save_book(thebook book)
  RETURNS text AS
$BODY$
BEGIN
   UPDATE book
   SET    bname =   thebook.bname
         ,btype =   thebook.btype
         ,bprices = thebook.bprices
   WHERE  id = thebook.id;

   IF FOUND THEN
      RETURN format('Record with PK[%s] successfully updated', thebook.id);
   END IF;

   INSERT INTO book SELECT (thebook).*;
   RETURN format('Record with PK[%s] successfully inserted', thebook.id);
END
$BODY$  LANGUAGE plpgsql

Tôi cũng đơn giản hóa INSERTtuyên bố của bạn . Sẽ an toàn khi bỏ qua danh sách cột từ INSERT trong các trường hợp cụ thể.


3

Mặc dù tôi không thấy lợi thế thực sự của giải pháp của bạn, tôi có nghĩa là chuyển một hàng cho hàm thay vì chuyển các giá trị riêng lẻ như trong

CREATE OR REPLACE FUNCTION save_book2(
      integer
    , text
    , text
    , integer[]
)
RETURNS text AS
...

Dù sao, giải pháp của bạn cũng hoạt động tốt nếu bạn gọi đúng chức năng:

SELECT ave_book((179, 'the art of war', 'fiction', '{190,220}')::book);

Đó là, biểu thức bản ghi không cần trích dẫn, trong khi các giá trị văn bản và mảng bằng chữ thì có.


Tôi đã thực hiện chức năng đó để xử lý tất cả các phần chèn và cập nhật để bảo vệ ứng dụng trực tiếp xử lý các bảng, bạn có nghĩ rằng việc có chức năng đó không thêm bất kỳ lợi thế nào không?
indago

0,02 đô la của tôi về quyết định thiết kế. Tôi nghĩ rằng nó có một lợi thế lớn với điều kiện bạn có mã ứng dụng để tra cứu cấu trúc của loại liên quan và xây dựng đối số cho bạn. Điều này cung cấp cho bạn một API có thể khám phá rất hữu ích. Tuy nhiên, nếu không có điều đó, điều đó có nghĩa là nếu cấu trúc bảng của bạn thay đổi, bạn có thêm các vị trí để thực hiện thay đổi, điều này chắc chắn không tốt. Tôi nói rằng đây là một người đang thúc đẩy sự phát triển của tôi theo hướng bạn đang đi, và về tổng thể tôi nghĩ rằng đó là một ý tưởng tốt. Nó có những nguy hiểm mặc dù.
Chris Travers
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.