无法从非对象Postgres json数组中提取字段

时间:2015-02-21 02:13:45

标签: arrays json postgresql python-2.7 psycopg2

我有一个名为Stores的表,其中有一个名为store_id的密钥和一个名为Sales的表,其中包含store_id引用和一个名为{{1}的json字段}。

sales_json看起来像这样:

sales_json

我有一个plpgsql函数,其中我试图确定商店当前是否有销售,并在结果表中存储一个名为[{'start_date': '2-20-15', 'end_date': '2-21-15', 'start_time': '11:00 AM', 'end_time': '11:00 PM', 'discount_percentage': 20}, etc] 的布尔值,具体取决于是否为真。< / p>

我的SELECT语句如下所示:

having_sale

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

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;

我做错了什么以及如何解决?

2 个答案:

答案 0 :(得分: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错误。

答案 1 :(得分:0)

除了您自己整理的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

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