Update multiple keys in json data in same query - postgresql

I have the following query:
UPDATE events e
SET
details = jsonb_set ( details, '{"data", "user_name"}', '"Resident"' ),
details = jsonb_set ( details, '{"data", "is_by_support"}', '"false"' ),
details = jsonb_set ( details, '{"data", "is_by_resident"}', '"true"' ),
updated_by = 1,
updated_on = now ()
FROM
test_req tr
WHERE
e.id = 12345 AND
AND e.data_reference_id = tr.id
AND e.event_type_id = 4
AND e.created_by = 2
AND e.updated_by = 2
AND e.details -> 'data' ->> 'user_name' = 'Test'
AND e.details -> 'data' ->> 'is_by_support' = 'true'
AND e.details -> 'data' ->> 'is_by_resident' = 'false';
After executing it gives me an error as
ERROR: multiple assignments to same column "details"
How to use multiple keys update in the same query? Is there any other way?

You cannot have the same column several times in the SET clause of an UPDATE. Why not do something like
SET details = jsonb_set(
jsonb_set(
jsonb_set(
details,
'{"data", "user_name"}',
'"Resident"'
),
'{"data", "is_by_support"}',
'"false"'
),
'{"data", "is_by_resident"}',
'"true"'
)

try this :
WITH list AS
(
SELECT e.id, jsonb_set(
jsonb_set(
jsonb_set(
details,
'{"data", "user_name"}',
'"Resident"'
),
'{"data", "is_by_support"}',
'"false"'
),
'{"data", "is_by_resident"}',
'"true"'
) AS sol
FROM events e
INNER JOIN test_req tr
ON e.data_reference_id = tr.id
WHERE e.id = 12345
AND e.event_type_id = 4
AND e.created_by = 2
AND e.updated_by = 2
AND e.details -> 'data' ->> 'user_name' = 'Test'
AND e.details -> 'data' ->> 'is_by_support' = 'true'
AND e.details -> 'data' ->> 'is_by_resident' = 'false'
)
UPDATE events e
SET details = l.sol,
updated_by = 1,
updated_on = now ()
FROM list AS l
WHERE e.id = l.id

Related

How can I update a table using the following CROSS APPLY?

UPDATE ItemDim_DEV
set ActiveFlag = 0,
EndUTCDate = GETUTCDATE()
from (select i.SKU, i.itemname, i.category, i.CategoryInternalId, i.itemtype, i.IsActive, i.assetaccount, i.InternalId,
ni.sku,
ni.itemname,
ni.class_name,
ni.productclass,
ni.ItemType,
ni.isactive,
ni.assetaccount,
i.CalculatedHash, ni.hashid
from (
SELECT i.*
FROM itemDim_Dev i
WHERE i.SourceSystem = 'NetSuite'
--and InternalId = '1692'
) i
CROSS APPLY (
SELECT (CONVERT([binary](64),hashbytes('SHA2_512',
concat (ISNULL(im.TargetSKU, ni.sku), ni.itemname, pc.class_name, ni.productclass, ni.ItemType,
CAST(CASE WHEN ni.IsInactive = 'T' THEN 0 ELSE 1 END AS BIT ), ni.assetaccount)))) hashid,
ISNULL(im.TargetSKU, ni.sku) sku,
ni.itemname,
pc.class_name,
ni.productclass,
ni.ItemType,
CAST(CASE WHEN ni.IsInactive = 'T' THEN 0 ELSE 1 END AS BIT ) isactive, ni.assetaccount
FROM NetSuiteInventory ni
INNER JOIN Product_Class pc ON ni.productclass = pc.class_id
LEFT JOIN ItemMapping im ON ni.sku = im.SourceSKU
WHERE ni.ItemInternalId = CAST(i.InternalId as bigint)
and
(CONVERT([binary](64),hashbytes('SHA2_512',
concat (ISNULL(im.TargetSKU, ni.sku), ni.itemname, pc.class_name, ni.productclass, ni.ItemType,
CAST(CASE WHEN ni.IsInactive = 'T' THEN 0 ELSE 1 END AS BIT ), ni.assetaccount)))) = i.CalculatedHash
) ni
I understand that I can put a select after the from of the update but in the CROSS APPLY that is in the query starts with a select as in the second one, can you help me please.
Make a join to the target table
UPDATE A
SET XXX = YYY
FROM ItemDim_DEV A
JOIN (
--huge SELECT that I recommend you try to shorten in the next questions
CROSS APPLY (any secrets...)
) B ON A.PK = B.PK

Detect if an aggregate is null in a SQL case conditional statement

I have 3 tables:
entities: list of entities, some of them can participate to an event;
events: list of the events;
participations: list of participations
Here are the structures:
ENTITIES
id | name
EVENTS
id | name | date_start | date_end
PARTICIPATIONS
id | id_event | id_participation | type
I'd like to get all the entities, with in the column "events" the array of all the events. Here is what gives me a good result:
SELECT
e.id,
e.name,
array_agg(
json_build_object(
'id', evts.id,
'name', evts.name,
'date_start', evts.date_start,
'date_end', evts.date_end
)
) as events
FROM g.entities e
LEFT JOIN public.events_participations p
ON (
p.id_participation::integer = e.id
AND type ='entity'
)
LEFT JOIN public.events evts
ON evts.id = p.id_event
WHERE e.id = 22
GROUP BY p.id_participation, e.id
BUT (and there is a but otherwise I wouldn't write here) when an entity doesn't participate in any event, I have this in the column:
{"{\"id\" : null, \"name\" : null, \"date_start\" : null, \"date_end\" : null}"}
I thought I could get a "NULL" because of the LEFT JOIN.
I tried to use "CASE" to detect when there are no event, but it doesn't seem to be the solution. How could I get the expected result and get an array only if there is at least one event and NULL otherwise?
Thanks in advance.
EDIT:
I tried this:
WITH list_events AS (
SELECT
e.id,
json_build_object(
'id', evts.id,
'name', evts.name,
'date_start', evts.date_start,
'date_end', evts.date_end
) as events
FROM g.entities e
LEFT JOIN public.events_participations p
ON (
p.id_participation::integer = e.id
AND type ='entity'
)
JOIN public.events evts
ON evts.id = p.id_event
WHERE e.id = 1
)
SELECT
e.id,
e.nom,
array_agg(
l.*
) as events
FROM g.entities e
LEFT JOIN list_events l
ON e.id = l.id
WHERE e.id = 1
GROUP BY e.id
But instead of having "NULL" I have en empty column of type "record[]".
I was almost there:
WITH list_events AS (
SELECT
e.id,
json_build_object(
'id', evts.id,
'name', evts.name,
'date_start', evts.date_start,
'date_end', evts.date_end
) as events
FROM g.entities e
LEFT JOIN public.events_participations p
ON (
p.id_participation::integer = e.id
AND type ='entity'
)
JOIN public.events evts
ON evts.id = p.id_event
WHERE e.id = 1
)
SELECT
e.id,
e.nom,
array_agg(
l.events
) as events
FROM g.entities e
LEFT JOIN list_events l
ON e.id = l.id
WHERE e.id = 1
GROUP BY e.id

Ordinary query to Zend format Query?

manually I constructed a query... I'm facing struggle to construct in zend,
e.i select inside select
these is my query,
SELECT * FROM (
SELECT t1.eventId,t1.start_date,t1.end_date, COUNT(*) pos FROM events t1
LEFT JOIN events t2
ON t2.start_date = t1.start_date AND t2.eventId <= t1.eventId
GROUP BY
t1.eventId,t1.start_date
) t
WHERE
pos <= 3;
$query = $database->select ()
->from ('events AS t1', array (
'eventId',
'start_date',
'end_date',
new Zend_Db_Expr ('COUNT(*) AS pos')
))
->joinLeft ('events AS t2', 't2.start_date = t1.start_date AND t2.eventId <= t1.eventId', array ())
->group ('t1.eventId,t1.start_date');
$outer_query = $database->select ()
->from ($query)
->where ('pos <= 3');

sql server update many columns with a select from a join of a table variable and a db table

I have the classical person -> person attributes scheme.
So, like this: person(PK) <- person_attribute(FK)
What I need is a query to get one row where a person is joined with her attributes.
For example, to transform:
Person:
{ ID = 123456, Name = 'John Smith', Age = 25 }
Attributes:
1) { PersonID = 123456, AttributeTypeID = 'Height', AttributeValue = '6'6'''}
2) { PersonID = 123456, AttributeTypeID = 'Weight', AttributeValue = '220lbs'}
3) { PersonID = 123456, AttributeTypeID = 'EyeColor', AttributeValue = 'Blue'}
To:
PersonWithAttributes
{
ID = 123456, Name = 'John Smith', Age = 25, Height = '6'6''', Weight = '220lbs', EyeColor = 'Blue'
}
To make things worse my persons are in a table variable.
So, I have (in a sp with a parameter of person_id):
--result table
declare #people_info table
(
person_id int,
name nvarchar(max),
age int,
height nvarchar(10) null,
weight nvarchar(10) null,
eye_color nvarchar(16) null
)
insert into #people_info
select person_id, name, age, null, null, null
from dbo.HR.people where person_id = #person_id
update pi
set
pi.height = (select pa.attribute_value where pa.attribute_type_id = 'Height'),
pi.height = (select pa.attribute_value where pa.attribute_type_id = 'Weight'),
pi.eye_color = (select pa.attribute_value where pa.attribute_type_id = 'EyeColor')
from
#people_info pi
inner join dbo.HR.person_attributes pa on pi.person_id = pa.person_id
select * from #people_info
Which of course does not work for some reason.
If I query the two joined tables and select "pa.attribute_value where pa.attribute_type_id = 'someval'" I get the correct value. But the update does not work.
Of course, I can write this as three updates, but I am thinking that it will be faster to do one join and then to filter in the update clause.
Also, please keep in mind that my attributes are spread over three tables, not just the attributes table. So, this is why I have the table variable.
Any help is very welcome. Maybe I am going about this the wrong way. Performance matters. What is the most performant way to accomplish this?
Thank you very much.
Try this code for update with pivot:
update
pi
set
pi.height = pa.Height
pi.weight = pa.Weight
pi.eye_color = pa.EyeColor
from
#people_info pi
inner join
(
SELECT
person_id
,[Height] Height
,[Weight] Weight
,[EyeColor] EyeColor
FROM
(
SELECT
attribute_type_id
, attribute_value
, person_id
FROM
dbo.HR.person_attributes pa
) pa
PIVOT
(
MAX(attribute_value) FOR attribute_type_id IN ([Height],[Weight],[EyeColor])
)pvt
) pa
on
pi.person_id = pa.person_id
Maybe you want something like:
update pi
set
pi.height = paH.attribute_value,
pi.weight = paW.attribute_value,
pi.eye_color = paE.attribute_value
from
#people_info pi
inner join dbo.HR.person_attributes paH on pi.person_id = paH.person_id
and paH.attribute_type_id = 'Height'
inner join dbo.HR.person_attributes paW on pi.person_id = paW.person_id
and paW.attribute_type_id = 'Weight'
inner join dbo.HR.person_attributes paE on pi.person_id = paE.person_id
and paE.attribute_type_id = 'EyeColor'

How can I join a subquery using Zend_Db_Select

How would I construct this query using Zend_Db_Select?:
SELECT users.user_id, email_address, t1.value as 'languages'
FROM users
LEFT JOIN (
SELECT
user_id
, field_id
, GROUP_CONCAT(value SEPARATOR ',') AS value
FROM user_multivalued
WHERE field_id=25
GROUP BY user_id, field_id) t1
ON t1.user_id = users.users_id
WHERE list_id = 45
$user_multivalued = $db
->select()
->from('user_multivalued', array(
'user_id',
'field_id',
new Zend_Db_Expr("GROUP_CONCAT(value SEPARATOR ',') AS value")
))
->where('field = ?', 25)
->group('user_id')
->group('field_id')
;
$select = $db
->select()
->from('users', array('user_id', 'email_address'))
->joinLeft(
array('t1' => $user_multivalued),
't1.user_id = users.user_id',
array('languages'=>'value')
)
->where('list_id = ?', 45)
;