Tricky PostgreSQL加入和订单查询

时间:2015-04-02 05:22:59

标签: sql postgresql

我在PostgreSQL 9.3.6数据库中有四个表:

  • 部分
  • 字段(部分的孩子)
  • 条目(部分的孩子)
  • 数据(参赛作品的孩子)

CREATE TABLE section (
  id serial PRIMARY KEY,
  title text,
  "group" integer
);

CREATE TABLE fields (
  id serial PRIMARY KEY,
  title text,
  section integer,
  type text,
  "default" json
);

CREATE TABLE entries (
  id serial PRIMARY KEY,
  section integer
);

CREATE TABLE data (
  id serial PRIMARY KEY,
  data json,
  field integer,
  entry integer
);

我试图生成一个如下所示的页面:

section title

          field 1 title  | field 2 title | field 3 title 
entry 1 | data 'as' json | data 1 json   | data 3 json        <-- table
entry 2 | data 'df' json | data 5 json   | data 6 json  
entry 3 | data 'gh' json | data 8 json   | data 9 json  

我现在的方式就是设置每一条数据&#39;有一个它链接的条目,一个相应的字段(该字段有确定如何解释数据的json字段的列),一个用于存储不同类型数据的json字段和一个id(1 -9在表格中。)

在此示例中,有3个条目和3个字段,并且每个单元格之间都有一个数据片段。

它的设置是这样的,因为一个部分的字段类型和数量可能与另一个部分不同,因此数据的数量和类型也不同。

挑战1:

我试图以一种可以按任何列排序的方式加入表格(该字段的数据内容为json列)。例如,我希望能够以相反的顺序对字段3(第三列)进行排序,表格如下所示:

section title

          field 1 title  | field 2 title | field 3 title 
entry 3 | data 'gh' json | data 8 json   | data 9 json  
entry 2 | data 'df' json | data 5 json   | data 6 json  
entry 1 | data 'as' json | data 1 json   | data 3 json        <-- table

如果有更好的方式,我也可以采取另一种方式。

挑战2:

每个字段都有一个&#39;默认值&#39;专栏 - 理想情况下,我只需要创建数据&#39;条目的值不是默认值。因此,如果字段2的默认值是&#39; asdf&#39;:

,那么表格实际上可能是这样的
section title

          field 1 title  | field 2 title | field 3 title 
entry 3 | data 'gh' json | data 8 json   | data 9 json  
entry 2 | data 'df' json | 'asdf'        | data 6 json  
entry 1 | data 'as' json | 'asdf'        | data 3 json        <-- table

2 个答案:

答案 0 :(得分:2)

编写此查询的关键是要了解您只需获取单个部分的所有数据以及您刚刚加入的其余部分。您也可以使用您的架构直接按部分过滤数据,因此您只需为此加入条目:

SELECT d.* FROM data d JOIN entries e ON (d.entry = e.id) 
WHERE e.section = ?

然后,您可以将字段连接到每一行以获取默认值,类型和标题:

SELECT d.*, f.title, f.type, f."default" 
FROM data d JOIN entries e ON (d.entry = e.id) 
            JOIN fields f ON (d.field = f.id)
WHERE e.section = ?

或者您可以在单独的查询中选择字段以节省一些网络流量。

所以这是一个答案,这里有奖金:

  1. 使用外键而不是整数来引用其他表,它将为您提供数据库检查的一致性。

  2. 关系(表格)应按惯例以单数形式调用,因此它是sectionentryfield

  3. 引用字段称为<name>_id,例如按惯例field_idsection_id

  4. JSON字段的重点是存储一个没有静态定义数据的集合,因此不使用entriesdata表更有意义,但是单个表与包含所有字段的JSON。

  5. 像这样:

    CREATE TABLE row ( -- less generic name would be even better
        id int primary key,
        section_id int references section (id),
        data json
    )
    

    data字段包含以下内容:

    {
        "title": "iPhone 6",
        "price": 650,
        "available": true,
        ...
    }
    

答案 1 :(得分:1)

@Suor提供了很好的建议,其中一些你已经接受了。我正在构建更新架构

模式

CREATE TABLE section (
  section_id serial PRIMARY KEY,
  title text,
  grp integer
);

CREATE TABLE field (
  field_id serial PRIMARY KEY,
  section_id integer REFERENCES section,
  title text,
  type text,
  default_val json
);

CREATE TABLE entry (
  entry_id serial PRIMARY KEY,
  section_id integer REFERENCES section
);

CREATE TABLE data (
  data_id serial PRIMARY KEY,
  field_id integer REFERENCES field,
  entry_id integer REFERENCES entry,
  data json
);

我更改了两个细节:

  • section_id代替id等。&#34; id&#34;由于一些ORM使用它,因此列名是一种受欢迎的反模式。唐&#39;吨。描述性名称要好得多。相同内容的相同名称是一个有用的指南。它还允许在join子句中使用快捷方式USING

  • 不要使用保留字作为标识符。使用合法的,小写的,不带引号的名称专门使您的生活更轻松。

参照完整性?

您的设计还有另一个固有的弱点。是什么阻止data中的条目引用不合并的fieldentry?与dba.SE密切相关的问题

查询

不确定您是否需要复杂的设计。但要回答这个问题,这是基本查询:

SELECT entry_id, field_id, COALESCE(d.data, f.default_val) AS data
FROM   entry     e
JOIN   field     f USING (section_id)
LEFT   JOIN data d USING (field_id, entry_id)  -- can be missing
WHERE  e.section_id = 1
ORDER  BY 1, 2;

LEFT JOIN对于允许丢失数据条目并使用默认值非常重要。

SQL Fiddle.

crosstab()

最后一步是交叉制表。由于未安装附加模块tablefunc,因此无法在SQL Fiddle中显示此内容。

crosstab()的基础知识:

SELECT * FROM crosstab(
   $$
   SELECT entry_id, field_id, COALESCE(d.data, f.default_val) AS data
   FROM   entry     e
   JOIN   field     f USING (section_id)
   LEFT   JOIN data d USING (field_id, entry_id)  -- can be missing
   WHERE  e.section_id = 1
   ORDER  BY 1, 2
   $$
  ,$$SELECT field_id FROM field WHERE section_id = 1 ORDER BY field_id$$
   ) AS ct (entry int, f1 json, f2 json, f3 json)  -- static
ORDER  BY f3->>'a';  -- static

这里棘手的部分是函数的返回类型。我为3个字段提供了静态类型,但你真的想要那个动态。另外,我引用了json类型中可能存在或可能不存在的字段... 因此,动态构建该查询并在第二次调用中执行它。

更多相关信息: