--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');
Als ich versucht habe, alle Nachrichten aus der Nachrichtentabelle in der Site zu finden, die mich interessiert, habe ich einen CTE geschrieben und er funktioniert einwandfrei. Nehmen wir an, ich interessiere mich für die Site 11111 und 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
Das Ergebnis ist wie erwartet:
"1";1;"aaa"
"2";1;"aaa"
"3";1;"aaa"
"4";1;"aaa"
"5";2;"aaa"
"6";2;"aaa"
"7";5;"aaa"
"8";8;"aaa"
Aber wenn ich das Ganze in eine Funktion einbinde, werden die falschen Zeilen zurückgegeben. Kannst du mir helfen zu verstehen warum?
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;
Bei Verwendung der Funktion:
select * from getMessageFromSites('11111,22222');
select * from getMessageFromSites('1')
select * from getMessageFromSites('33333')
es gibt immer das gleiche Ergebnis unterhalb mehrerer Zeilen aber offensichtlich falsch Zeilen, warum? kannst du hier helfen
"1";1;"aaa"
"2";1;"aaa"
"3";1;"aaa"
"4";1;"aaa"
"5";2;"aaa"
"6";2;"aaa"
"9";11;"aaa"
"10";11;"bbb"
Lösung
Dank @a_horse_with_no_name habe ich jetzt zwei funktionierende Lösungen, eine mit SQL, eine mit pl / pgsql:
Lösung 1 (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;
Lösung 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;
Testen des Codes
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');
Beide gespeicherten PG-Prozeduren funktionieren wie erwartet!
Lösung 3: Eine besser vereinfachte Lösung siehe Erwins Antwort unten.
Jetzt Fall geschlossen!
quelle
Antworten:
Ich denke, das liegt daran, dass Sie immer nur die erste Zeile aus dem Ergebnis der Abfrage zurückgeben.
Der
select ... into ...
ruft nur eine Zeile ab undquery select * from result
gibt nur diesen einzelnen Datensatz zurück:Sie benötigen auch keine PL / pgSQL-Funktion, eine einfache SQL-Funktion funktioniert einwandfrei:
Beachten Sie, dass die Reihenfolge innerhalb des CTE nicht wirklich nützlich ist. Sie müssen die endgültige Auswahl sortieren, nicht die Zwischenschritte.
Wenn Sie PL / pgSQL benötigen, weil Sie mehr Dinge in der Funktion tun, sollten Sie es einfach ändern in:
quelle
Sie haben "Fall geschlossen" geschrieben, aber ich werde wieder öffnen. Es ist einfach zu viel schief gelaufen ...
Datenbankdesign und Testeinstellung
Hauptpunkte
Vereinfachen Sie Namen für eine bessere Lesbarkeit.
Verwenden Sie keine nicht beschreibenden Spaltennamen wie
entity_id
. Durch nützliche Namen ersetzt.Es wird empfohlen, für Spalten mit identischem Inhalt denselben Namen zu verwenden. Verwenden
patient_id
für die FK-Spalte inmessageq
.Wenn Sie tatsächlich eine
varchar
PK in Ihrer Nachrichtenwarteschlange haben, testen Sie mit tatsächlichenvarchar
Werten.Vereinfachen Sie
INSERT
Anweisungen.Fügen Sie einen Index hinzu
messageq.patient_id
. Dies ist entscheidend für die Leistung.Funktion
Ja das ist alles
Anruf:
SQL Fiddle (auf Seite 9.2 ist Seite 9.3 überladen)
Hauptpunkte
Kein CTE (oder sogar zwei) erforderlich. Das wäre hier eine Verschwendung von Code und Zeit. Eine einfache Abfrage mit Joins erledigt den Job.
Verwendung eines
VARIADIC
Parameters für einfachere Aufrufe (optional).Verbunden:
Wenn
(patient_id int, site_held_at)
in der Tabelle eindeutig istpatient
, benötigen SieDISTINCT
in der Abfrage nicht. Sonst füge es hinzu.quelle
SELECT m.* FROM patient p JOIN messageq m ON m.patient_id = p.patient_id ...
.