挨拶、レコードから列名を動的に取得したい。以下のコードに示すように、カーソルを作成し、ループを使用して、各行をレコードタイプr1にフェッチすることにより、そのカーソル内の各レコードを処理しました。私のテーブルには、これらの列があります[dlq_2000、dlq_2001、...、dlq_2017、dlq_2017]。また、各列を個別に処理するループを作成しました。
私が直面している問題は、r1からフィールド名を動的に取得することであり、コードを実行するとこのエラーが発生します。
[エラー]エラー:レコード "r1"にはフィールド "'dlq_' || counter :: text"がありませんCONTEXT:SQLステートメント "SELECT(r1。" 'dlq_' || counter :: text "= 1)"
この問題を解決する方法をアドバイスしてください。
ありがとうございました、
CREATE OR REPLACE FUNCTION update()
RETURNS VOID AS $$
DECLARE
cur SCROLL CURSOR FOR select * from my_tbl;
r1 RECORD;
begin
OPEN cur ;
FOR counter IN 2000..2017 LOOP
r1 := NULL;
LOOP
FETCH cur INTO r1;
EXIT WHEN NOT FOUND;
IF (r1."'dl_'||counter::text" = 1 ) THEN
-- do some thing
RAISE NOTICE 'processing year of : %', counter;
END IF;
END LOOP;
END LOOP;
CLOSE cur;
END;
$$
LANGUAGE plpgsql;
「変数列」をチェックするだけの簡単な方法はありません。 CASE
を使用してこの結果を達成する(あまりエレガントではない)方法があります。
CREATE OR REPLACE FUNCTION update()
RETURNS VOID AS $$
DECLARE
cur SCROLL CURSOR FOR select * from my_tbl;
r1 RECORD;
BEGIN
OPEN cur ;
FOR counter IN 2000..2017 LOOP
r1 := NULL;
LOOP
FETCH cur INTO r1;
EXIT WHEN NOT FOUND;
IF
(CASE counter
WHEN 2000 THEN r1.dl_2000
WHEN 2001 THEN r1.dl_2001
WHEN 2002 THEN r1.dl_2002
WHEN 2003 THEN r1.dl_2003
WHEN 2004 THEN r1.dl_2004
WHEN 2005 THEN r1.dl_2005
WHEN 2006 THEN r1.dl_2006
WHEN 2007 THEN r1.dl_2007
WHEN 2008 THEN r1.dl_2008
WHEN 2009 THEN r1.dl_2009
WHEN 2010 THEN r1.dl_2010
WHEN 2011 THEN r1.dl_2011
WHEN 2012 THEN r1.dl_2012
WHEN 2013 THEN r1.dl_2013
WHEN 2014 THEN r1.dl_2014
WHEN 2015 THEN r1.dl_2015
WHEN 2016 THEN r1.dl_2016
WHEN 2017 THEN r1.dl_2017
END) = 1
THEN
-- do some thing
RAISE NOTICE 'processing year of : %', counter;
END IF;
END LOOP;
END LOOP;
CLOSE cur;
END;
$$
LANGUAGE plpgsql;
すべての列dl_2000
.. dl_2017
がinteger
(またはbit
)として定義されていると想定しています。つまり、テーブル定義は次のようになります。
CREATE TABLE t
(
/* some columns */
dl_2000 integer,
dl_2001 integer,
dl_2002 integer,
dl_2003 integer,
/* ... */
dl_2017 integer,
/* more columns */
) ;
代わりに整数のARRAY
を使用できます。
CREATE TABLE t
(
/* some columns */
dl integer[],
/* more columns */
) ;
(dl
には、列と同じ方法でNOT NULL制約を設定できますが、異なる方法で記述されています。つまり、必要に応じて、CHECK (dl[2000] NOT NULL)
を設定できます。FOREIGN KEY
を設定することはできません。それらの場合の制約。
その場合、関数は次のようになります。
CREATE OR REPLACE FUNCTION update()
RETURNS VOID AS $$
DECLARE
cur SCROLL CURSOR FOR select * from my_tbl;
r1 RECORD;
BEGIN
OPEN cur ;
FOR counter IN 2000..2017 LOOP
r1 := NULL;
LOOP
FETCH cur INTO r1;
EXIT WHEN NOT FOUND;
IF dl[counter] = 1 THEN
-- do some thing
RAISE NOTICE 'processing year of : %', counter;
END IF;
END LOOP;
END LOOP;
CLOSE cur;
END;
$$
LANGUAGE plpgsql;
注:関数のロジックは変更していませんが、この方法で変更するかどうかはよくわかりません。少なくとも、2つのループを交換します。カーソル用に大きく、次に列用(または配列のインデックス用)です。
do $$
declare
r json;
i int;
begin
for r in
select row_to_json(t.*)
from (values(1,'a1','a2','a3'),(2,'b1','b2','b3')) as t(x,y11,y12,y13)
loop
raise info '%', r;
for i in 11..13 loop
if r->>('y'||i) like '%2' then -- Condition here
raise info 'Do something for %', r->>('y'||i);
end if;
end loop;
end loop;
end $$;
情報:{"x":1、 "y11": "a1"、 "y12": "a2"、 "y13": "a3"} 情報:a2 情報:{"x":2、 "y11": "b1"、 "y12": "b2"、 "y13": "b3"} 情報:b2
配列コンストラクタ の使用:
do $$
declare
r record;
i int;
begin
for r in
select x, array[y11,y12,y13] as y
from (values(1,'a1','a2','a3'),(2,'b1','b2','b3')) as t(x,y11,y12,y13)
loop
raise info '%', r;
for i in 1..3 loop
if r.y[i] like '%2' then -- Condition here
raise info 'Do something for %', r.y[i];
end if;
end loop;
end loop;
end $$;
情報:(1、 "{a1、a2、a3}") 情報:a2に対して何かを行う 情報:(2、 "{b1、b2、b3}" ) 情報:b2に対して何かを行います
そして data normalization を使用します:
do $$
declare
r record;
i int;
begin
for r in
with
test as (
select *
from (values(1,'a1','a2','a3'),(2,'b1','b2','b3')) as t(x,y11,y12,y13)),
norm as (
select *, unnest(array[y11,y12,y13]) as y, unnest(array[11,12,13]) as z from test)
select * from norm
where y like '%2' -- Condition here
loop
raise info 'Do something for %', r;
end loop;
end $$;
情報:(1、a1、a2、a3、a2,12)に対して何かを行う 情報:(2、b1、b2、b3、b2,12)に対して何かを行う
より単純な例で、より複雑なタスクでどのように実行できるかを示します。
しかし、それはPostgreSQL 9.5でテストされました
@joanoloのつま先を踏むべきではありませんが、私があなたが何を求めているのか理解しているのであれば、ここに答えにアプローチする別の方法があります。データを別の方法で保存するとクエリが簡単になることに同意します。
次のようなデータがあるとします。
CREATE TABLE my_tbl ( id serial primary key, dl_2000 integer, dl_2001 integer, dl_2002 integer, dl_2003 integer, dl_2017 integer); insert into my_tbl (dl_2000,dl_2001,dl_2002,dl_2003,dl_2017) values (1,null,null,null,1), (1,null,1,null,null), (null,1,null,null,null), (2,4,5,6,7);
一致するかどうか動的に列をクエリできます。
DO $$ DECLARE rec RECORD; v_col_id TEXT; BEGIN FOR rec IN SELECT column_name,array_to_string(regexp_matches(column_name,'[0-9]+$'),',')::integer AS year FROM information_schema.columns WHERE table_name = 'my_tbl' LOOP EXECUTE FORMAT('SELECT id FROM my_tbl WHERE %I = 1', rec.column_name) INTO v_col_id; IF ( v_col_id IS NOT NULL) THEN RAISE NOTICE 'Column % for year %, with id of % matched.', rec.column_name, rec.year, v_col_id; END IF; END LOOP; END; $$;
デモのために、匿名のplpgsql関数を使用しました。名前付き関数として簡単に書くことができます。