【问题标题】:Is there a function that would allow me to output current index of an element in json array?是否有一个函数可以让我输出 json 数组中元素的当前索引?
【发布时间】:2017-07-14 01:51:15
【问题描述】:

我有以下示例 Postgres SQL 查询。

SELECT name, json_array_elements(myjson)->>'id' id FROM
(SELECT 'Dingsda'::text AS name, json_build_array('{"id" : 100}'::json, '{"id" : 200}'::json) myjson
  UNION ALL
 SELECT 'Dingsbums'::text AS name, json_build_array('{"id" : 101}'::json, '{"id" : 201}'::json, '{"id" : 301}'::json) myjson
) t;

它输出下表。

+-----------+-----+
|   name    | id  |
+-----------+-----+
| Dingsda   | 100 |
| Dingsda   | 200 |
| Dingsbums | 101 |
| Dingsbums | 201 |
+-----------+-----+

现在我想要实现的是在末尾放置第三列,其中包含以 0 开头的 json 数组元素的索引,依此类推。结果应如下所示。

+-----------+-----+-------+
|   name    | id  | index |
+-----------+-----+-------+
| Dingsda   | 100 |     0 |
| Dingsda   | 200 |     1 |
| Dingsbums | 101 |     0 |
| Dingsbums | 201 |     1 |
| Dingsbums | 301 |     2 |
+-----------+-----+-------+

不幸的是,我还没有找到对我有帮助的功能。我尝试使用row_number() OVER 进行试验,但当涉及json 类型列时,它似乎返回了奇怪的结果。

是否有任何功能可以支持这个相当简单的要求?

编辑

Mureinik 的回答很有帮助,但是我希望能够保留 json 中元素的原始序列。在此示例中,序列丢失。我认为这不是确定性的。

SELECT name, id, ROW_NUMBER() OVER (PARTITION BY name ORDER BY 1)
FROM (SELECT name, JSON_ARRAY_ELEMENTS(myjson)->>'id' id 
      FROM   (SELECT 'Dingsda'::text AS name, JSON_BUILD_ARRAY('{"id" : 100}'::json, '{"id" : 200}'::json, '{"id" : 300}'::json, '{"id" : 400}'::json) myjson
          UNION ALL
          SELECT 'Dingsbums'::text AS name, JSON_BUILD_ARRAY('{"id" : 101}'::json, '{"id" : 201}'::json, '{"id" : 301}'::json) myjson
         ) t
      ) s;
+-----------+-----+-------+
|   name    | id  | index |
+-----------+-----+-------+
| Dingsbums | 101 |     1 |
| Dingsbums | 201 |     2 |
| Dingsbums | 301 |     3 |
| Dingsda   | 100 |     1 |
| Dingsda   | 400 |     2 |
| Dingsda   | 200 |     3 |
| Dingsda   | 300 |     4 |
+-----------+-----+-------+

【问题讨论】:

    标签: sql json postgresql select postgresql-9.4


    【解决方案1】:

    永远不要在SELECT 子句中使用集合返回函数(例如json_array_elements()),除非您确切地知道自己在做什么。它只会让你的事情变得更难。

    您可以使用表函数 (9.4+) 的 WITH ORDINALITY 子句,这正是您的问题:

    SELECT name, elem->>'id' id, "index"
    FROM (SELECT 'Dingsda'::text AS name, json_build_array('{"id" : 100}'::json, '{"id" : 200}'::json) myjson
            UNION ALL
          SELECT 'Dingsbums'::text AS name, json_build_array('{"id" : 101}'::json, '{"id" : 201}'::json, '{"id" : 301}'::json) myjson) t,
         json_array_elements(t.myjson) WITH ORDINALITY e(elem, "index");
    

    您也可以将generate_series()json_array_length() 一起使用,但WITH ORDINALITY 更简单。

    【讨论】:

    • 谢谢,我不知道这一点。关键字WITH ORDINALITY 都不是。非常有帮助!它也确实保留了我的数组元素的序列。
    【解决方案2】:

    您可以使用横向连接(无论如何推荐)和with ordinality

    SELECT name, x.element ->> 'id' as id, x.idx
    FROM (
      SELECT 'Dingsda'::text AS name, json_build_array('{"id" : 100}'::json, '{"id" : 200}'::json) myjson
      UNION ALL
      SELECT 'Dingsbums'::text AS name, json_build_array('{"id" : 101}'::json, '{"id" : 201}'::json, '{"id" : 301}'::json) myjson
    ) t
      cross join lateral json_array_elements(myjson) with ordinality as x(element, idx)
    

    【讨论】:

      【解决方案3】:

      使用row_number 似乎是正确的方法。您只需将此查询与另一个查询包装以生成行号:

      SELECT name, id, ROW_NUMBER() OVER (PARTITION BY name ORDER BY 1)
      FROM (SELECT name, JSON_ARRAY_ELEMENTS(myjson)->>'id' id 
            FROM   (SELECT 'Dingsda'::text AS name, JSON_BUILD_ARRAY('{"id" : 100}'::json, '{"id" : 200}'::json) myjson
                    UNION ALL
                    SELECT 'Dingsbums'::text AS name, JSON_BUILD_ARRAY('{"id" : 101}'::json, '{"id" : 201}'::json, '{"id" : 301}'::json) myjson
                   ) t
            ) s;
      

      【讨论】:

      • 嗨,这很有帮助,但在某些情况下,这里的序列丢失了。请查看我的编辑。
      猜你喜欢
      • 2020-02-05
      • 2010-12-01
      • 2013-07-19
      • 2014-01-21
      • 1970-01-01
      • 2013-01-21
      • 1970-01-01
      • 2012-09-08
      • 1970-01-01
      相关资源
      最近更新 更多