How to update postgresql using json sub-element - postgresql

How can I update "A" named value with 10.2 where ID equal 1003 in to a postgresql database table.
Json Data Table
Id
Column
1001
{"results":[{"name":"A","value":"7.8"}, {"name":"B","value":"0.5"}]}
1002
{"results":[{"name":"B","value":"5.4"}, {"name":"D","value":"4.5"}]}
1003
{"results":[{"name":"D","value":"4.8"}, {"name":"A","value":"6.7"}]}
Result after update
Id
Column
1001
{"results":[{"name":"A","value":"7.8"}, {"name":"B","value":"0.5"}]}
1002
{"results":[{"name":"B","value":"5.4"}, {"name":"D","value":"4.5"}]}
1003
{"results":[{"name":"D","value":"4.8"}, {"name":"A","value":"10.2"}]}

It isn't a simple query, was able to make it with CTE only. I refer to your example table as test:
with item_in_list_pos as (
select
pos - 1 as pos
from test, jsonb_array_elements(column1->'results') with ordinality a(elem, pos)
where (
id = 1003
and elem->>'name' = 'A'
)
)
update test
set
column1 = jsonb_set(column1, array['results', pos, 'value']::text[], to_jsonb('10.2'::text))
from item_in_list_pos
where (
id = 1003
)

Related

SAS finding overlapping dates and add a flag

I have a dataset at id level with some overlapping dates. All I need to find those rows and add an identifier to count the number overlapping records.
Data:
ID ITEM StrDate EndDate
1001 A121 02/01/2022 02/15/2022
1001 B121 03/10/2022 03/10/2022
1002 C121 02/01/2022 02/10/2022
1002 D121 02/05/2022 02/15/2022
1003 E121 03/10/2022 03/21/2022
1003 F121 03/12/2022 03/21/2022
1004 G121 01/12/2022 01/14/2022
Below is the Result that I am expecting
Want:
ID ITEM StrDate EndDate Indicator
1001 A121 02/01/2022 02/15/2022 N
1001 B121 03/10/2022 03/10/2022 N
1002 C121 02/01/2022 02/10/2022 Y
1002 D121 02/05/2022 02/15/2022 Y
1003 E121 03/10/2022 03/21/2022 Y
1003 F121 03/12/2022 03/21/2022 Y
1004 G121 01/12/2022 01/14/2022 N
I tried sorting the data first on StrDate and EndDate
Proc sort data=Data; by ID StrDate EndDate;run;
Then I tried using lag function to find the same id and subtract the dates but I figured that's not the correct way of doing.
I appreciate your help here. thanks
SAS Date values are integers that can be used as an index into a tracking array. This technique is called a direct-index search.
Example:
A double DOW solution can be coded to find the overlapping records. The first loop flags dates in use and the second loop evaluates the range for an overlap by finding a flag via direct-index.
data have;
input ID ITEM $ StrDate EndDate;
attrib strdate enddate format=mmddyy10. informat=mmddyy10.;
datalines;
1001 A121 02/01/2022 02/15/2022
1001 B121 03/10/2022 03/10/2022
1002 C121 02/01/2022 02/10/2022
1002 D121 02/05/2022 02/15/2022
1003 E121 03/10/2022 03/21/2022
1003 F121 03/12/2022 03/21/2022
1004 G121 01/12/2022 01/14/2022
;
data want;
array tracker(100000) _temporary_ ;
do _n_ = 1 by 1 until (last.id);
set have;
by id;
do _i_ = strdate to enddate;
tracker(_i_) + 1; /* flag date using direct-index */
end;
end;
do _n_ = 1 to _n_;
set have;
/* no overlap would mean no dates in range would find a flag set */
/* and loop would exit with _i_ > enddate */
do _i_ = strdate to enddate while (tracker(_i_) = 1);
end;
length overlap_indicator $1;
overlap_indicator = ifc (_i_ > enddate, 'N', 'Y');
output;
end;
call missing (of tracker(*));
drop _: ;
run;
Extend, count and remerge, this is my thought.
*An extra observation added to ID 1002;
data have;
input ID $ ITEM $ StrDate mmddyy10. +1 EndDate mmddyy10.;
format StrDate EndDate mmddyy10.;
cards;
1001 A121 02/01/2022 02/15/2022
1001 B121 03/10/2022 03/10/2022
1002 C121 02/01/2022 02/10/2022
1002 D121 02/05/2022 02/15/2022
1002 D121 03/05/2022 03/15/2022
1003 E121 03/10/2022 03/21/2022
1003 F121 03/12/2022 03/21/2022
1004 G121 01/12/2022 01/14/2022
;
run;
*Extend;
data middle;
set have;
do date=StrDate to EndDate;
output;
end;
run;
*Count and remerge;
proc sql noprint;
create table want as
select distinct a.*, ifc(b.count and a.StrDate<=b.date<=a.EndDate,'Y','N') as Indicator
from have as a
left join (
select id, date, count(date) as count from middle
group by id, date
having count>1
) as b on a.id=b.id
;
quit;
By the way, if not all records overlapping in dates of one ID but you want to flag all of them out, you need to modify table lookup condition by removing the a.StrDate<=b.date<=a.EndDate.
Simple overlap logic:
proc sql;
create table want as
select
a.*,
/* simple overlap logic */
case
when a.strdate <= b.strdate & a.enddate >= b.strdate then 'Y'
when b.strdate < a.strdate & b.enddate >= a.strdate then 'Y'
else 'N'
end as overlap
from
have a
left join
have b
on a.id = b.id /* join on same ids */
and a.item <> b.item /* but not the same item */
;
quit;
Result:
ID ITEM StrDate EndDate overlap
1001 B121 03/10/2022 03/10/2022 N
1001 A121 02/01/2022 02/15/2022 N
1002 D121 02/05/2022 02/15/2022 Y
1002 C121 02/01/2022 02/10/2022 Y
1003 E121 03/10/2022 03/21/2022 Y
1003 F121 03/12/2022 03/21/2022 Y
1004 G121 01/12/2022 01/14/2022 N
Overlap occurs if StartA <= StartB when:
StartA EndA>=StartB
|-------------|
|---------
StartB

Pivot Table in SQL (using Groupby)

I have a table structured as below
Customer_ID Sequence Comment_Code Comment
1 10 0 a
1 11 1 b
1 12 1 c
1 13 1 d
2 20 0 x
2 21 1 y
3 100 0 m
3 101 1 n
3 102 1 o
1 52 0 t
1 53 1 y
1 54 1 u
Sequence number is the unique number in the table
I want the output in SQL as below
Customer_ID Sequence
1 abcd
2 xy
3 mno
1 tyu
Can someone please help me with this. I can provide more details if required.
enter image description here
This looks like a simple gaps/islands problem.
-- Sample Data
DECLARE #table TABLE
(
Customer_ID INT,
[Sequence] INT,
Comment_Code INT,
Comment CHAR(1)
);
INSERT #table
(
Customer_ID,
[Sequence],
Comment_Code,
Comment
)
VALUES (1,10 ,0,'a'),(1,11 ,1,'b'),(1,12 ,1,'c'),(1,13 ,1,'d'),(2,20 ,0,'x'),(2,21 ,1,'y'),
(3,100,0,'m'),(3,101,1,'n'),(3,102,1,'o'),(1,52 ,0,'t'),(1,53 ,1,'y'),(1,54 ,1,'u');
-- Solution
WITH groups AS
(
SELECT
t.Customer_ID,
Grouper = [Sequence] - DENSE_RANK() OVER (ORDER BY [Sequence]),
t.Comment
FROM #table AS t
)
SELECT
g.Customer_ID,
[Sequence] =
(
SELECT g2.Comment+''
FROM groups AS g2
WHERE g.Customer_ID = g2.Customer_ID AND g.Grouper = g2.Grouper
FOR XML PATH('')
)
FROM groups AS g
GROUP BY g.Customer_ID, g.Grouper;
Returns:
Customer_ID Sequence
----------- ----------
1 abcd
1 tyu
2 xy
3 mno

How to count the number of unique values in column B associated with each value in column A?

My Data resembles something like this
Column A Column B
101 1001
101 1002
101 1003
101 1004
102 1001
102 1005
102 1006
101 1001
102 1001
Expected Output is like this
column_a unique_column_b_vals
101 4
102 3
Knowing that COUNT function supports a distinct argument
http://www.postgresqltutorial.com/postgresql-count-function/
select column_a , count(distinct column_b)
from f1
group by column_a

SQL Server - Renumber in Order

I have a table that I need to reorder a column, but I need to keep the original order by date.
TABLE_1
id num_seq DateTimeStamp
fb4e1683-7035-4895-b2c8-d084d9b42ce3 111 08-02-2005
e40e4c3e-65e4-47b7-b13a-79e8bce2d02d 114 10-07-2017
49e261a8-a855-4844-a0ac-37b313da2222 113 01-30-2010
6c4bffb7-a056-4a20-ae1c-5a31bdf683f2 112 04-15-2006
I want to reorder num_seq starting with 1001 through 1004 and keep the numbering in order. So 111 = 1001 and 112 = 1002 and so forth.
This is what I have so far:
DECLARE #num INT
SET #num = 0
UPDATE Table_1
SET #num = num_seq = #id + 1
GO
I know that UPDATE doesn't let me use the keyword ORDER BY. Is there a way to do this in SQL 2008 R2?
Stage the new num_seq in a CTE, then leverage that in your update statement:
declare #Table_1 table (id uniqueidentifier, num_seq int, DateTimeStamp datetime);
insert into #Table_1
values
('fb4e1683-7035-4895-b2c8-d084d9b42ce3', 111, '08-02-2005'),
('e40e4c3e-65e4-47b7-b13a-79e8bce2d02d', 114, '10-07-2017'),
('49e261a8-a855-4844-a0ac-37b313da2222', 113, '01-30-2010'),
('6c4bffb7-a056-4a20-ae1c-5a31bdf683f2', 112, '04-15-2006');
;with stage as
(
select *,
num_seq_new = 1000 + row_number()over(order by DateTimeStamp asc)
from #Table_1
)
update stage
set num_seq = num_seq_new;
select * from #Table_1
Returns:
id num_seq DateTimeStamp
FB4E1683-7035-4895-B2C8-D084D9B42CE3 1001 2005-08-02 00:00:00.000
E40E4C3E-65E4-47B7-B13A-79E8BCE2D02D 1004 2017-10-07 00:00:00.000
49E261A8-A855-4844-A0AC-37B313DA2222 1003 2010-01-30 00:00:00.000
6C4BFFB7-A056-4A20-AE1C-5A31BDF683F2 1002 2006-04-15 00:00:00.000

SQL - Pivot tables with 3 cross tabs

I have the raw data looks like
Class Cert Name Benefit Coverage
-------------------------------
1 1001 ABC EHC Family
1 1001 ABC DEN Family
2 1002 XYZ EHC Single
2 1002 XYZ DEN Single
3 1003 LMN EHC Couple
3 1003 LMN DEN Couple.
I want the final output to look like
**Class** **Benefit**
EHC-Single EHC-Couple EHC-Family DEN-Single DEN-Couple DEN-Family
1 1 1
2 1 1
3 1 1
Values below the columns are count of Certificates.
yes you can do it like below. See in SQL Fiddle
;WITH CTE
AS (SELECT COUNT(*) Counts,
Class,
Benefit + '-' + Coverage AS [Benefits]
FROM ##MyTemp
GROUP BY Class,
Benefit,
Coverage)
SELECT Class,
[EHC-Single],
[EHC-Couple],
[EHC-Family],
[DEN-Single],
[DEN-Couple],
[DEN-Family]
FROM CTE
PIVOT(MAX(Counts)
FOR [Benefits] IN ([EHC-Single],
[EHC-Couple],
[EHC-Family],
[DEN-Single],
[DEN-Couple],
[DEN-Family])) AS TempList;