I'm a newbie to SQL Server. I have a table Accounts which is defined as:
OrganizationId int,
AccountDetails varchar(max)
The AccountDetails column contains XML data.
The data in the table looks like this:
1 | <Account><Id>100</Id><Name>A</Name></Account>
2 | <Account><Id>200</Id><Name>B</Name></Account>
3 | <Account><Id>300</Id><Name>C</Name></Account>
4 | <Account><Id>400</Id><Name>D</Name></Account>
I need write a SQL query to get the records from this table where AccountId is 200 or 400.
The query should return two rows (#2 and #4) in JSON format, like this:
result1 : { "account_id": 200, "account_name": B }
result2 : { "account_id": 400, "account_name": D }
I'm wondering how do I go about this?
Thank you.
For # 1 above, should I be trying to cast the AccountDetails column to XML type and then use "nodes" feature for querying/filtering?
For #2, I should be writing a SQL function to convert the XML to JSON first and querying XML to build the JSON as needed?
As already mentioned, it is much better to use a proper XML data type for the AccountDetails column.
Please try the following solution.
It will work starting from SQL Server 2016 onwards.
SQL
-- DDL and sample data population, start
DECLARE #tbl TABLE (OrganizationId INT IDENTITY PRIMARY KEY, AccountDetails NVARCHAR(MAX));
INSERT #tbl (AccountDetails) VALUES
('<Account><Id>100</Id><Name>A</Name></Account>'),
('<Account><Id>200</Id><Name>B</Name></Account>'),
('<Account><Id>300</Id><Name>C</Name></Account>'),
('<Account><Id>400</Id><Name>D</Name></Account>');
-- DDL and sample data population, end
;WITH rs AS
(
SELECT t.OrganizationId
, account_id = x.value('(/Account/Id/text())[1]', 'INT')
, account_name = x.value('(/Account/Name/text())[1]', 'VARCHAR(20)')
FROM #tbl AS t
CROSS APPLY (VALUES(TRY_CAST(AccountDetails AS XML))) AS t1(x)
)
SELECT *
, JSONData = (SELECT rs.account_id, rs.account_name FOR JSON PATH,WITHOUT_ARRAY_WRAPPER)
FROM rs
WHERE rs.account_id IN (200, 400);
Output
OrganizationId
account_id
account_name
JSONData
2
200
B
{"account_id":200,"account_name":"B"}
4
400
D
{"account_id":400,"account_name":"D"}
Related
I have this function which checks friendship status between 2 users using their userIds and it is working fine.
function checkFriendShipBetweenUsers(user1Id, user2Id) {
var checkFriendShipBetweenUsersQuery = "SELECT status FROM friends WHERE (user1Id=? AND user2Id =?) OR (user1Id=? AND user2Id =?)"
var queryParameterList = [user1Id, user2Id, user2Id, user1Id]
}
I have a case in which i need to check friendship status between a user and other 3 users.
I can call above function 3 times, one for each other user to get desired result but i would like to make it with a single db call using a single query or using a mysql procedure.
function checkFriendShipBetweenUsers(user1Id, userIdList) {
var checkFriendShipBetweenUsersQuery = ""
var queryParameterList = []
}
So this query/procedure call should return 3 integers indicating user1's friendship status with users in userIdList.
Here is an example db fiddle:
db-fiddle.com/f/p5RP61V3AcawRgJcogeXey/1
given user1Id : 'a8t57h6p8n2efden' and
userIdList : ['typ3vg6xb1vt7nw2', 'cy6mqqyykpldc2j1g5vm5cqsi6x1dgrl', '0bw87kprb97pes1crom8ceodi07r2kd0']
How do i write such query or procedure?
DEMO
-- source data
CREATE TABLE test (
id INT,
user1Id VARCHAR(100),
user2Id VARCHAR(100),
status INT
);
INSERT INTO test (id,user1Id,user2Id,status) VALUES
(1,'a8t57h6p8n2efden','typ3vg6xb1vt7nw2',0),
(2,'cy6mqqyykpldc2j1g5vm5cqsi6x1dgrl','a8t57h6p8n2efden',1),
(3,'0bw87kprb97pes1crom8ceodi07r2kd0','a8t57h6p8n2efden',2),
(4,'a8t57h6p8n2efden','ap21wzbew0bprt5t',0);
SELECT * FROM test;
id
user1Id
user2Id
status
1
a8t57h6p8n2efden
typ3vg6xb1vt7nw2
0
2
cy6mqqyykpldc2j1g5vm5cqsi6x1dgrl
a8t57h6p8n2efden
1
3
0bw87kprb97pes1crom8ceodi07r2kd0
a8t57h6p8n2efden
2
4
a8t57h6p8n2efden
ap21wzbew0bprt5t
0
-- searching parameters
SET #user1Id := 'a8t57h6p8n2efden';
SET #userIdList := '[
"typ3vg6xb1vt7nw2",
"cy6mqqyykpldc2j1g5vm5cqsi6x1dgrl",
"0bw87kprb97pes1crom8ceodi07r2kd0",
"absent value"
]';
SELECT jsontable.userid, test.status
FROM JSON_TABLE( #userIdList,
'$[*]' COLUMNS ( rowid FOR ORDINALITY,
userid VARCHAR(255) PATH '$'
)) jsontable
LEFT JOIN test
ON (#user1Id, jsontable.userid) IN ( (test.user1Id, test.user2Id),
(test.user2Id, test.user1Id)
)
userid
status
typ3vg6xb1vt7nw2
0
cy6mqqyykpldc2j1g5vm5cqsi6x1dgrl
1
0bw87kprb97pes1crom8ceodi07r2kd0
2
absent value
null
fiddle
If you do not need status value for the IDs which are not found then use INNER JOIN.
If you want to receive the output as one solid value then add according GROUP BY and aggregation. Use jsontable.rowid for to provide needed values ordering.
PS. If you won't use an aggregation then you may do not obtain rowid value - simply remove rowid FOR ORDINALITY, in this case.
I have a table that has the following data
START_DATE
NAME
ID
01/18/2022
JOHN
10
01/19/2022
ADAM
20
I am trying to convert this to JSON in a custom format like below -
{
"labels":{
"name":"JOHN",
"id":[10]
}
"values":{
"startDate":"01/18/2022"
}
}
PARSE_JSON way of
SELECT parse_json('{"values": {startDate: A.STARTDATE}}')
FROM TABLE_A A;
resulted in error
Error parsing JSON: unknown keyword "A", pos 25
OBJECT_CONSTRUCT results in converting column name as key and column value as value.
Please advise how to have custom field names in JSON conversion in Snowflake.
Renamed objects names as per data given and changed Id to array:
create table test1 values(START_DATE date, NAME string,ID number);
insert into test1(START_DATE, NAME,ID ) values('01/18/2022','JOHN', 10);
insert into test1(START_DATE, NAME,ID ) values('01/19/2022','ADAM', 20);
Select
OBJECT_CONSTRUCT('ID',id::array,'NAME',name) as label_obj,
OBJECT_CONSTRUCT(
'start_date',
START_DATE::string) as start_dt_obj,
object_insert(object_construct('labels', label_obj), 'values', start_dt_obj) as final_json
from
Test1;
I have a table that has some columns.
One of these columns stores data in JSON format.
I select a row from this table with FOR JSON AUTO.
My problem is that SQL Server puts quotations around the value of JSON properties but I don't want this; because I want to use the values of inner JSON with JSON_VALUE(). What can I do?
Code:
SELECT TOP 1 *
FROM users;
Result:
name lastName age favorites
John Duo 20 {"city": "paris", "color": "blue", "sport": "football"}
Code:
SELECT TOP 1 *
FROM users
FOR JSON AUTO, WITHOUT_ARRAY_WRAPPER;
Result:
{"name":"John","lastName":"Duo","age":20,"favorites":"{\"city\": \"paris\", \"color\": \"blue\", \"sport\": \"football\"}"}
Code:
SELECT JSON_VALUE(#user_json,'$.favorites.color')
Result:
NULL
I can use this trick to get values from inner JSON, but it's not clean.
Code:
SELECT JSON_VALUE(JSON_VALUE(#user_json,'$.favorites'), '$.color')
Result:
blue
How can I do this in a clean way?
Some code for testing in SQL Server:
DROP TABLE IF EXISTS #test_tbl;
DECLARE #user_json AS NVARCHAR(MAX);
SELECT 'John' AS Name, 'Duo' AS LastName, 20 AS Age, '{"city": "paris", "color": "blue", "sport": "football"}' AS favorites
INTO #test_tbl;
SET #user_json =
(
SELECT *
FROM #test_tbl
FOR JSON AUTO, WITHOUT_ARRAY_WRAPPER
)
SELECT JSON_VALUE(#user_json,'$.favorites.color');
SELECT JSON_VALUE(JSON_VALUE(#user_json,'$.favorites'),'$.color');
You need to nest the favorites JSON using json_query(), e.g.:
SET #user_json =
(
SELECT Name, LastName, Age, json_query(favorites) as favorites
FROM #test_tbl
FOR JSON AUTO, WITHOUT_ARRAY_WRAPPER
)
SELECT JSON_VALUE(#user_json,'$.favorites.color');
# (No column name)
# ----------------
# blue
We've JSON entry in the MS SQL database. I would like to export JSON entry in the "Data" column which match with the list of EMPNO. Could someone please help?
ColumnName: Data
Data inside the column:
output:{
"Request":{
"Person":{
"DisplayName":"Test User",
"EMPNO":"000001",
"Entity":"01",
"Country":"AA"
},
"DomainOverride":null,
"ReasonForGen":"Only",
"Email":"123#test.com",
"CurrentSIP":"123#test.com"
},
"EmailAddress":"123#testcom",
"SIPAddress":"123#test.com",
"Status":"NoChange"
}
Query in layman language:
select DisplayName,EMPNO,Entity,Country,DomainOverride,ReasonForGen,Email
from Table1
where data.output.Request.EMPNO in ([EMPNO list])
You can use JSON_VALUE. Something like this:
select JSON_VALUE(data,'$.Output.Request.Person.DisplayName'), ...
from Table1
where JSON_VALUE(data,'$.Output.Request.Person.EMPNO') in ([EMPNO list])
You may try to use OPENJSON() to parse the JSON text and get objects and values from the JSON input as a table. You need to use OPENJSON() with explicit schema in the WITH clause to define the columns:
Table:
CREATE TABLE Data (
JsonData nvarchar(max)
)
INSERT INTO Data
(JsonData)
VALUES
(N'{
"output":{
"Request":{
"Person":{
"DisplayName":"Test User",
"EMPNO":"000001",
"Entity":"01",
"Country":"AA"
},
"DomainOverride":null,
"ReasonForGen":"Only",
"Email":"123#test.com",
"CurrentSIP":"123#test.com"
},
"EmailAddress":"123#testcom",
"SIPAddress":"123#test.com",
"Status":"NoChange"
}
}')
Statement:
SELECT
j.DisplayName,
j.EMPNO,
j.Entity,
j.Country,
j.DomainOverride,
j.ReasonForGen,
j.Email
FROM Data d
CROSS APPLY OPENJSON(d.JsonData) WITH (
EMPNO nvarchar(10) '$.output.Request.Person.EMPNO',
DisplayName nvarchar(50) '$.output.Request.Person.DisplayName',
EMPNO nvarchar(50) '$.output.Request.Person.EMPNO',
Entity nvarchar(50) '$.output.Request.Person.Entity',
Country nvarchar(50) '$.output.Request.Person.Country',
DomainOverride nvarchar(50) '$.output.Request.DomainOverride',
ReasonForGen nvarchar(50) '$.output.Request.ReasonForGen',
Email nvarchar(50) '$.output.Request.Email'
) j
-- Use additional WHERE clause
--WHERE j.EMPNO IN ('00001', '000002')
Result:
DisplayName EMPNO Entity Country DomainOverride ReasonForGen Email
Test User 000001 01 AA Only 123#test.com
I have data like this:
I want to query result like this:
Here is my code
SELECT
PML_CODE
,PML_NAME_ENG
,(
SELECT
PML_ID
,PML_NO
,PML_CODE
,PML_NAME_ENG
,PML_FORMULA
FROM DSP.PARAMET_LIST AS A WITH(NOLOCK)
WHERE A.PML_ID = B.PML_ID
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
) AS BR_OBJECT
FROM DSP.PARAMET_LIST AS B WITH(NOLOCK)
My code works for what I want, but I want to know if there is a better, faster way to write this query?
Next time please do not post pictures, but rather try to create some DDL, fill it with sample data and state your own attempts and the expected output. This makes it easier for us to understand and to answer your issue.
You can try it like this:
DECLARE #tbl TABLE(PML_ID BIGINT, PML_NO INT, PML_CODE VARCHAR(10), PML_NAME_ENG VARCHAR(10), PML_FORMULA VARCHAR(10));
INSERT INTO #tbl VALUES
(2017102600050,1,'KHR','Riel','01')
,(2017102600051,2,'USD','Dollar','02')
,(2017102600052,3,'THB','Bath','05')
SELECT
PML_CODE
,PML_NAME_ENG
,BR_OBJECT
FROM #tbl
CROSS APPLY(
SELECT
(
SELECT
PML_ID
,PML_NO
,PML_CODE
,PML_NAME_ENG
,PML_FORMULA
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
)) AS A(BR_OBJECT);
The big difference to your own approach is that I use a CROSS APPLY using the columns we have already instead of calling a correlated sub-query.
You can just concatenate the values. Be sure to cast the integers and to handle the NULL values. For example, if there is NULL value for column, there can be two cases - ignore the property or add the property with null, right?
For SQL Server 2016 SP1+ and later you can use FOR JSON. Basically, you should end up with something like this:
DECLARE #DataSource TABLE
(
[PML_ID] VARCHAR(64)
,[PML_NO] INT
,[PML_CODE] VARCHAR(3)
,[PML_NAME_ENG] NVARCHAR(32)
,[PML_FORMULA] VARCHAR(2)
);
INSERT INTO #DataSource ([PML_ID], [PML_NO], [PML_CODE], [PML_NAME_ENG], [PML_FORMULA])
VALUES ('201710260000000050', 1, 'KHR', 'Riel', 01)
,('201710260000000051', 2, 'USD', 'Dollar', 02)
,('201710260000000052', 3, 'THB', 'Bath', 05);
SELECT [PML_CODE]
,[PML_NAME_ENG]
,'{"PML_ID":'+ [PML_ID] +',"PML_NO":'+ CAST([PML_NO] AS VARCHAR(12)) +',"PML_CODE":'+ [PML_CODE] +',"PML_NAME_ENG":'+ [PML_NAME_ENG] +',"PML_FORMULA":'+ [PML_FORMULA] +'}' AS [BR_OBJECT]
FROM #DataSource;
-- SQL Server 2016 SP1 and latter
SELECT DS1.[PML_CODE]
,DS1.[PML_NAME_ENG]
,DS.[BR_OBJECT]
FROM #DataSource DS1
CROSS APPLY
(
SELECT *
FROM #DataSource DS2
WHERE DS1.[PML_CODE] = DS2.[PML_CODE]
AND DS2.[PML_NAME_ENG] = DS2.[PML_NAME_ENG]
FOR JSON AUTO
) DS ([BR_OBJECT]);