Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1spnLM-004Pzm-4p for pgsql-general@arkaria.postgresql.org; Sun, 15 Sep 2024 11:23:05 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.94.2) (envelope-from ) id 1spnLK-00GbIH-BO for pgsql-general@arkaria.postgresql.org; Sun, 15 Sep 2024 11:23:02 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1spnLJ-00GbI8-VI for pgsql-general@lists.postgresql.org; Sun, 15 Sep 2024 11:23:02 +0000 Received: from mail-ej1-x62e.google.com ([2a00:1450:4864:20::62e]) by magus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.94.2) (envelope-from ) id 1spnLB-001Isg-Lp for pgsql-general@lists.postgresql.org; Sun, 15 Sep 2024 11:23:01 +0000 Received: by mail-ej1-x62e.google.com with SMTP id a640c23a62f3a-a8629ddffbaso52568366b.1 for ; Sun, 15 Sep 2024 04:22:54 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1726399374; x=1727004174; darn=lists.postgresql.org; h=to:references:message-id:content-transfer-encoding:cc:date :in-reply-to:from:subject:mime-version:from:to:cc:subject:date :message-id:reply-to; bh=JsPGL47+pN66xMiKX9oBfdEbApDnlRbCR6VlYUmcI18=; b=EtiwZRQR1PJWuYNGFXottjB49waN3XYpanqCgZoQLfymE4IeZltUy2vxAsurUYqMtw 5LAuma63sES2IKalJLTyIppNCOVnJl6B9SjrB56A+i0Rmm/pTRboVze3+86jScfY0Do9 5v6mlfGp600t2p/U9av7R+F7wXbjif29bwSHhGyWysToaqjGAcdu0NuNcQsqJEB/8v32 GXMOY0odYAcbvi2mvBrQ/PqTlbb4j4T32Z/aFeXMcIan799AMETknzIKraZX9eMgn1H4 ytPSqVZS/ANZyBl1mHP/yercIKxlPvVPGG6IrXu9Mpb6KXC573dBb6yr1RIakRuOj1T0 sA/w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1726399374; x=1727004174; h=to:references:message-id:content-transfer-encoding:cc:date :in-reply-to:from:subject:mime-version:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=JsPGL47+pN66xMiKX9oBfdEbApDnlRbCR6VlYUmcI18=; b=PFpUtnhNeKvQRQBCJac7T4Uxdb8sHbxYZYXC7kiJVOqwhTxH0S2XgF8QBAJdj5xjgL 6mO4lJ0hwf6Ju/ti97wU+VChMT8S+0tt/CiZCqGrjY4woiVQk3MbuNy2WalZN/FGs4OS Ea0y7fSaYCHvJF+3u1Vj5zzFZZD8aOMLO4tpx+xf+cv2A5iSFczVmS2fLzCNx7sTjnRN vRXgCrRTzrI33jtLw6NxNe9zcq8b6G6i9h7LQLbfjIjVgEzrejNK8q9uR0X0iiiGNcS0 7drNqUD3dZdlvCVQ8kRn93EDDbjbcXd5LvFmQebGdAFRWZhemZBhSnbTpbX0zhpn2EFn JTCQ== X-Gm-Message-State: AOJu0YzpPs+I4SwsPvncGiu5ZeAeLf34QGd142KQziM9sLImbGTtAOaJ ckgGO2KjLzkfVXYFzE7RRQ1Ixe1MDdaKdjkwmsF9ym4KS82ToQ6RdDR/hQ== X-Google-Smtp-Source: AGHT+IHxqQkdTgNFcCoGAF8NbOC8+f2HU/DKr/Zljjyj/UX9FJNiP7IkUQnE9FObnKzzfLopfqy2TQ== X-Received: by 2002:a17:907:d22:b0:a8d:2623:dd18 with SMTP id a640c23a62f3a-a902963a4d7mr474960166b.13.1726399372870; Sun, 15 Sep 2024 04:22:52 -0700 (PDT) Received: from smtpclient.apple ([188.212.112.125]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-a9061096a08sm187576566b.2.2024.09.15.04.22.52 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Sun, 15 Sep 2024 04:22:52 -0700 (PDT) Content-Type: text/plain; charset=utf-8 Mime-Version: 1.0 (Mac OS X Mail 16.0 \(3776.700.51\)) Subject: Re: re-novice coming back to pgsql: porting an SQLite update statement to postgres From: Alban Hertroys In-Reply-To: <93c6b90cd39ae5fdf49cbd698e8de0c6b6f9bf3f.camel@kortschak.io> Date: Sun, 15 Sep 2024 13:22:41 +0200 Cc: pgsql-general@lists.postgresql.org Content-Transfer-Encoding: quoted-printable Message-Id: <90CC4567-4C9E-4F83-902C-A3CE5C3C74E5@gmail.com> References: <1aea530e4c96109b91fdde53a29e38beb625dc77.camel@kortschak.io> <20240723221121.51f2b3872d0ebfc36a6fa8ff@wanadoo.fr> <2f11b2ba-3182-492a-ab46-23cfa5ec913c@aklaver.com> <735913e4c9601a2a7ad57d2253d74451c42ba899.camel@kortschak.io> <21078220628bcebca0395a52d42b558ed965bbc0.camel@kortschak.io> <5c86256a4e83b1af73da22faab54889564e93129.camel@kortschak.io> <7320B905-E950-4035-9A13-8C9447AB52EC@gmail.com> <48019d21fd7c669afb83161337f7e007ae619534.camel@kortschak.io> <93c6b90cd39ae5fdf49cbd698e8de0c6b6f9bf3f.camel@kortschak.io> To: Dan Kortschak X-Mailer: Apple Mail (2.3776.700.51) List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk > On 15 Sep 2024, at 11:07, Dan Kortschak = wrote: >=20 > I have come to hopefully my last stumbling point. >=20 > I am unable to see a way to express something like this SQLite syntax >=20 > select json_group_array(json_replace(value, > '$.a', case > when json_extract(value, '$.a') > 2 then > 2 > else > json_extract(value, '$.a') > end, > '$.b', case > when json_extract(value, '$.b') < -2 then > -2 > else > json_extract(value, '$.b') > end > )) > from > json_each('[{"a":1, "b":-3},{"a":2, "b":-2},{"a":3, "b":-1}]'); What=E2=80=99s the result of that query in SQLite? I=E2=80=99m guessing it would be: [{"a":1, "b":-2},{"a":2, = "b":-2},{"a":2, "b":-1}] I see basically two approaches. One is to take the objects apart and = build them back together again, the other is to attempt to only replace = the values that need replacing. For the sake of showing how both approaches modify the original, I added = an extra field =E2=80=9Cc=E2=80=9D to your objects that should be in the = result unmodified. The first approach rebuilds the objects: with t as ( select jsonb($$[{"a":1, "b":-3, "c":1},{"a":2, "b":-2, = "c":2},{"a":3, "b":-1, "c":3},{"a":3, "b":-3, "c":4}]$$) arr ) select jsonb_agg(jsonb_build_object( 'a', case when records.a > 2 then 2 else records.a end , 'b', case when records.b < -2 then -2 else records.b end , 'c', c )) from t cross join lateral jsonb_to_recordset(t.arr) records(a int, b int, c = int) ; jsonb_agg = =20 = --------------------------------------------------------------------------= ------------------------------------ [{"a": 1, "b": -2, "c": 1}, {"a": 2, "b": -2, "c": 2}, {"a": 2, "b": = -1, "c": 3}, {"a": 2, "b": -2, "c": 4}] (1 row) The drawback is that you have to specify all fields and types, but you = don=E2=80=99t need to cast the values all the time either. The replacement approach gets a bit trickier. I don=E2=80=99t see any = good method that would replace both =E2=80=98a=E2=80=99 and =E2=80=98b=E2=80= =99 values if they both go outside bounds in the same object.=20 The jsonb_set function in PG doesn=E2=80=99t seem to be able to handle = setting a value conditionally, let alone, setting multiple values = conditionally in one call, so I ended up with replacing either =E2=80=98a=E2= =80=99 or =E2=80=98b=E2=80=99. I did include a case where both =E2=80=98a=E2= =80=99 and =E2=80=98b=E2=80=99 go out of bounds, replacing both values = with there respective replacements, but the syntax for that approach = doesn=E2=80=99t scale well to more combinations of fields and boundaries = to check and replace. Hence I added the problematic case to the test string. As you can see = from the previous query, that one does handle that case properly without = much extra hassle. with t as ( select jsonb($$[{"a":1, "b":-3, "c":1},{"a":2, "b":-2, = "c":2},{"a":3, "b":-1, "c":3},{"a":3, "b":-3, "c":4}]$$) arr ) select jsonb_agg( case when (obj->>'a')::INTEGER > 2 and (obj->>'b')::INTEGER < -2 then jsonb_set(jsonb_set(obj, '{a}', '2') ,'{b}', '-2') when (obj->>'a')::INTEGER > 2 then jsonb_set(obj, '{a}', '2') when (obj->>'b')::INTEGER < -2 then jsonb_set(obj, '{b}', '-2') else obj end) newArr from ( select jsonb_array_elements(arr) obj from t ) elements; newarr = =20 = --------------------------------------------------------------------------= ------------------------------------ [{"a": 1, "b": -2, "c": 1}, {"a": 2, "b": -2, "c": 2}, {"a": 2, "b": = -1, "c": 3}, {"a": 2, "b": -2, "c": 4}] (1 row) For understanding both queries better, it probably helps to take out the = jsonb_agg calls to see the separate objects from the array. Add the = original obj back in for comparison, if you like. I typically use the documentation pages for the JSON functions and the = one on aggregate functions, where the JSONB aggregates are located: https://www.postgresql.org/docs/16/functions-json.html https://www.postgresql.org/docs/16/functions-aggregate.html And if you=E2=80=99re not familiar with dollar quoting: = https://www.postgresql.org/docs/16/sql-syntax-lexical.html#SQL-SYNTAX-DOLL= AR-QUOTING Alban Hertroys -- There is always an exception to always.