棘手的PostgreSQL连接和订单查询

棘手的PostgreSQL连接和订单查询,sql,postgresql,Sql,Postgresql,我在PostgreSQL 9.3.6数据库中有四个表: 章节 字段(节的子项) 条目(节的子项) 数据(条目的子项) 我正在尝试生成一个如下所示的页面: 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' js

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

  • 章节
  • 字段(节的子项)
  • 条目(节的子项)
  • 数据(条目的子项)

我正在尝试生成一个如下所示的页面:

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  
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
章节标题
字段1标题|字段2标题|字段3标题

条目1 | data“as”json | data 1 json | data 3 json编写此查询的关键是理解您只需要获取单个节的所有数据,其余的数据只需加入。您也不能使用模式直接按节筛选数据,因此您需要加入条目,以便:

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 = ?
或者,您可以在单独的查询中选择字段以保存一些网络流量

这是一个答案,奖金来了:

  • 使用外键而不是整数来引用其他表,这将使数据库为您检查一致性

  • 按照惯例,关系(表)应以单数形式调用,因此它是
    部分
    条目
    字段

  • 引用字段被称为
    \u id
    ,例如
    字段id
    节id

  • JSON字段的全部意义在于存储一个包含非静态定义数据的集合,因此不使用
    条目
    数据
    表,而是使用包含所有字段的JSON的单个表更有意义

  • 像这样:

    CREATE TABLE row ( -- less generic name would be even better
        id int primary key,
        section_id int references section (id),
        data json
    )
    
    使用包含以下内容的
    数据
    字段:

    {
        "title": "iPhone 6",
        "price": 650,
        "available": true,
        ...
    }
    
    提供了很好的建议,其中一些建议你已经接受了。我正在更新的模式上构建

    模式 我又改变了两个细节:

    • section\u id
      而不是
      id
      ,等等。“id”作为列名是一种反模式,由于一些orm使用它,这种反模式变得很流行。不要。描述性名称更好。相同内容的相同名称是一条有用的指导原则。它还允许在join子句中使用快捷方式

    • 不要使用保留字作为标识符。只使用合法的、小写的、不带引号的名字,让你的生活更轻松

    参照完整性? 您的设计中还有一个固有的弱点。是什么阻止
    数据
    中的条目引用不在一起的
    字段
    条目
    ?关于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
    对于允许丢失数据条目并使用默认值非常重要

    crosstab()
    最后一步是交叉制表。由于未安装附加模块
    tablefunc
    ,因此无法在SQL FIDLE中显示此信息

    交叉表()的基础知识

    这里比较棘手的部分是函数的返回类型。我为3个字段提供了一个静态类型,但您确实需要动态类型。此外,我正在引用
    json
    类型中的一个字段,该字段可能存在,也可能不存在。。。 因此,动态构建该查询并在第二次调用中执行它

    更多信息:


    您还可以发布数据库表的架构吗?或者您正在查找完整的表信息吗?我希望获得一些表名和列名,以便构造正确的SQL查询。好了-谢谢您的检查!你对博士后的看法应该一直是个问题。我会做1-3题。您是否能够按照4中的设置以任何方式说“title”进行排序?另外,您提供的查询看起来与我要查找的非常相似。不过,我可能会在单独的查询中选择字段。有没有办法通过其中一个查询按数据对结果进行排序?如果是这样的话,我不知道该怎么做——我只需要一个通用的指令,在PostgreSQL 9.3中,您可以访问JSON字段中的值,所以可以按标题排序。您还可以创建一个函数索引,使其更快。您可以升级到PostgreSQL 9.4并获取二进制JSON和GIN索引。如果您需要编写这样的查询,您的模式可能设计得不好。@Suor:可能是这样。引用完整性还有另一个问题。我添加了一点,如果我删除了数据表,并在条目表中添加了一个数据json列,我认为这将完全消除对join的需要——我可以选择字段。然后选择条目并按我想要的json字段中的任意键进行排序。然后,在创建表时,如果设置了该列/字段的条目json键,则使用该值,否则使用默认值。唯一棘手的部分是有效地更新数据json字段,然后假设在9.3中按json键排序效果良好,这里是否需要交叉表步骤?如果没有它,它在小提琴中似乎很好用?交叉表部分做什么?@maxhud:它将列
    字段\u id
    的值转换为单独的列,以匹配您问题中的格式。请关注详细信息。
    {
        "title": "iPhone 6",
        "price": 650,
        "available": true,
        ...
    }
    
    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
    );
    
    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 * 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