PostgresSQL thực thi động với các giá trị đối số trong mảng


8

Tôi tự hỏi nếu điều này là có thể trong Postgres:

Giải thích tốt nhất bằng cách sử dụng một ví dụ giả định:

create or replace function test_function(filter_param1 varchar default null
                                       , filter_param2 varchar default null)
  returns integer as
$$ 
declare
  stmt text;
  args varchar[];
  wher varchar[];
  retid integer;
begin
  if filter_param1 is not null then 
    array_append(args, filter_param1);
    array_append(wher, 'parameter_name = $1');
  end if;
  if filter_param2 is not null then 
    array_append(args, filter_param2);
    array_append(wher, 'parameter_name = $2');
  end if;

  stmt := 'select id from mytable where ' || array_to_string(wher, ' or ');
  execute stmt into retid using args;

  return retid;
end;
$$ language plpgsql;

Trong Python có *args- có lẽ PostgreSQL có cơ chế tương tự?

EDIT cho câu hỏi Erwin Brandstetter:

  • Tất cả các filtertham số sẽ được áp dụng cho các cột khác nhau, nhưng nên được AND'ed.
  • Trở về setofcó ý nghĩa hơn nhiều ở đây.
  • Tất cả các tham số có thể cùng loại cột (ví dụ. varchar).

Không, EXECUTE ... USINGkhông thể lấy một loạt các đối số, vì các mảng PostgreSQL không hỗ trợ các kiểu hetrogenous. Chúng phải là một mảng của một loại cụ thể và SQL có thể không có cùng loại cho mỗi tham số. Nó sẽ vô dụng trừ những trường hợp sử dụng rất hẹp. Thay vào đó, USINGsẽ phải có thể lấy một bản ghi ẩn danh, giống như bạn tạo từ một nhà ROW(...)xây dựng ... điều này có thể có thể, nhưng hiện không được thực hiện.
Craig Ringer

Nó được giải thích tốt nhất với một ví dụ cộng với một lời giải thích thực tế . Bạn có định áp dụng tất cả các tham số cho cùng một cột parameter_namekhông? (Điều đó có thể được đơn giản hóa phần lớn.) Hoặc bạn đang thực sự nghĩ về các cột khác nhau? Bạn có muốn trả về một giá trị đơn hoặc một tập hợp các giá trị không? Một loạt các vị từ OR'd thường trả về nhiều hàng, không phù hợp với RETURNSmệnh đề của bạn .
Erwin Brandstetter

Câu trả lời:


6

Dù bằng cách nào, điều đó hoàn toàn có thể , với điều kiện là tất cả các tham số của bạn đều thuộc cùng một loại dữ liệu .

EXECUTE ... USINGvui vẻ nhận một mảng, được coi là một đối số duy nhất . Các phần tử truy cập với các mảng con.

create or replace function test_function(_filter1 text = null
                                       , _filter2 text = null
                                       , OUT retid int) as
$func$
declare
   _args text[] := ARRAY[_filter1, _filter2];
   _wher text[];
begin
   if _filter1 is not null then 
      _wher := _wher || 'parameter_name = $1[1]'; -- note array subscript
   end if;

   if _filter2 is not null then 
      _wher := _wher || 'parameter_name = $1[2]'; -- assign the result!
   end if;

   IF _args  IS NULL         -- check whether all params are NULL
      RAISE EXCEPTION 'At least one parameter required!';
   END IF;

   execute 'select id from mytable where ' -- cover case with all params NULL
         || array_to_string(_wher, ' or ')
         || ' ORDER BY id LIMIT 1';   -- For a single value (???)
   into  retid
   using _args;
end
$func$  language plpgsql;

Đây chỉ là một bằng chứng về khái niệm và phức tạp không cần thiết. Nó sẽ là một tùy chọn thú vị cho đầu vào mảng thực tế , ví dụ với một VARIADIChàm .

Đối với trường hợp trong tầm tay, sử dụng thay thế:

CREATE OR REPLACE FUNCTION test_function(_filter1 text = null
                                       , _filter2 text = null)
  RETURNS SETOF int AS
$func$
DECLARE
   _wher text := concat_ws(' OR '
             , CASE WHEN _filter1 IS NOT NULL THEN 'parameter_name = $1' END
             , CASE WHEN _filter2 IS NOT NULL THEN 'parameter_name = $2' END);
BEGIN
   IF _wher = ''   -- check whether all params are NULL
      RAISE EXCEPTION 'At least one parameter required!';
   END IF;

   RETURN QUERY EXECUTE 'SELECT id FROM mytable WHERE ' || _wher
   USING  $1, $2;
   -- USING  _filter1 , filter2; -- alternatively use func param names
END
$func$  LANGUAGE plpgsql;

Giải thích

  • Liệt kê tất cả các giá trị có thể có thể được tham chiếu trong truy vấn động trong USINGmệnh đề theo thứ tự xuất hiện của chúng. Nếu không phải tất cả chúng sẽ được tham chiếu trong truy vấn động, thì không có hại gì trong đó. Nhưng chúng ta cần giữ nguyên vị trí thứ tự .

  • Đặc biệt lưu ý rằng bên trong các tham chiếu truy vấn động đã cho các giá trị của mệnh đề theo số thứ tự, trong khi trong các tham số hàm tham chiếu mệnh đề . Cùng một cú pháp, phạm vi khác nhau! Trong ví dụ của tôi tham khảo cho đơn giản. Nhưng người ta có thể sắp xếp lại các giá trị trong mệnh đề theo bất kỳ cách nào, sao cho (ví dụ) trong tham chiếu truy vấn động ở vị trí thứ 2 trong mệnh đề, tham chiếu tham số hàm số 1.$n USING$n USING
    $2$2USING$2$1USING

  • Điều này cho phép bất kỳ số lượng tham số với bất kỳ loại dữ liệu (không đồng nhất) .

  • Trả về một tập hợp số nguyên trong ví dụ này ( RETURNS SETOF int), phù hợp hơn với ví dụ - sử dụng RETURN QUERY EXECUTEtương ứng.

  • concat_ws() đặc biệt tiện dụng để tập hợp một danh sách các vị từ OR'ed hoặc AND'ed một cách có điều kiện.


1
cảm ơn - thậm chí tốt hơn! thật thú vị khi pg không phàn nàn về các đối số thặng dư trong ví dụ thứ 2 - nếu một bộ lọc được chỉ định.
Richard

@Richard: Các đối số được cung cấp trong USINGmệnh đề có thể được tham chiếu 0-n lần. Nó không chính xác giống như *argstrong python, nó thực sự linh hoạt hơn.
Erwin Brandstetter

-2
RETURN QUERY EXECUTE SELECT
        TotalBookingAmount,
        TotalQuantity,
        TotalActualWeight,
        TotalChargedWeight,
        TotalLR,
        TotalDeliveryAmount,
        GrandTotalAmount,
        waybilltypeid,
        bookingmonth,
        bookingyear,
        sourcebranchid, 
        destinationbranchid 
                FROM '|| tableName || '
            where 
                accountgroupid          = ' || accountGropIdCol     :: numeric || '
            AND sourcebranchid          = ' || SourceBranchIdCol    :: numeric || '
            AND bookingmonth            = ' || bookingMonthCol      :: numeric || '
            AND bookingyear             = ' || bookingYearCol       :: numeric || '
            AND destinationbranchid     =  any (string_to_array(' || DestinationBranchIdCol ||',' || ',' || ')' :: numeric[] ||')';

Chào mừng bạn đến với Quản trị viên Cơ sở dữ liệu ! Vui lòng giải thích cách truy vấn của bạn giải quyết vấn đề của tác giả; câu trả lời mà không giải thích thường không nhận được tốt.
Glorfindel

1
Câu trả lời này không liên quan gì đến câu hỏi. có lẽ bạn đã đăng sai trang?
Richard
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.