1

基本的に、列名をいくつかの制限付きで格納するテーブルとinfos、それらの列の値を格納する別のテーブルがあります: info_datainfosからの列と からのデータを持つテーブルを取得したいinfo_data。クロス集計機能を試してみましたが、期待した効果がありません。

私は2つのテーブルを持っています:

CREATE TABLE infos
(id serial PRIMARY KEY,
 name text NOT NULL,
 id_member integer NOT NULL,
 title text,
 min_length integer NOT NULL DEFAULT 0,
 max_length integer NOT NULL DEFAULT 30,
 required boolean NOT NULL DEFAULT false,
 type text NOT NULL DEFAULT 'text'::text
);

CREATE INDEX info_id_idx ON infos (id);

CREATE TABLE info_data
(id serial PRIMARY KEY,
 id_info integer,
 value text,
  CONSTRAINT info_data_id_info_fkey FOREIGN KEY (id_info)
      REFERENCES infos (id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION
);

CREATE INDEX info_data_id_idx ON info_data(id);

次の値を使用します。

情報:

COPY infos (id, name, id_member, title, min_length, max_length, required, type)     FROM     stdin;
1 nume 1 Nume 0 30 t text
2 prenume 1 Prenume 0 30 t text
3 cnp 1 C.N.P. 13 13 t number
4 nume anterior 1 Nume anterior 0 30 f text
5 stare civila 1 Starea civila 0 30 f text
6 cetatenie 1 Cetatenie 0 30 f text
7 rezidenta 1 Rezidenta 0 30 f text
9 tip act 1 C.I. / B.I. 0 10 t text
10 serie ci 1 Serie C.I. / B.I. 0 30 t text
11 numar ci 1 Numar C.I. / B.I. 0 30 t text
12 data eliberarii 1 Data eliberarii 0 30 t text
13 eliberat de 1 Eliberat de 0 30 t text
8 adresa 1 Adresa 0 50 f text
\.

情報データ:

COPY info_data (id, id_info, value) FROM stdin;
1 1 a
2 2 a
3 3 100
4 4
5 5
6 6
7 7
8 8
9 9 ci
10 10 sv
11 11 13
12 12 132
13 13 123
14 1 b
15 2 b
16 3 100
17 4
18 5
19 6
20 7
21 8
22 9 BI
23 10 XT
24 11 123
25 12 10
26 13 10
\.

質問: どうすればこの出力を達成できますか? infos(テーブルからの一意のエントリに基づいて列を生成する必要があります

nume, prenume, cnp, nume anterior, ... (as columns - built from infos)
a   , a, ...
b   , b, ... (as rows - built from info_data)
4

4 に答える 4

1

この質問は、あなたが予想していたよりも解決するのが非常に困難でした。でのあなたの試みcrosstab()は、正しい方向を目指していました。ただし、動的な列名を割り当てるには、さらに動的 SQL が必要です: EXECUTEplpgsql 関数で。

SQL インジェクションやその他のエラーを防ぐために、列のデータ型をinfos.typeからtextに変更します。regtypeたとえばnumber、有効な PostgreSQL データ型ではない data type があります。に置き換えたnumericので、動作します。

二重引用符が必要な列名を避けることで、タスクを簡素化できます。のようnume_anterior"nume anterior"

row_idテーブルに列を追加してinfo_data、1 つの行のすべての要素をマークすることができます。関数に必要であり、値crosstab()を持つ列を無視できますNULL。2 つのcrosstab()パラメーターを持つ関数は、欠落している列を処理できます。不足している列を以下の式で合成(d.id-1)/13します。これは、例のデータに対して機能します。

追加のモジュール tablefuncをインストールする必要があります (データベースごとに 1 回):

CREATE EXTENSION tablefunc;

この関連する回答で追加の説明とリンクを見つけてください。

この関数は、探していることを行います:

CREATE OR REPLACE FUNCTION f_mytbl()
  RETURNS TABLE (id int
, nume text           , prenume text       , cnp numeric
, "nume anterior" text, "stare civila" text, cetatenie text
, rezidenta text      , adresa text        , "tip act" text
, "serie ci" text     , "numar ci" text    , "data eliberarii" text
, "eliberat de" text)
  LANGUAGE plpgsql AS
$BODY$
BEGIN

RETURN QUERY EXECUTE $f$
SELECT *
FROM   crosstab(
    'SELECT (d.id-1)/13 -- AS row_id
          , i.id, d.value
     FROM   infos i
     JOIN   info_data d ON d.id_info = i.id
     ORDER  BY 1, i.id',

    'SELECT id
     FROM   infos
     ORDER  BY id'
    )
AS tbl ($f$ || 'id int,
, nume text           , prenume text       , cnp numeric
, "nume anterior" text, "stare civila" text, cetatenie text
, rezidenta text      , adresa text        , "tip act" text
, "serie ci" text     , "numar ci" text    , "data eliberarii" text
, "eliberat de" text)';

END;
$BODY$;

電話:

SELECT * FROM x.mytbl();

ネストされたドル引用符に混乱しないでください。

ところで:次のステートメントで列リストを作成しました:

SELECT 'id int,' || string_agg(quote_ident(name) || ' ' || type
                              ,', ' ORDER BY i.id) 
FROM   infos i;
于 2012-05-27T21:29:22.030 に答える
0

私はあなたがこのようなことをすることができると思います

SELECT 
    C1.VALUE AS (SELECT NAME FROM INFOS WHERE ID=1),  
    C2.VALUE AS (SELECT NAME FROM INFOS WHERE ID=2),  
    C3.VALUE AS (SELECT NAME FROM INFOS WHERE ID=3),  
    C4.VALUE AS (SELECT NAME FROM INFOS WHERE ID=4),  
    C5.VALUE AS (SELECT NAME FROM INFOS WHERE ID=5),  
    C6.VALUE AS (SELECT NAME FROM INFOS WHERE ID=6),  
    C7.VALUE AS (SELECT NAME FROM INFOS WHERE ID=7),  
    C8.VALUE AS (SELECT NAME FROM INFOS WHERE ID=8),  
    C9.VALUE AS (SELECT NAME FROM INFOS WHERE ID=9),  
    C10.VALUE AS (SELECT NAME FROM INFOS WHERE ID=10),  
    C11.VALUE AS (SELECT NAME FROM INFOS WHERE ID=11),  
    C12.VALUE AS (SELECT NAME FROM INFOS WHERE ID=12),  
    C13.VALUE AS (SELECT NAME FROM INFOS WHERE ID=13)  
    FROM (
    (SELECT VALUE FROM INFO_DATA WHERE ID_INFO=1) C1,
    (SELECT VALUE FROM INFO_DATA WHERE ID_INFO=2) C2,
    (SELECT VALUE FROM INFO_DATA WHERE ID_INFO=3) C3,
    (SELECT VALUE FROM INFO_DATA WHERE ID_INFO=4) C4,
    (SELECT VALUE FROM INFO_DATA WHERE ID_INFO=5) C5,
    (SELECT VALUE FROM INFO_DATA WHERE ID_INFO=6) C6,
    (SELECT VALUE FROM INFO_DATA WHERE ID_INFO=7) C7,
    (SELECT VALUE FROM INFO_DATA WHERE ID_INFO=8) C8,
    (SELECT VALUE FROM INFO_DATA WHERE ID_INFO=9) C9,
    (SELECT VALUE FROM INFO_DATA WHERE ID_INFO=10) C10,
    (SELECT VALUE FROM INFO_DATA WHERE ID_INFO=11) C11,
    (SELECT VALUE FROM INFO_DATA WHERE ID_INFO=12) C12,
    (SELECT VALUE FROM INFO_DATA WHERE ID_INFO=13) C13)

これは、Infos テーブルの行数が 13 の場合のみです。

編集

列名は動的であるため、直接クエリの代わりにストアド プロシージャを使用する必要があります。その場合、動的クエリを使用する必要があります。ストアド プロシージャを使用できるかどうか教えてください。正確なクエリを伝えることができます。

于 2012-05-25T10:47:44.853 に答える
0

これが、EAVがほとんどのことを嫌う理由です。列内のものを取得したい場合は、おそらく EAV を使用したくないでしょう。私は、アプリケーション データ カタログと ALTER TABLE コマンドを使用して、カスタム フィールドに似たものに基づいて実際のリレーショナル モデルを作成する独自の EAV-lite を考え出しました。しかし、それは実際にはここかそこかではありません。

基本的に、2 つのオプションがあります。使用する結果の名前と型を PostgreSQL に事前に伝える必要があるため、「ストアド プロシージャだけを使用する」ことはできません。refcursor と動的 SQL を返すストアド プロシージャを使用できます。それが、あなたの厳しい要件をそのまま満たす答えです。

それは次のようになります

CREATE OR REPLACE FUNCTION eav_get(in_id int) RETURNS refcursor
LANGUAGE PLPGSQL AS 
 $$ DECLARE outval refcursor;
            t_row RECORD;
            t_query TEXT;
  BEGIN
      t_query := 'SELECT ';

      FOR t_row IN select distinct "name" FROM infos
      LOOP
         t_query := t_query 'max(CASE WHEN "name" = '|| quote_literal(t_row."name") || ' THEN value ELSE NULL END) ';
      END LOOP;
      t_query := t_query || 'FROM info_data WHERE id_key = || in_id || ' 
                 GROUP BY id_key ';
      OPEN outval FOR EXECUTE t_query;
      RETURN outval;
 END; $$;

上記のコードはまだテストしていませんが、十分に近いはずです。

ただし、考慮すべき別のオプションがあります。これは列には返されませんが、よりクリーンで、クライアント側での解析が難しくありません。EAVが実際に必要なLedgerSMBで使用します。

 SELECT id_key, array_agg("name"::text || '=' || "value"::text) 
   FROM infos_data 
  WHERE id_key = ?

等号を許可しないと仮定すると (他の区切り記号を使用できます)、アプリケーションで簡単に解析できる PostgreSQL 配列が返されます。

于 2012-09-06T06:51:05.910 に答える
0

この醜い獣は、EAV を非正規化する必要があり、ピボットのような機能が利用できない場合に便利です。

info_data にエンティティのキ​​ーフィールドを追加する必要がありました。

ALTER TABLE info_data
        ADD column id_key INTEGER
        ;
UPDATE info_data
SET id_key= 1+ ((id-1)/13);

WITH reut AS (
   SELECT d.id_info
        , d.value
        , d.id_key
        , i.zname AS zname
   FROM info_data d
   JOIN infos i ON i.id = d.id_info
        )
, num AS (
        SELECT DISTINCT id_key AS id_key
        FROM info_data
        )
SELECT n.id_key AS id_key
        ,r1.value AS "nume"
        ,r2.value AS "prenume"
        ,r3.value AS "cnp"
        ,r4.value AS "nume anterior"
        ,r5.value AS "stare civila"
        ,r6.value AS "cetatenie"
        ,r7.value AS "rezidenta"
        ,r8.value AS "adresa"
        ,r9.value AS "tip act"
        ,r10.value AS "serie ci"
        ,r11.value AS "numar ci"
        ,r12.value AS "data eliberarii"
        ,r13.value AS "eliberat de"
FROM num n
LEFT JOIN reut r1 ON r1.id_key = n.id_key AND r1.zname = 'nume'
LEFT JOIN reut r2 ON r2.id_key = n.id_key AND r2.zname = 'prenume'
LEFT JOIN reut r3 ON r3.id_key = n.id_key AND r3.zname = 'cnp'
LEFT JOIN reut r4 ON r4.id_key = n.id_key AND r4.zname = 'nume anterior'
LEFT JOIN reut r5 ON r5.id_key = n.id_key AND r5.zname = 'stare civila'
LEFT JOIN reut r6 ON r6.id_key = n.id_key AND r6.zname = 'cetatenie'
LEFT JOIN reut r7 ON r7.id_key = n.id_key AND r7.zname = 'rezidenta'
LEFT JOIN reut r8 ON r8.id_key = n.id_key AND r8.zname = 'adresa'
LEFT JOIN reut r9 ON r9.id_key = n.id_key AND r9.zname = 'tip act'
LEFT JOIN reut r10 ON r10.id_key = n.id_key AND r10.zname = 'serie ci'
LEFT JOIN reut r11 ON r11.id_key = n.id_key AND r11.zname = 'numar ci'
LEFT JOIN reut r12 ON r12.id_key = n.id_key AND r12.zname = 'data eliberarii'
LEFT JOIN reut r13 ON r13.id_key = n.id_key AND r13.zname = 'eliberat de'

ORDER BY n.id_key;
于 2012-05-30T14:21:41.477 に答える