【问题标题】:Cast JSON to HSTORE in Postgres 9.3+?在 Postgres 9.3+ 中将 JSON 转换为 HSTORE?
【发布时间】:2015-04-03 15:42:23
【问题描述】:

我已阅读文档,似乎没有明显的方法可以执行 ALTER TABLE ... ALTER COLUMN ... USING 语句以将 json 类型列直接转换为 hstore 类型。没有可用的功能(我知道)来执行演员表。

我的下一个最佳选择是创建一个hstore 类型的新列,使用一些外部工具将我的 JSON 数据复制到该新列,删除旧的 json 列并将新的 hstore 列重命名为旧列的名称。

有没有更好的办法?

到目前为止我所拥有的是:

$ CREATE TABLE blah (unstructured_data JSON);

$ ALTER TABLE blah ALTER COLUMN unstructured_data 
       TYPE hstore USING CAST(unstructured_data AS hstore);
ERROR:   cannot cast type json to hstore

【问题讨论】:

  • JSON 可以有多维数组,而 hstore 不能。这意味着简单地在两者之间进行转换并不是一个直接的过程。只要您知道这两种数据类型并不总是相等的,就有很多可用的用户函数。
  • 我听到了——假设我的 JSON 没有嵌套,只有引用的属性名称并且只有属性字符串,我应该能够编写从 JSON 到 HSTORE 的转换函数吗?或者您会建议在 Postgres 外部的 for 循环中执行此“更新”操作吗?

标签: json postgresql database-migration hstore


【解决方案1】:

重复键存在“陷阱” - jsonhstore 输入都允许,但不幸的是解决方法不同 (!)。考虑这个示例值:

json '{"double_key":"key1","foo":null,"double_key":"key2"}'

json 中,'double_key 实际上是'key2'The manual:

因为json 类型存储了输入文本的精确副本,所以它将 保留标记之间的语义无关紧要的空白,如 以及 JSON 对象中键的顺序。此外,如果一个 JSON 对象 值内多次包含同一个键​​,所有 保留键/值对。 (处理函数将最后一个值视为有效值。)

我的大胆强调。

然而,在hstore 中,对于相同顺序的键/值对,'double_key'可能实际上是'key1' . The manual:

hstore 中的每个键都是唯一的。如果您声明 hstore 与 重复的密钥,hstore 中只会存储一个,并且不保证会保留哪个

通常是键的第一个实例,但这是一个可能会改变的实现细节。

始终保持有效、可操作的值的简单快速选项:在转换之前强制转换为 jsonbThe manual again:

[...] jsonb 不保留空白,不保留 对象键的顺序,并且不保留重复的对象键。 如果输入中指定了重复键,则只保留最后一个值。

修改@pozs's conversion function:

CREATE OR REPLACE FUNCTION json2hstore(json)
  RETURNS hstore AS
$func$
  SELECT hstore(array_agg(key), array_agg(value))
  FROM   jsonb_each_text($1::jsonb)  -- !
$func$ LANGUAGE sql IMMUTABLE STRICT;

需要 Postgres 9.4 或更高版本。 Postgres 9.3 有 json 类型,但还没有 jsonb。 PL/v8 中的无操作可能会在那里替代,例如 @jpmc mentioned

【讨论】:

    【解决方案2】:

    不幸的是,PostgreSQL 不允许在 ALTER TABLE ... SET DATA TYPE ...USING 子句中使用所有类型的表达式(例如,不允许子查询)。

    但是,您可以编写一个函数来克服这个问题,您只需要决定如何处理高级类型(在对象的值中),例如数组和对象。这是一个示例,它只是将它们转换为字符串:

    CREATE OR REPLACE FUNCTION my_json_to_hstore(json)
      RETURNS hstore
      IMMUTABLE
      STRICT
      LANGUAGE sql
    AS $func$
      SELECT hstore(array_agg(key), array_agg(value))
      FROM   json_each_text($1)
    $func$;
    

    之后,您可以在 ALTER TABLE 中使用它,例如:

    ALTER TABLE blah
      ALTER COLUMN unstructured_data
      SET DATA TYPE hstore USING my_json_to_hstore(unstructured_data);
    

    【讨论】:

    • 我在运行 ALTER 时收到 Error : ERROR: default for column "my_column" cannot be cast automatically to type hstore
    • 在 JSON 中,密钥的最后一个规范获胜,而在 hstore 中,密钥的第一个规范获胜。这意味着结果可能与您对 JSON 的预期不一致。解决这个问题的一种方法是通过一种无操作的 PL/v8 函数运行它,它清除了无关的关键规范。 jsonb 可能没有必要这样做。
    • @jpmc26 或者,您可以将SELECT DISTINCT ON (key)ORDER BY key 添加到我的解决方案的函数中(ASC|DESC 取决于您要保留的值 - 甚至可以过滤窗口函数他们出去)
    • 仅按键排序不一定会产生与 JSON 中相同的顺序,并且该值也不会为您提供执行此操作的信息。您可以执行SELECT key, $1->>key FROM (SELECT DISTINCT key FROM json_object_keys($1) json_key (key)) distinct_key 之类的操作,但这并不比 imo 的无操作 PL/v8 函数更好。
    • @jpmc26:好点。 (实际上,重复键中的赢家是 undefined for hstore。)我添加了 pozs 智能功能的替代版本来覆盖角落案例。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-06
    • 2019-02-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多