I'm struggling to structure JSON using SQL.
Say I have a table like this:
| col1 | col2 | col3 |
+---------------+----------+----------+
| specialvalue | someval | someval |
| specialvalue2 | someval2 | someval2 |
| | | |
I'm trying to get a structure like the following:
{
"specialvalue": {
"specialcol": "specialvalue",
"col2": "someval",
"col3": "someval"
},
"specialvalue2": {
"specialcol": "specialvalue2",
"col2": "someval2",
"col3": "someval2"
}
}
How can I accomplish this? Can I use JSON_MODIFY with dynamic keys, while mapping through every row in the set?
The closest I've gotten is the following:
SELECT
specialcol,
col2,
col3
INTO #tmpTbl
FROM myTable
SELECT
specialcol,
(SELECT * FROM #tmpTbl FOR JSON AUTO) as 'Value'
FROM #tmpTbl
FOR JSON AUTO
DROP TABLE #tmp
Which returns the following:
{
"specialcol":"specialvalue",
"Value":{
"col1": "specialvalue",
"col2": "someval",
"col3": "someval"
},
"specialcol":"specialvalue2",
"Value":{
"col1": "specialvalue2",
"col2": "someval2",
"col3": "someval2"
}
}
Which is close, but not quite what I need.
Is there a way to use JSON_MODIFY to accomplish what I'm trying to get?
From my comment and thinking about this. Not the most elegant, but could potential get you there.
Inline query to get the results in json, then wrap those results based on the value from your col1 as the key. Put it all together comma delimited and then a final wrap in brackets.
DECLARE #TestData TABLE
(
[col1] NVARCHAR(100)
, [col2] NVARCHAR(100)
, [col3] NVARCHAR(100)
);
DECLARE #JsonValue NVARCHAR(MAX) = '';
INSERT INTO #TestData (
[col1]
, [col2]
, [col3]
)
VALUES ( 'specialvalue', 'someval', 'someval' )
, ( 'specialvalue2', 'someval2', 'someval2' );
SELECT #JsonValue = #JsonValue + N'"' + [a].[col1] + N'": '
+ (
SELECT [aa].[col1] AS 'specialvalue'
, [aa].[col2] AS 'col2'
, [aa].[col3] AS 'col3'
FROM #TestData [aa]
WHERE [aa].[col1] = [a].[col1]
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
) + N','
FROM #TestData [a];
SET #JsonValue = N'{' + SUBSTRING(#JsonValue, 1, LEN(#JsonValue) - 1) + N'}';
SELECT #JsonValue;
Gets you this:
{
"specialvalue": {
"specialvalue": "specialvalue",
"col2": "someval",
"col3": "someval"
},
"specialvalue2": {
"specialvalue": "specialvalue2",
"col2": "someval2",
"col3": "someval2"
}
}
SQL Server 2017 -
;WITH cte as (
select 'specialvalue' as specialcol, 'someval' col2, 'someval' col3 union
select 'specialvalue2' as specialcol, 'someval2' col2, 'someval2' col3 )
SELECT
CASE WHEN specialcol = 'specialvalue' THEN specialcol END as [specialvalue.specialcol],
CASE WHEN specialcol = 'specialvalue' THEN col2 END as [specialvalue.col2],
CASE WHEN specialcol = 'specialvalue' THEN col3 END as [specialvalue.col3],
CASE WHEN specialcol = 'specialvalue2' THEN specialcol END as [specialvalue2.specialcol],
CASE WHEN specialcol = 'specialvalue2' THEN col2 END as [specialvalue2.col2],
CASE WHEN specialcol = 'specialvalue2' THEN col3 END as [specialvalue2.col3]
FROM cte
FOR JSON PATH
Output:
{
"specialvalue":{
"specialcol":"specialvalue",
"col2":"someval",
"col3":"someval"
}
},
{
"specialvalue2":{
"specialcol":"specialvalue2",
"col2":"someval2",
"col3":"someval2"
}
}
Related
I have a table "jobs" with one of the columns called "check_list" ( varchar(max) that has JSON values, an example value would be
{
"items":[
{
"name":"machine 1",
"state":"",
"comment":"",
"isReleaseToProductionCheck":true,
"mnachine_id":10
},
{
"name":"machine 2",
"state":"",
"comment":"",
"isReleaseToProductionCheck":true,
"machine_id":12
}
]
}
Now how would I write a SQL query to only return the rows where the column "check_list" has items[machine_id] = 12
In the end after some trial and error this was the solution that worked for me. I had to add the ISJSON check because some of the older data was invalid
WITH jobs (id, workorder, selectedMachine) AS(
SELECT
[id],
[workorder],
(
select
*
from
openjson(check_list, '$.items') with (machine_id int '$.machine_id')
where
machine_id = 12
) as selectedMachine
FROM
engineering_job_schedule
WHERE
ISJSON(check_list) > 0
)
Select
*
from
jobs
where
selectedMachine = 12
how can i replace values in string with values that are in a table?
for example
select *
into #t
from
(
select 'bla'c1,'' c2 union all
select 'table'c1,'TABLE' c2 union all
select 'value'c1,'000' c2 union all
select '...'c1,'' c2
)t1
declare #s nvarchaR(max)='this my string and i want to replace all values that are in table #t'
i have some values in my table and i want to replace C1 with C2 in my string.
the results should be
this my string and i want to replace all 000 that are in TABLE #t
UPDATE:
i solved with a CLR
using System;
using Microsoft.SqlServer.Server;
using System.Data.SqlTypes;
using System.Data.Linq;
namespace ReplaceValues
{
public partial class Functions
{
[SqlFunction
(
//DataAccess = DataAccessKind.Read,
SystemDataAccess = SystemDataAccessKind.Read
)
]
public static string ReplaceValues(string row, string delimitator, string values, string replace/*, bool CaseSensitive*/)
{
//return row;
string[] tmp_values = values.Split(new string[] { delimitator }, StringSplitOptions.None);
string[] tmp_replace = replace.Split(new string[] { delimitator }, StringSplitOptions.None);
row = row.ToUpper();
for (int i = 0; i < Math.Min(tmp_values.Length, tmp_replace.Length); i++)
{
row = row.Replace(tmp_values[i].ToUpper(), tmp_replace[i]);
}
return row;
}
}
}
and then
select *
into #t
from
(
select 'value1'OldValue,'one'NewValue union all
select 'value2'OldValue,'two'NewValue union all
select 'value3'OldValue,'three'NewValue union all
select 'value4'OldValue,'four'NewValue
)t1
select dbo.ReplaceValues(t1.column,'|',t2.v,t2.r)
from MyTable t1
cross apply
(
select dbo.inlineaggr(i1.OldValue,'|',1,1)v,
dbo.inlineaggr(i1.NewValue,'|',1,1)r
from #t i1
)t2
i have to improved it to manage better the case sensitive, but performance are not bad.
(also 'inlineaggr' is a CLR i wrote years ago)
You can do this via recursion. Assuming you have a table of find-replace pairs, you can number the rows and then use recursive cte:
create table #t(c1 nvarchar(100), c2 nvarchar(100));
insert into #t(c1, c2) values
('bla', ''),
('table', 'table'),
('value', '000'),
('...', '');
declare #s nvarchar(max) = 'this my string and i want to replace all values that are in table #t';
with ncte as (
select row_number() over (order by (select null)) as rn, *
from #t
), rcte as (
select rn, replace(#s, c1, c2) as newstr
from ncte
where rn = 1
union all
select ncte.rn, replace(rcte.newstr, ncte.c1, ncte.c2)
from ncte
join rcte on ncte.rn = rcte.rn + 1
)
select *
from rcte
where rn = 4
I'm in the progress of migrating data from an old system. I have some boolean columns, which can be True, False or Null. Each column has a certain meaning. In the new database, I use a JSON Data object. What I would like to achieve is this:
OldTable:
ValueA, ValueB, ValueC, ValueD
Null , True , False , True
I create now Json data using a select on my old table and FOR JSON to get the Json object. The object should look something like this:
{
myObject: {
data: "valueB, ValueD"
}
}
My problem is the appending the values to the same "data" field.
So right now I can only do it with single values like this:
SELECT CASE WHEN ValueA == True THEN 'ValueA' END 'myObject.data',
CASE WHEN ValueB == True THEN 'ValueB' END 'myObject.data'
FROM myLegacyTable FOR JSON Path
This would obviously overwrite, what ever is in myObject.data, whenever both values are true. JSON_MODIFY seems not to be an option, as I'm not working on an existing Json object, but creating a new one. Maybe someone else has an idea?
You can just build your json string with a CASE WHEN. If you want to handle a variable number of columns you'll probably need dynamic TSQL.
This is a simple static version that should do the job:
declare #tmp table(ValueA bit, ValueB bit, ValueC bit, ValueD bit)
insert into #tmp values (null, 1 ,0 , 1)
SELECT JSON_QUERY('{"data":"' +
stuff(
case when ValueA = 1 then ',ValueA' else '' end
+ case when ValueB = 1 then ',ValueB' else '' end
+ case when ValueC = 1 then ',ValueC' else '' end
+ case when ValueD = 1 then ',ValueD' else '' end
,1,1,'')
+ '"}') as myObject
FROM #tmp FOR JSON Path, without_array_wrapper
Result:
I don't know how your source data is really stored but here is an example of what you need to do if this was all in SQL Server
data.property is not seperate items so don't split it into columns. It's just a single concatenated string. So concatenate everything up and remove the trailing comma.
DECLARE #MyTable TABLE (
RowID INT,
ValueA VARCHAR(5),
ValueB VARCHAR(5),
ValueC VARCHAR(5),
ValueD VARCHAR(5)
)
INSERT INTO #MyTable (RowID,ValueA,ValueB,ValueC,ValueD) VALUES
(1,NULL,'True','False','True'),
(2,'False',NULL,'True','True'),
(3,NULL,'False',NULL,NULL)
SELECT
ISNULL(
RTRIM(REVERSE(STUFF(REVERSE
(
CASE WHEN ValueA='True' THEN 'ValueA, ' ELSE '' END +
CASE WHEN ValueB='True' THEN 'ValueB, ' ELSE '' END +
CASE WHEN ValueC='True' THEN 'ValueC, ' ELSE '' END +
CASE WHEN ValueD='True' THEN 'ValueD, ' ELSE '' END
), 2, 1, ''))),'') AS 'myObject.data'
FROM #MyTable
FOR JSON Path
The huge construct required to remove trailing , is from here:
Remove the last character in a string in T-SQL?
Result:
[
{
"myObject": {
"data": "ValueB, ValueD"
}
},
{
"myObject": {
"data": "ValueC, ValueD"
}
},
{
"myObject": {
"data": ""
}
}
]
I'm just trying to set a query to get data from a collection of object JSON:
create table test (LINE_SPECS nvarchar(max));
insert into test values (N'
{
"lineName":"GHjr",
"pipeDiameter":"12",
"pipeLength":"52000",
"pressure":"15",
"volume":"107"
},
{
"lineName":"Ks3R",
"pipeDiameter":"9",
"pipeLength":"40000",
"pressure":"15",
"volume":"80"
}
');
Now, as getting lineName of the first object ( lineName : Ghjr) is a success
select
JSON_VALUE(LINE_SPECS, '$.lineName') as line_name
, JSON_VALUE(LINE_SPECS, '$.pipeDiameter') as diameter
from test
WHERE JSON_VALUE(LINE_SPECS, '$.lineName') = 'GHjr'
;
that is not possible when I try to get the second that is "Ks3R" :
select
JSON_VALUE(LINE_SPECS, '$.lineName') as line_name
, JSON_VALUE(LINE_SPECS, '$.pipeDiameter') as diameter
from test
WHERE JSON_VALUE(LINE_SPECS, '$.lineName') = 'Ks3R'
How can I do that ?
Thanks.
First your JSON data isn't valid, it might be an array.
look like this.
create table test (LINE_SPECS nvarchar(max));
insert into test values (N'
[
{
"lineName":"GHjr",
"pipeDiameter":"12",
"pipeLength":"52000",
"pressure":"15",
"volume":"107"
},
{
"lineName":"Ks3R",
"pipeDiameter":"9",
"pipeLength":"40000",
"pressure":"15",
"volume":"80"
}
]');
You can try to use OPENJSON with CROSS APPLY to parse JSON and make it.
select
t2.*
from test t1
CROSS APPLY
OPENJSON(t1.LINE_SPECS)
WITH
(
line_name varchar(MAX) N'$.lineName',
diameter varchar(MAX) N'$.pipeDiameter'
) AS t2
WHERE line_name = 'Ks3R'
sqlfiddle
Hi I Have Table that called Tags, in tag table I have 2 columns (QuestionID int ,Tag nvachar(100))
I want to Select Questions with all Tags in one column like the below
QuestionID Tag
---------- ----
1 Math
1 Integral
2 Physics
QuestionID QuestionText
---------- -----------
1 What is 2*2?
2 What is Quantom roles?
QuestionID QuestionText Tags
---------- ----------- -------
1 What is 2*2? Math, Integral
2 What is Quantom roles? Physics
Can any one help me with out using scalar value function
There are two ways to answer this:
can use a query like in other answer, but this is work for one table only.
create clr aggregate function for this like a below code (my code in C#).
this solution work for all tables and simple for use,
only use: select Concat(column) from Table in sql server
using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
using System.Text;
[Serializable]
[Microsoft.SqlServer.Server.SqlUserDefinedAggregate(Format.UserDefined, IsInvariantToDuplicates = false, IsInvariantToNulls = true, IsInvariantToOrder = false, IsNullIfEmpty = true, MaxByteSize = -1)]
public struct Concat : IBinarySerialize
{
public void Init()
{
SB = new StringBuilder();
}
public void Accumulate(SqlString Value)
{
if (Value.IsNull)
return;
if (SB.Length > 0)
SB.Append("\n");
SB.Append(Value);
}
public void Merge(Concat Group)
{
if (SB.Length > 0 && Group.SB.Length > 0)
SB.Append("\n");
SB.Append(Group.SB.ToString());
}
public SqlString Terminate()
{
return new SqlString(SB.ToString());
}
// This is a place-holder member field
StringBuilder SB;
public void Read(System.IO.BinaryReader r)
{
SB = new StringBuilder(r.ReadString());
}
public void Write(System.IO.BinaryWriter w)
{
w.Write(SB.ToString());
}
}
CREATE TABLE #temp
(
QuestionID INT,
Tag NVARCHAR(100)
)
INSERT INTO #temp
(QuestionID,Tag)
VALUES (1,N'Math'),
(1,N'Integral'),
(2,N'Physics')
CREATE TABLE #temp1
(
QuestionID INT,
QuestionText NVARCHAR(100)
)
INSERT INTO #temp1
(QuestionID,QuestionText)
VALUES (1,N'What is 2*2?'),
(2,'What is Quantom roles?')
SELECT h.QuestionID,
h.QuestionText,
Stuff((SELECT ', ' + CONVERT(VARCHAR, b.TAG)
FROM #temp b
WHERE b.QuestionID = h.QuestionID
FOR XML PATH('')), 1, 2, '')
FROM #temp t
JOIN #temp1 h
ON t.QuestionID = h.QuestionID
GROUP BY h.QuestionID,
h.QuestionText
SELECT q.QuestionText
,STUFF((
SELECT ', ' + t2.Tag
FROM Tags t2
WHERE t1.QuestionID = t2.QuestionID
ORDER BY t2.Tag
FOR XML PATH('')
,TYPE
).value('.', 'varchar(max)'), 1, 2, '') AS Tag
FROM Questions q
INNER JOIN Tags t1
ON q.QuestionID = t1.QuestionID
GROUP BY q.QuestionText
,t1.QuestionID
Working example : http://sqlfiddle.com/#!3/e8f0f/7
Try this
create function fn_comma (#question_id int)
returns varchar(100)
as
begin
declare #value varchar(100)
set #value=(SELECT top 1 STUFF((SELECT ', ' + CAST(Value AS VARCHAR(10)) [text()]
FROM Tags
WHERE ID = t.ID
FOR XML PATH(''), TYPE)
.value('.','NVARCHAR(MAX)'),1,2,' ') List_Output
FROM Tags
--where id=1
GROUP BY ID)
return #value
end
Try sub query to concat column data in comma separated values like below :
SELECT [QuestionID],
[QuestionText],
STUFF(( SELECT ',' + [Tag]
FROM [dbo].[Tags]
WHERE [QuestionID] = [Question].[QuestionID]
FOR XML PATH ('')), 1, 1, '') AS [Tags]
FROM [dbo].[Question]
SQL Fiddle Demo
Try the below idea. You just need to rewrite it as a function, then it will return all tags for the question id:
declare #function_in_questionid_para as #int
with std as
(select *,ROW_NUMBER() over(partition by QuestionID order by QuestionID,tag) as dd from #temp)
select * #temp3 into from std
declare #counter as int
set #counter = (select count(*) from #temp where QuestionID = #function_in_questionid_para as #int)
declare #c as int = 1
declare #tags as varchar(200) = ''
while (#c <= #counter)
begin
if (#c > 1) set #tags = #tags + ', '
set #tags = #tags + (select tag from #temp3 where QuestionID = #function_in_questionid_para as #int and dd = #c)
set #c = #c + 1
end
print #tags