Update JSON value - MSSQL - sql-server

In MSSQL I need to update the actualWatchedTime with totalDuration.
Current Table
Id VideoId UserId ProgressJson
1 1 1 {"actualWatchedTime":228,"currentWatchTime":3,"totalDuration":657}
2 2 1 {"actualWatchedTime":328,"currentWatchTime":23,"totalDuration":349}
3 3 1 {"actualWatchedTime":28,"currentWatchTime":2,"totalDuration":576}
4 1 2 {"actualWatchedTime":82,"currentWatchTime":103,"totalDuration":576}
5 2 2 {"actualWatchedTime":280,"currentWatchTime":253,"totalDuration":456}
Expected Table
Id VideoId UserId ProgressJson
1 1 1 {"actualWatchedTime":657,"currentWatchTime":3,"totalDuration":657}
2 2 1 {"actualWatchedTime":349,"currentWatchTime":23,"totalDuration":349}
3 3 1 {"actualWatchedTime":576,"currentWatchTime":2,"totalDuration":576}
4 1 2 {"actualWatchedTime":576,"currentWatchTime":103,"totalDuration":576}
5 2 2 {"actualWatchedTime":456,"currentWatchTime":253,"totalDuration":456}
How do I do that?

declare #t table
(
id int,
ProgressJson nvarchar(500)
);
insert into #t(id, ProgressJson)
values
(1, N'{"actualWatchedTime":228,"currentWatchTime":3,"totalDuration":657}'),
(2, N'{"actualWatchedTime":328,"currentWatchTime":23,"totalDuration":349}'),
(3, N'{"actualWatchedTime":28,"currentWatchTime":2,"totalDuration":576}'),
(4, N'{"actualWatchedTime":82,"currentWatchTime":103,"totalDuration":576}'),
(5, N'{"actualWatchedTime":280,"currentWatchTime":253,"totalDuration":456}');
select *
from #t;
update #t
set ProgressJson = JSON_MODIFY(ProgressJson,'$.actualWatchedTime', cast(json_value(ProgressJson, '$.totalDuration') as int));
select *
from #t;

Related

Create comma separated value strings using data from different tables in SQL Server

I have the following database model:
criteria table:
criteria_id criteria_name is_range
1 product_category 0
2 product_subcategory 0
3 items 1
4 criteria_4 1
evaluation_grid table:
evaluation_grid_id criteria_id start_value end_value provider property_1 property_2 property_3
1 1 3 NULL internal 1 1 1
2 1 1 NULL internal 1 1 1
3 2 1 NULL internal 1 2 1
4 3 1 100 internal 2 1 1
5 4 1 50 internal 2 2 1
6 1 2 NULL external 2 8 1
7 2 2 NULL external 2 5 1
8 3 1 150 external 2 2 2
9 3 1 100 external 2 3 1
product_category table:
id name
1 test1
2 test2
3 test3
product_subcategory table:
id name
1 producttest1
2 producttest2
3 producttest3
What I am trying to achieve is returning the values like this:
criteria start_value end_value provider property_1 property_2 property_3
product_category test3, test1 NULL internal 1 1 1
product_subcategory producttest1 NULL internal 1 2 1
items 1 100 internal 2 1 1
criteria_4 1 50 internal 2 2 1
product_category test2 NULL external 2 8 1
product_subcategory producttest2 NULL external 2 5 1
items 1 150 external 2 2 2
criteria_4 1 100 external 2 3 1
Basically keeping the order from table evaluation_grid but grouping only the criterias which are not ranges
in comma separated value strings based on start_value, end_value, provier, property_1, property_2 and property_3
I tried like this:
SELECT c.criteria_name AS criteria
,CASE WHEN c.criteria_id = 1
THEN
(IsNull(STUFF((SELECT ', ' + RTRIM(LTRIM(pc.name))
FROM product_category pc
INNER JOIN [evaluation_grid] eg ON eg.start_value=pc.id
WHERE srsg.criteria_id=c.criteria_id
FOR XML PATH('')), 1, 2, ''), ''))
WHEN c.criteria_id = 2
THEN (IsNull(STUFF((SELECT ' , ' + RTRIM(LTRIM(psc.name))
FROM product_subcategory psc
INNER JOIN [evaluation_grid] eg ON eg.start_value=psc.id
WHERE srsg.criteria_id=c.criteria_id
FOR XML PATH('')
), 1, 3, ''), ''))
ELSE
CAST(eg.start_value AS VARCHAR)
END AS start_value
,eg.end_value AS end_value
,eg.provider AS provider
,eg.property_1 AS property_1
,eg.property_2 AS property_2
,eg.property_3 AS property_3
FROM [evaluation_grid] eg
INNER JOIN criteria c ON eg.criteria_id = crs.criteria_id
GROUP BY c.criteria_name,c.criteria_id,c.is_range,eg.start_value,eg.end_value,eg.provider,eg.property_1,eg.property_2,eg.property_3
But it is returning wrong data, like this:
criteria start_value end_value provider property_1 property_2 property_3
product_category test3, test1, test2 NULL internal 1 1 1
product_category test3, test1, test2 NULL external 2 8 1
product_category test3, test1, test2 NULL internal 1 1 1
product_subcategory producttest1,producttest2 NULL internal 1 2 1
product_subcategory producttest1,producttest2 NULL external 2 5 1
items 1 100 internal 1 1 1
items 1 150 external 2 2 2
criteria_4 1 50 internal 2 2 1
criteria_4 1 100 external 2 3 1
I tried some versions with "with cte;" as well but didn't manage to find the solution yet and yes, I checked the similar questions already. :)
PS: I cannot use STRING_AGG because we have below 2017 Sql Server version.
Any suggestion will be highly appreciated, thanks !
As far as I can tell this query returns the exact output you're looking for.
with cte as (
select c.criteria_name,
eg.evaluation_grid_id,
case when c.criteria_id = 1 then pc.[name]
when c.criteria_id = 2 then psc.[name]
else null end pc_cat,
c.criteria_id,c.is_range, eg.start_value, eg.end_value,
eg.[provider], eg.property_1, eg.property_2,eg.property_3
from #evaluation_grid eg
join #criteria c ON eg.criteria_id = c.criteria_id
left join #product_category pc on eg.start_value=pc.id
left join #product_subcategory psc on eg.start_value=psc.id)
select c.criteria_name as criteria,
case when c.is_range=0 then
STUFF((SELECT ', ' + RTRIM(LTRIM(c2.pc_cat))
FROM cte c2
WHERE c2.criteria_id=c.criteria_id
and c2.is_range=c.is_range
and c2.[provider]=c.[provider]
and c2.property_1=c.property_1
and c2.property_2=c.property_2
and c2.property_3=c.property_3
FOR XML PATH('')), 1, 2, '')
else max(cast(c.start_value as varchar(50))) end as start_value,
c.end_value, c.[provider], c.property_1, c.property_2, c.property_3
from cte c
group by c.criteria_name, c.criteria_id, c.is_range, c.end_value,
c.[provider], c.property_1, c.property_2, c.property_3
order by max(c.evaluation_grid_id);
Output
criteria start_value end_value provider property_1 property_2 property_3
product_category test3, test1 NULL internal 1 1 1
product_subcategory producttest1 NULL internal 1 2 1
items 1 100 internal 2 1 1
criteria_4 1 50 internal 2 2 1
product_category test2 NULL external 2 8 1
product_subcategory producttest2 NULL external 2 5 1
items 1 150 external 2 2 2
criteria_4 1 100 external 2 3 1
It's a bit difficult to follow the requirements. Can you review the setup & results below and let us know what the desired resultset should be?
declare #criteria table (criteria_id int, criteria_name varchar(50), is_range bit)
insert into #criteria
values(1, 'product_category', 0), (2, 'product_subcategory', 0), (3, 'items', 1), (4, 'criteria_4', 1);
declare #evaluation_grid table (evaluation_grid_id int, criteria_id int, start_value int, end_value int, [provider] varchar(50), property_1 int, property_2 int, property_3 int);
insert into #evaluation_grid
values
(1, 1, 3, NULL, 'internal', 1, 1, 1),
(2, 1, 1, NULL, 'internal', 1, 1, 1),
(3, 2, 1, NULL, 'internal', 1, 2, 1),
(4, 3, 1, 100, 'internal', 2, 1, 1),
(5, 4, 1, 50, 'internal', 2, 2, 1),
(6, 1, 2, NULL, 'external', 2, 8, 1),
(7, 2, 2, NULL, 'external', 2, 5, 1),
(8, 3, 1, 150, 'external', 2, 2, 2),
(9, 4, 1, 100, 'external', 2, 3, 1)
declare #product_category table (id int, [name] varchar(50))
insert into #product_category
values (1, 'test1'), (2, 'test2'), (3, 'test3'), (4, 'test4');
declare #product_subcategory table (id int, [name] varchar(50))
insert into #product_subcategory
values (1, 'producttest1'), (2, 'producttest2'), (3, 'producttest3');
select c.criteria_name,
stuff(( select ',' + ipc.[name]
from #evaluation_grid ieg
join #product_category ipc on ieg.start_value = ipc.id
where [provider] = eg.[provider] and property_1 = eg.property_1 and property_2 = eg.property_2 and property_3 = eg.property_3
order by ieg.evaluation_grid_id
for xml path('')), 1 ,1, '') as start_value,
end_value,
[provider],
property_1,
property_2,
property_3
from #evaluation_grid eg
join #criteria c on eg.criteria_id = c.criteria_id
where c.is_range = 0
group
by c.criteria_name, end_value, [provider], property_1, property_2, property_3
union all
select c.criteria_name, cast(start_value as varchar(10)), end_value, [provider], property_1, property_2, property_3
from #evaluation_grid eg
join #criteria c on eg.criteria_id = c.criteria_id
where c.is_range = 1;

Rank or merge sequential rows

I have a log file I need to either rank (but treating sequential and equal rows as ties), or merge sequential equal rows (based on specific column). My table looks like below, The Start and Stop are all being sequential (within the same ID window)
ID Start Stop Value
1 0 1 A
1 1 2 A
1 2 3 A
1 3 4 B
1 4 5 B
1 5 6 A
2 3 4 A
I have two approches to get what I need.
Approach 1: Rank (treating sequential rows with equal values in "Value" as ties) and using ID as partition.
This should give the output below. But how do I do the special rank: Treating sequential rows with equal values in "Value" as ties.
Select *,
rank() OVER (partition by id order by start, stop) as Rank,
XXX as SpecialRank
from Table
ID Start Stop Value Rank SpecialRank
1 0 1 A 1 1
1 1 2 A 2 1
1 2 3 A 3 1
1 3 4 B 4 2
1 4 5 B 5 2
1 5 6 A 6 3
2 3 4 A 1 1
Approach 2: Merge sequential rows with equal values in "Value".
This will shall create a table like below.
ID Start Stop Value
1 0 3 A
1 3 5 B
1 5 6 A
2 3 4 A
I don't know if this helps, but I have also a nextValue column that might help in this
ID Start Stop Value NextValue
1 0 1 A A
1 1 2 A A
1 2 3 A B
1 3 4 B B
1 4 5 B A
1 5 6 A A
2 3 4 A ...
Example-table:
CREATE TABLE #Table ( id int, start int, stop int, Value char(1), NextValue char(1));
INSERT INTO #Table values (1,0, 1, 'A', 'A');
INSERT INTO #Table values (1,1, 2, 'A', 'A');
INSERT INTO #Table values (1,2, 3, 'A', 'B');
INSERT INTO #Table values (1,3, 4, 'B', 'B');
INSERT INTO #Table values (1,4, 5, 'B', 'A');
INSERT INTO #Table values (1,5, 6, 'A', 'A');
INSERT INTO #Table values (2,3, 4, 'A', null);
Use a self join to an aggregate subquery from the full set, e.g.
with rankTable (id, value) as
( select 1, 'A' union all select 1, 'A' union all select 1, 'B' union all select 2, 'A')
select t2.* from rankTable t1 join (
select id, value, rank() over (partition by id order by value) as specialRank from
(
select distinct id, value
from rankTable
) t) t2 on t2.id =t1.id and t2.value = t1.value
id value specialRank
1 A 1
1 A 1
1 B 2
2 A 1

Filter a table's records based on its other records

First, I apologize if the title won't make sense but below is the detailed scenario.
Say I have a document_revision table
id document_id phase_id user_id
1 1 3 1
2 1 2 1
3 1 1 1
4 2 3 2
5 2 2 2
where phase_id is: transcribe = 3; proof = 2; and submit = 1.
I would like to write a query where I can filter the revision records where I will disregard a proof phase if the same user did the transcribe and proof. So the output would be:
id document_id phase_id user_id
1 1 3 1
3 1 1 1
4 2 3 2
I've been struggling for hours figuring out a query for this but no luck so far.
Assuming you only want the phase 3 for any case where a user_id was involved in phase 2 and 3, then one way you could do this is with ROW_NUMBER(), e.g.:
DECLARE #T TABLE (ID INT IDENTITY(1, 1), Document_ID INT, Phase_ID INT, [User_ID] INT);
INSERT #T (Document_ID, Phase_ID, [User_ID]) VALUES
(1, 1, 1), (1, 2, 1), (1, 3, 1), (2, 3, 2), (2, 2, 2), (3, 1, 1), (3, 2, 1), (3, 3, 2);
SELECT ID, Document_ID, Phase_ID, [User_ID]
FROM
(
SELECT *, RN = ROW_NUMBER() OVER (PARTITION BY Document_ID, [User_ID], CASE WHEN Phase_ID IN (2, 3) THEN 2 ELSE Phase_ID END ORDER BY Phase_ID DESC)
FROM #T
) AS T
WHERE RN = 1;
DECLARE #document_revision TABLE (
id INT IDENTITY(1,1),
document_id INT,
phase_id INT,
user_id INT
);
INSERT INTO #document_revision
(document_id, phase_id, user_id)
VALUES
(1, 3, 1),
(1, 2, 1),
(1, 1, 1),
(2, 3, 2),
(2, 2, 2),
-- To test a scenario where there is a proof and a submit with no transcribe phases and same document
(3, 2, 3),
(3, 1, 3),
-- To test a scenario where there is a transcribe and a submit with no proof phases and same document
(4, 3, 4),
(4, 1, 4),
-- To test a scenario where there is a proof and a submit with no transcribe phase (for document_id 5) but different document and same user as above
(5, 2, 4);
SELECT dr.id
, dr.document_id
, dr.phase_id
, dr.user_id
FROM #document_revision AS dr
WHERE NOT EXISTS ( SELECT 1
FROM #document_revision AS temp
-- Same user
WHERE temp.user_id = dr.user_id
-- Same document
AND temp.document_id = dr.document_id
-- To check if there is already a transcribe phase_id with the same user_id and document_id
AND temp.phase_id = 3
-- -- To check if there is already a proof phase_id with the same user_id and document_id
AND dr.phase_id = 2 )
results:
id document_id phase_id user_id
1 1 3 1
3 1 1 1
4 2 3 2
6 3 2 3
7 3 1 3
8 4 3 4
9 4 1 4
10 5 2 4

Separate a group of values in to A or B (1 or 2) SQL

Note: I have already asked a similar question but had omitted a key part in the fact that a tool has many components.
I have a list of multiple tools and their components that all have a model number. I want to group every second tool based on the model it belongs to.
The derivedColumn is the query I want to return
declare #t table (Model int, toolID INT ,Component INT,DerivedColumn int);
insert into #t values (1,1,1,1),(1,1,2,1),(1,1,3,1),(1,2,1,2),(1,2,2,2),(1,2,3,2),(1,3,1,1),(1,3,2,1),(1,3,3,1),(1,4,1,2),(1,4,2,2),(1,4,3,2),(1,5,1,1),(1,5,2,1),(1,5,3,1),(2,1,1,1),(2,1,2,1),(2,2,1,2),(2,2,2,2),(2,3,1,1),(2,3,2,1)
SELECT * FROM #t
Model toolID Component DerivedColumn
1 1 1 1
1 1 2 1
1 1 3 1
1 2 1 2
1 2 2 2
1 2 3 2
1 3 1 1
1 3 2 1
1 3 3 1
1 4 1 2
1 4 2 2
1 4 3 2
1 5 1 1
1 5 2 1
1 5 3 1
2 1 1 1
2 1 2 1
2 2 1 2
2 2 2 2
2 3 1 1
2 3 2 1
Every second tool belonging to a model should have an alternative group number.
I believe I have to use a windows function but haven't been able to solve.
You could use dense_rank() and mod function %2 to calculate
DECLARE #SampleData AS TABLE
(
Model int,
ToolId int,
Component int
)
INSERT INTO #SampleData
(
Model, ToolId, Component
)
VALUES
(1, 1, 1),(1, 1, 2),(1, 1, 3),(1, 2, 1),
(1, 2, 2),(1, 2, 3),(1, 3, 1),(1, 3, 2),
(1, 3, 3),(1, 4, 1),(1, 4, 2),(1, 4, 3),
(1, 5, 1),(1, 5, 2),(1, 5, 3),(2, 1, 1),
(2, 1, 2),(2, 2, 1),(2, 2, 2),(2, 3, 1),
(2, 3, 2)
SELECT *,
CASE (dense_rank() OVER(PARTITION BY sd.Model ORDER BY sd.ToolId) + 1) % 2
WHEN 1 THEN 2
WHEN 0 THEN 1
END as DerivedColumn
FROM #SampleData sd
ORDER BY sd.Model, sd.ToolId
Demo link: http://rextester.com/LIQL79881
Hope it may helps you
DECLARE #T TABLE (Model INT, toolID INT ,Component INT,DerivedColumn INT);
INSERT INTO #T VALUES (1,1,1,1),(1,1,2,1),(1,1,3,1),(1,2,1,2),(1,2,2,2),(1,2,3,2),(1,3,1,1),(1,3,2,1),(1,3,3,1),(1,4,1,2),(1,4,2,2),(1,4,3,2),(1,5,1,1),(1,5,2,1),(1,5,3,1),(2,1,1,1),(2,1,2,1),(2,2,1,2),(2,2,2,2),(2,3,1,1),(2,3,2,1)
SELECT Model
,toolID
,ROW_NUMBER()Over(Partition by toolID order by Model) AS AlternativetoolID
,Component
,DerivedColumn
from #t;

How to auto increment update a column in a SQL Server table

How to auto increment update a column in a SQL Server table based on sorting other columns and restart the increment after value changes in other columns.
My table structure and data is like that:
Name Class OldSrNo NewSrNo
----------------------------
aa 1 1 1
bb 1 2 2
aa 1 3 3
bb 2 4 1
cc 2 5 2
dd 2 6 3
aa 2 7 4
I want to update old sr no to look like NewSrNo
Are you asking this,
Declare #t table (Name varchar(50), Class int, OldSrNo int)
insert into #t values
('aa', 1, 1)
,('bb', 1, 2)
,('aa', 1, 3)
,('bb', 2, 4)
,('cc', 2, 5)
,('dd', 2, 6)
,('aa', 2, 7)
select *,
row_number()over(partition by class order by class)NewSrNo
from #t

Resources