PostgreSql : Json Array to Rows using Lateral Join

前端 未结 4 995
误落风尘
误落风尘 2020-12-12 06:17

I have two following JSON Array in details field of my table and need to evaluate the query as I use in another relational table.

{
    \"city\": \"London\",         


        
相关标签:
4条回答
  • 2020-12-12 06:26
    select
      quantity, price,
      avg(quantity/price) as ratio
    from my_table cross join lateral (
      select
        json_array_elements_text(details->'dates') as dates,
        (json_array_elements_text(details->'quantities'))::numeric as quantity,
        (json_array_elements_text(details->'prices'))::numeric as price) as data
    where details->>'city' = 'London'
    group by quantity, price;
    
    0 讨论(0)
  • 2020-12-12 06:36

    Is this what you want ?

        -- just simulate table:
    with my_table(details) as(
    values
    ('{
    "city": "London",
    "name": "Sainburry",
    "quantities": [112, 145, 222, 122, 124],
    "prices": [4, 4, 4, 0, 3],
    "dates": ["13.05.2020", "14.05.2020", "15.05.2020", "16.05.2020", "17.05.2020"]
    }'::json)
    )
    
    
    -- here is query:
    select  
    my_table.details->>'city',  u.quantities, u.prices  
    from my_table
    JOIN LATERAL UNNEST( 
        ARRAY(SELECT json_array_elements_text(details->'quantities')) ,
        ARRAY(SELECT json_array_elements_text(details->'prices')) 
    ) u(quantities, prices) ON TRUE
    WHERE
    my_table.details->>'city' = 'London'
    

    See demo

    0 讨论(0)
  • 2020-12-12 06:50

    Seems you need WITH ORDINALITY along with LEFT JOIN LATERALs to match the corresponding elements of the arrays due to the order in the arrays, respectively :

    SELECT q.elm AS quantities, p.elm AS prices, 
           AVG(p.elm::float/q.elm::float) AS ratio
      FROM my_table t0
      LEFT JOIN LATERAL jsonb_array_elements(details -> 'quantities') 
        WITH ORDINALITY AS q(elm, i) ON TRUE
      LEFT JOIN LATERAL jsonb_array_elements(details -> 'prices') 
        WITH ORDINALITY AS p(elm, i) ON q.i = p.i
      LEFT JOIN LATERAL jsonb_array_elements(details -> 'dates') 
        WITH ORDINALITY AS d(elm, i) ON d.i = q.i
     WHERE t0.details ->> 'city' = 'London'   
     GROUP BY q.elm, p.elm;
    

    Demo

    0 讨论(0)
  • 2020-12-12 06:51

    Basically, what Abelisto said. Just a couple of improvements and some explanation:

    SELECT to_date(d, 'DD.MM.YYYY') AS date -- ①
         , quantity, price
         , round(price / quantity, 4) AS ratio -- ③, ④
    FROM   my_table
    CROSS  JOIN LATERAL ( -- ②
       SELECT json_array_elements_text(details->'dates'     )          AS d -- ①
            , json_array_elements_text(details->'quantities')::int     AS quantity -- ③
            , json_array_elements_text(details->'prices'    )::numeric AS price -- ③
       ) AS data
    WHERE  details->>'city' = 'London';
    

    db<>fiddle here

    ① Date strings are interpreted depending on locale settings and session variables by default. Do it the clean way with to_date().

    ② Multiple set-returning functions in the SELECT list behave in surprising ways up until Postgres 10 if the number of resulting rows is not exactly the same for all. (Consider upgrading. In any case.) See:

    • What is the expected behaviour for multiple set-returning functions in SELECT clause?

    ③ In your original query AVG(quantities/prices::float) makes no sense in combination with group by quantities, prices. Neither does quantities/prices on its own. I fixed as I saw fit, and threw in round() to format output.

    ④ If quantity can be 0 defend against division by 0 with NULLIF:

         , round(price / NULLIF(quantity, 0), 4) AS ratio
    
    0 讨论(0)
提交回复
热议问题