Gob*_*0st 7 postgresql stored-procedures cte plpgsql
--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');
Run Code Online (Sandbox Code Playgroud)
当我试图从我感兴趣的站点的消息表中查找所有消息时,我写了一个 CTE 并且它工作正常,假设我对站点 11111 和 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
Run Code Online (Sandbox Code Playgroud)
结果如预期:
"1";1;"aaa"
"2";1;"aaa"
"3";1;"aaa"
"4";1;"aaa"
"5";2;"aaa"
"6";2;"aaa"
"7";5;"aaa"
"8";8;"aaa"
Run Code Online (Sandbox Code Playgroud)
但是当我将整个事情包装在一个函数中时,它返回了错误的行。你能帮我看看为什么吗?
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;
Run Code Online (Sandbox Code Playgroud)
使用该功能时:
select * from getMessageFromSites('11111,22222');
select * from getMessageFromSites('1')
select * from getMessageFromSites('33333')
Run Code Online (Sandbox Code Playgroud)
它总是在多行下方返回相同的结果,但显然是错误的行,为什么?你能帮忙吗?
"1";1;"aaa"
"2";1;"aaa"
"3";1;"aaa"
"4";1;"aaa"
"5";2;"aaa"
"6";2;"aaa"
"9";11;"aaa"
"10";11;"bbb"
Run Code Online (Sandbox Code Playgroud)
感谢@a_horse_with_no_name,现在我有两种可行的解决方案,一种使用 sql,一种使用 pl/pgsql:
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;
Run Code Online (Sandbox Code Playgroud)
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;
Run Code Online (Sandbox Code Playgroud)
代码测试
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');
Run Code Online (Sandbox Code Playgroud)
两个 PG 存储过程都按预期工作!
解决方案 3:一个更好的简化解决方案,请参见下面 Erwin 的回答。
现已结案!
我认为这是因为您只从查询结果中返回第一行。
该select ... into ...
会只检索一行和query select * from result
只返回单个记录:
您也不需要 PL/pgSQL 函数,一个普通的 SQL 函数就可以正常工作:
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;
Run Code Online (Sandbox Code Playgroud)
请注意,CTE 内部的 order by 并不是很有用。您必须对最终选择进行排序,而不是对中间步骤进行排序。
如果你确实需要 PL/pgSQL 因为你在函数中做了更多的事情,你应该简单地将它更改为:
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;
Run Code Online (Sandbox Code Playgroud)
你写了“case close”,但我会重新打开。错的太多了……
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');
Run Code Online (Sandbox Code Playgroud)
简化名称以提高可读性。
不要使用非描述性的列名,如entity_id
. 替换为有用的名称。
对具有相同内容的列使用相同的名称是一种很好的做法。使用patient_id
在FK列messageq
。
如果您varchar
的消息队列中确实有一个PK,请使用实际varchar
值进行测试。
简化INSERT
语句。
在 上添加索引messageq.patient_id
。这对性能至关重要。
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;
Run Code Online (Sandbox Code Playgroud)
是的,仅此而已。
称呼:
SELECT * FROM f_get_msg_from_sites(11111, 44444);
SELECT * FROM f_get_msg_from_sites(22222);
Run Code Online (Sandbox Code Playgroud)
SQL Fiddle(在 pg9.2 上,pg9.3 已重载)
不需要 CTE(甚至两个)。在这里会浪费代码和时间。一个带有连接的简单查询就可以完成这项工作。
使用VARIADIC
参数进行更简单的调用(可选)。
有关的:
如果(patient_id int, site_held_at)
在 table 中是唯一的patient
,则DISTINCT
在查询中不需要。否则添加它。
归档时间: |
|
查看次数: |
10643 次 |
最近记录: |