CTE hoạt động như mong đợi, nhưng không phải khi được gói vào một chức năng


7
--testing table
CREATE TABLE public.test_patient_table (
                entity_id INTEGER NOT NULL,
                site_held_at INTEGER NOT NULL,
                CONSTRAINT entityid_pk PRIMARY KEY (entity_id)
);

CREATE TABLE public.test_messageq_table (
                entity_id VARCHAR NOT NULL,
                master_id INTEGER NOT NULL,
                message_body VARCHAR NOT NULL,
                CONSTRAINT mq_entity_id_pk PRIMARY KEY (entity_id)
);

CREATE INDEX test_patient_table_siteid_idx
 ON public.test_patient_table
 ( site_held_at );

ALTER TABLE public.test_messageq_table
ADD CONSTRAINT test_patient_table_test_messageq_table_fk
FOREIGN KEY (master_id)
REFERENCES public.test_patient_table (entity_id)
ON DELETE NO ACTION
ON UPDATE NO ACTION
NOT DEFERRABLE;

--test patient data
insert into test_patient_table values (1, 11111);
insert into test_patient_table values (2, 11111);
insert into test_patient_table values (3, 11111);
insert into test_patient_table values (4, 11111);

insert into test_patient_table values (5, 22222);
insert into test_patient_table values (6, 22222);
insert into test_patient_table values (7, 22222);
insert into test_patient_table values (8, 22222);

insert into test_patient_table values (9, 33333);
insert into test_patient_table values (10, 33333);

insert into test_patient_table values (11, 44444);

--testing message
insert into test_messageq_table values (1, 1, 'aaa');
insert into test_messageq_table values (2, 1, 'aaa');
insert into test_messageq_table values (3, 1, 'aaa');
insert into test_messageq_table values (4, 1, 'aaa');
insert into test_messageq_table values (5, 2, 'aaa');
insert into test_messageq_table values (6, 2, 'aaa');
insert into test_messageq_table values (7, 5, 'aaa');
insert into test_messageq_table values (8, 8, 'aaa');
insert into test_messageq_table values (9, 11, 'aaa');
insert into test_messageq_table values (10, 11, 'bbb');    

Khi tôi cố gắng tìm tất cả các tin nhắn từ bảng tin nhắn trong trang web mà tôi quan tâm, tôi đã viết một CTE và nó hoạt động tốt, giả sử tôi quan tâm đến trang 1111 và 22222:

WITH patient_msg_in_branches AS (
    select distinct test_messageq_table.master_id AS patient_id,
    test_patient_table.site_held_at as site_id
    from test_messageq_table 
    inner join test_patient_table 
    ON test_messageq_table.master_id = test_patient_table.entity_id 
    and site_held_at in (11111,22222) order by patient_id
),
messages_for_patients AS(
    select * from test_messageq_table where master_id in 
        (select patient_msg_in_branches.patient_id 
            from patient_msg_in_branches)
)select * from messages_for_patients

Kết quả như mong đợi:

"1";1;"aaa"
"2";1;"aaa"
"3";1;"aaa"
"4";1;"aaa"
"5";2;"aaa"
"6";2;"aaa"
"7";5;"aaa"
"8";8;"aaa"

Nhưng khi tôi bọc toàn bộ trong một hàm, nó sẽ trả về các hàng sai. Bạn có thể giúp tôi xem tại sao?

drop function getMessageFromSites(text);
CREATE OR REPLACE FUNCTION getMessageFromSites(IN ids TEXT) RETURNS 
setof test_messageq_table AS $$ 
DECLARE
       sites INT[];
       result test_messageq_table%rowtype;

BEGIN
       sites = string_to_array(ids,',');
        raise info 'entire array: %', sites;

WITH patient_msg_in_branches AS (
    select distinct test_messageq_table.master_id AS patient_id,
    test_patient_table.site_held_at as site_id
    from test_messageq_table 
    inner join test_patient_table 
    ON test_messageq_table.master_id = test_patient_table.entity_id 
    and site_held_at = ANY(sites) order by patient_id
),
messages_for_patients AS(
    select * from test_messageq_table where master_id in 
        (select patient_msg_in_branches.patient_id 
            from patient_msg_in_branches)
)select * into result from messages_for_patients;
return query select * from result;
END;     
$$ LANGUAGE plpgsql;

Khi sử dụng chức năng:

select * from getMessageFromSites('11111,22222');
select * from getMessageFromSites('1')
select * from getMessageFromSites('33333')

nó luôn trả về cùng một kết quả bên dưới nhiều hàng nhưng rõ ràng là sai hàng, tại sao? bạn có thể giúp gì ở đây không?

"1";1;"aaa"
"2";1;"aaa"
"3";1;"aaa"
"4";1;"aaa"
"5";2;"aaa"
"6";2;"aaa"
"9";11;"aaa"
"10";11;"bbb"

Giải pháp

Cảm ơn @a_horse_with_no_name, bây giờ tôi có hai giải pháp hoạt động, một với sql, một với pl / pssql:

Giải pháp 1 (pl / pssql)

CREATE OR REPLACE FUNCTION getMessageFromSites(IN ids TEXT) RETURNS 
setof test_messageq_table AS $$ 
DECLARE
       sites INT[];
       result test_messageq_table%rowtype;

BEGIN
       sites = string_to_array(ids,',');
       raise info 'entire array: %', sites;
 return QUERY

    WITH patient_msg_in_branches AS (
        select distinct test_messageq_table.master_id AS patient_id,
        test_patient_table.site_held_at as site_id
        from test_messageq_table 
        inner join test_patient_table 
        ON test_messageq_table.master_id = test_patient_table.entity_id 
        and site_held_at = ANY(sites) order by patient_id
    ),
    messages_for_patients AS(
        select * from test_messageq_table where master_id in 
            (select patient_msg_in_branches.patient_id 
                from patient_msg_in_branches)
    )
    select * from messages_for_patients;

END;     
$$ LANGUAGE plpgsql;

Giải pháp 2 (sql)

CREATE OR REPLACE FUNCTION getMessageFromSites2(ids TEXT) RETURNS 
   setof test_messageq_table 
AS 
$$ 
  WITH patient_msg_in_branches AS (
      select distinct test_messageq_table.master_id AS patient_id,
             test_patient_table.site_held_at as site_id
      from test_messageq_table 
      join test_patient_table ON test_messageq_table.master_id = test_patient_table.entity_id 
                                and site_held_at = ANY (string_to_array($1,',')::int[]) 
  ),
  messages_for_patients AS
  (
    select * 
    from test_messageq_table 
    where master_id in (select patient_msg_in_branches.patient_id 
                        from patient_msg_in_branches)
  )
  select * 
  from messages_for_patients;
$$ 
LANGUAGE sql;

Kiểm tra mã

select * from getMessageFromSites('11111,44444');
select * from getMessageFromSites('22222');
select * from getMessageFromSites('1')
select * from getMessageFromSites('33333')

select * from getMessageFromSites2('11111');
select * from getMessageFromSites2('22222');
select * from getMessageFromSites2('33333');
select * from getMessageFromSites('44444,11111');
select * from getMessageFromSites('1');

Cả hai thủ tục lưu trữ PG đang làm việc như mong đợi!

Giải pháp 3: Một giải pháp đơn giản hóa tốt hơn xem từ câu trả lời của Erwin bên dưới.

Bây giờ trường hợp đóng cửa!


Đó là một chức năng không phải là SP
James Anderson

3
@JamesAnderson: Postgres không có thủ tục lưu trữ thực sự. Vì vậy, trong thế giới Postgres, thuật ngữ "thủ tục" thường được sử dụng làm từ đồng nghĩa với "hàm"
a_horse_with_no_name

Ah không thấy thẻ Postgres. Sai lầm của tôi
James Anderson

Câu trả lời:


8

Tôi nghĩ điều này là do bạn chỉ trả về hàng đầu tiên từ kết quả của truy vấn.

Các select ... into ...sẽ chỉ lấy một hàng và query select * from resultlợi nhuận duy nhất mà hồ sơ duy nhất:

Bạn cũng không cần một hàm PL / pgSQL, một hàm SQL đơn giản sẽ hoạt động tốt:

CREATE OR REPLACE FUNCTION getMessageFromSites(ids TEXT) RETURNS 
   setof test_messageq_table 
AS 
$$ 
  WITH patient_msg_in_branches AS (
      select distinct test_messageq_table.master_id AS patient_id,
             test_patient_table.site_held_at as site_id
      from test_messageq_table 
         join test_patient_table ON test_messageq_table.master_id = test_patient_table.entity_id 
                                and site_held_at = ANY (string_to_array(ids,',')::int[]) 
  ),
  messages_for_patients AS
  (
    select * 
    from test_messageq_table 
    where master_id in (select patient_msg_in_branches.patient_id 
                        from patient_msg_in_branches)
  )
  select * 
  from messages_for_patients;
$$ 
LANGUAGE sql;

Lưu ý rằng thứ tự bên trong CTE không thực sự hữu ích. Bạn phải sắp xếp lựa chọn cuối cùng, không phải các bước trung gian.

Nếu bạn cần PL / pgSQL vì bạn đang làm nhiều thứ hơn trong hàm, bạn chỉ cần thay đổi nó thành:

begin
  ....
  return query
    WITH patient_msg_in_branches AS (
        select distinct test_messageq_table.master_id AS patient_id,
        test_patient_table.site_held_at as site_id
        from test_messageq_table 
        inner join test_patient_table 
        ON test_messageq_table.master_id = test_patient_table.entity_id 
        and site_held_at = ANY(sites) order by patient_id
    ),
    messages_for_patients AS(
        select * from test_messageq_table where master_id in 
            (select patient_msg_in_branches.patient_id 
                from patient_msg_in_branches)
    )
    select * from messages_for_patients;
end;

6

Bạn đã viết "trường hợp đóng cửa", nhưng tôi sẽ mở lại. Có quá nhiều sai lầm ...

Thiết kế cơ sở dữ liệu và thiết lập thử nghiệm

CREATE TABLE patient (
   patient_id   int PRIMARY KEY
 , site_held_at int NOT NULL
);

CREATE TABLE messageq (
   messageq_id  varchar PRIMARY KEY  -- varchar ?!
 , patient_id   int NOT NULL REFERENCES patient
 , message_body varchar NOT NULL
);

CREATE INDEX patient_site_idx ON patient(site_held_at);
CREATE INDEX messageq_patient_id_idx ON patient(patient_id); -- !!

INSERT INTO patient VALUES
  (1, 11111)
, (2, 11111)
, (3, 11111)
, (4, 11111)
, (5, 22222)
, (6, 22222)
, (7, 22222)
, (8, 22222)
, (9, 33333)
, (10, 33333)
, (11, 44444);

INSERT INTO messageq VALUES
  ('m1', 1, 'aaa1')
, ('m2', 1, 'aaa2')
, ('m3', 1, 'aaa3')
, ('m4', 1, 'aaa4')
, ('m5', 2, 'aaa5')
, ('m6', 2, 'aaa6')
, ('m7', 5, 'aaa7')
, ('m8', 8, 'aaa8')
, ('m9', 11, 'aaa9')
, ('m10', 11, 'bbb10');

Những điểm chính

  • Đơn giản hóa tên để dễ đọc hơn.

  • Đừng sử dụng tên cột không mô tả như thế nào entity_id. Thay thế bằng tên hữu ích.

  • Đó là cách tốt để sử dụng cùng tên cho các cột có nội dung giống hệt nhau. Sử dụng patient_idcho cột FK trong messageq.

  • Nếu bạn thực sự có varcharPK trong hàng đợi tin nhắn của mình, hãy kiểm tra với các varchargiá trị thực tế .

  • Đơn giản hóa các INSERTcâu lệnh.

  • Thêm một chỉ mục trênmessageq.patient_id . Điều này là rất quan trọng cho hiệu suất.

Chức năng

CREATE OR REPLACE FUNCTION f_get_msg_from_sites(VARIADIC _id int[])
  RETURNS SETOF messageq AS 
$func$ 
   SELECT m.*
   FROM   patient  p
   JOIN   messageq m USING (patient_id)
   WHERE  p.site_held_at = ANY($1)
$func$ LANGUAGE sql;

Vâng đó là tất cả.
Gọi:

SELECT * FROM f_get_msg_from_sites(11111, 44444);
SELECT * FROM f_get_msg_from_sites(22222);

SQL Fiddle (trên pg9.2, pg9.3 bị quá tải)

Những điểm chính

  • Không cần CTE (hoặc thậm chí hai). Đó sẽ là một sự lãng phí mã và thời gian ở đây. Một truy vấn đơn giản với tham gia thực hiện công việc.

  • Sử dụng một VARIADICtham số cho các cuộc gọi đơn giản hơn (tùy chọn).
    Liên quan:

  • Nếu (patient_id int, site_held_at)là duy nhất trong bảng patient, bạn không cần DISTINCTtrong truy vấn. Khác thêm nó.


Cảm ơn Erwin cho tất cả những điểm tốt. Nhưng tôi đã nhận thấy khi sử dụng THAM GIA SỬ DỤNG, nó cho tôi thấy kết quả khác biệt như mong đợi trong khi tôi sử dụng THAM GIA (CHỌN tin nhắn. * TỪ bệnh nhân p THAM GIA messageq trên messageq.patient_id = messageq.patient_id). nó hiển thị kết quả trùng lặp trên 110 bản ghi. Đây có phải là mong đợi? Cả hai trường hợp không sử dụng DISTINCT với cùng một dữ liệu.
Yêu tinh

@ Gob00st: Điều kiện THAM GIA của bạn là sai, sẽ phải như vậy SELECT m.* FROM patient p JOIN messageq m ON m.patient_id = p.patient_id ....
Erwin Brandstetter

Xin lỗi, là lỗi của tôi !
Yêu tinh
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.