【问题标题】:Cannot extract field from a non-object, Postgres json array无法从非对象 Postgres json 数组中提取字段
【发布时间】:2015-04-22 20:36:57
【问题描述】:

我有一个名为Stores 的表,其中有一个名为store_id 的键和一个名为Sales 的表,其中包含一个store_id 引用和一个名为sales_json 的json 字段。

sales_json 看起来像这样:

[{'start_date': '2-20-15', 'end_date': '2-21-15', 'start_time': '11:00 AM', 
  'end_time': '11:00 PM', 'discount_percentage': 20}, etc] 

我有一个 plpgsql 函数,我在其中尝试确定商店当前是否有销售,并根据是否为真在结果表中存储一个名为 having_sale 的布尔值。

我的 SELECT 语句如下所示:

SELECT Stores.store_id,
       CASE WHEN (SELECT COUNT(*)
                  FROM (SELECT *
                        FROM json_array_elements(sales_json) AS sale
                        WHERE (now()::date BETWEEN (sale->>'start_date')::date AND
                                                   (sale->>'end_date')::date) AND
                              (now()::time BETWEEN (sale->>'start_time')::time AND
                                                   (sale->>'end_time')::time)
                       ) AS current_sales) > 0
                  THEN TRUE
            ELSE FALSE
       END) AS having_sale
FROM Stores INNER JOIN Sales
ON Stores.store_id = Sales.store_id;

代码在我看来是正确的,但我收到以下错误:

psycopg2.DataError: cannot extract field from a non-object

我做错了什么,我该如何解决?

【问题讨论】:

  • 一个或多个 sales_json 数组中的某些元素可能不是真正的对象? IE。你能在里面有任何空值吗?
  • 嗨,我正在使用上面的 sales_json 值进行测试,它不包含任何空值。
  • 好吧,上面的查询中有一个额外的括号,但除此之外,使用上面描述的测试数据对我来说效果很好 - 请参阅sqlfiddle.com/#!15/59fd7/2 - 所以我认为它有事情要做与您的输入数据。我可以得到该错误消息的唯一方法是将一个空数组插入其中一个 sales_json 条目,例如:sqlfiddle.com/#!15/e70fa/1
  • 这很奇怪,我使用的输入与适合您的输入完全相同,但仍然没有骰子:\
  • 您的 sqlfiddle 代码对我有用,但我使用类似但更复杂的表并通过 psycopg 的代码由于某种原因不起作用。它可能与 json 输入的类型转换有关,因为它从 python 调用到我的函数中。

标签: arrays json postgresql python-2.7 psycopg2


【解决方案1】:

该错误是由我编写的将 json 数组附加到 sales_json 的函数引起的。我修复了它,现在它看起来像这样:

CREATE OR REPLACE FUNCTION add_sales (insertion_id smallint, new_sales_json json)
RETURNS void AS $$
BEGIN
    UPDATE Sales
    SET sales_json = array_to_json(ARRAY(SELECT * FROM json_array_elements(sales_json)
                                         UNION ALL SELECT json_array_elements(new_sales_json)))
    WHERE store_id = insertion_id;
END;
$$ LANGUAGE plpgsql;

我之前错过了对 new_sales_json 的 json_array_elements() 调用,这就是为什么我在数组中获取数组并因此出现 cannot extract field from a non-object 错误。

【讨论】:

    【解决方案2】:

    除了你自己整理的mix-up of element and array input之外,你还可以大大简化你的SELECT语句:

    SELECT store_id
         , EXISTS (
              SELECT 1
              FROM   json_array_elements(s.sales_json) sale
              WHERE  now() BETWEEN (sale->>'start_date')::date + (sale->>'start_time')::time
                           AND     (sale->>'end_date')::date  + (sale->>'end_time')::time
              ) AS having_sale
    FROM   Stores st
    JOIN   Sales  s USING (store_id);
    

    越来越短。
    添加时间和日期会生成timestamp [without time zone]。存储时间戳数据而不是开始的日期和时间可能是个好主意。

    除非您的时间是每日时间表,而不是与日期相结合的绝对界限。那么你必须保持你原来的表达方式。

    最后,BETWEEN 包含两个边界,而您通常会包含下限并排除上限:

    WHERE  now() >= (sale->>'start_date')::date + (sale->>'start_time')::time
    AND    now() <  (sale->>'end_date')::date   + (sale->>'end_time')::time

    请注意,您的日期和时间是根据当前时区设置来解释的。

    【讨论】:

    • 鉴于我原来的帖子中的SELECT 声明和sales_json,结果当今天是2-20-15 和时间是11:01 PM 时,我得到false,当我实际上应该得到true。虽然您的解决方案没有解决我自己发现的原始问题,但它确实解决了这个问题。优化同样有价值。谢谢!
    • 请注意这里有一个额外的括号:(sale-&gt;&gt;'end_date')::date)
    • 这里:) AS having_sale
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-03-07
    • 2019-11-21
    • 2021-07-07
    • 2020-09-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多