I've been playing around with Substring, left, right, charindex and can't quite get this to work
If this is the value in column name 'Data' (this is all one line)
{"email":{"RecipientId":"usertest","RecipientEmail":"test#test.com","Subject":"This is a test subject heading","RecipientSubject":"A recipient subject"}}
How do I do a SELECT statement to find the 'Subject' heading and then get the data 'This is a test subject'? The Subject value is different for every record so I just can't look for 'This is a test subject'.
So the end result should be This is a test subject for that SELECT result
The following query should do what you want:
declare #string varchar(max);
set #string = '{"email":{"RecipientId":"usertest","RecipientEmail":"test#test.com","Subject":"This is a test subject heading","RecipientSubject":"A recipient subject"}}';
select substring(#string,charindex('"Subject":',#string)+11,charindex('"RecipientSubject"',#string)-charindex('"Subject"',#string)-13);
The plain and easy-cheesy approach is this:
SELECT SUBSTRING(
t.YourString
,A.StartPosition
,CHARINDEX('"'
,t.YourString
,A.StartPosition+1) - A.StartPosition
)
FROM #dummyTable t
CROSS APPLY(SELECT CHARINDEX('"Subject":"',t.YourString)+11) A(StartPosition)
I use APPLY to calculate a value and use it like you'd use a variable. The idea is: Find the starting point and look for the closing quote from there. But this will break, whenever the content includes an (escaped) quote like in
"Subject":"This is \"quoted\" internally"
A more generic approach
Starting with v2016 JSON-support was introduced. With this (or a higher) version this is really simple:
Use this mockup-table for testing
DECLARE #dummyTable TABLE (YourString VARCHAR(1000));
INSERT INTO #dummyTable VALUES('{"email":{"RecipientId":"usertest","RecipientEmail":"test#test.com","Subject":"This is a test subject heading","RecipientSubject":"A recipient subject"}}');
--The OPENJSON-method will read this for you:
SELECT JsonContent.*
FROM #dummyTable t
CROSS APPLY OPENJSON(t.YourString,'$.email')
WITH(RecipientId VARCHAR(100)
,RecipientEmail VARCHAR(100)
,[Subject] VARCHAR(100)
,RecipientSubject VARCHAR(100)) JsonContent;
But with a lower version you will need to trick this out. It is the easiest, to tranform JSON to attribute centered XML like here:
<email RecipientId="usertest" RecipientEmail="test#test.com" Subject="This is a test subject heading" RecipientSubject="A recipient subject" />
We can achieve this by some string methods and I must warn you, that there are several pit-falls with forbidden characters and other stuff... Just try it out:
SELECT Casted.ToXml.value('(/email/#RecipientId)[1]','varchar(1000)') AS RecipientId
,Casted.ToXml.value('(/email/#RecipientEmail)[1]','varchar(1000)') AS RecipientEmail
,Casted.ToXml.value('(/email/#Subject)[1]','varchar(1000)') AS [Subject]
,Casted.ToXml.value('(/email/#RecipientSubject)[1]','varchar(1000)') AS RecipientSubject
,Casted.ToXml.query('.') LookHowThisWasTransformed
FROM #dummyTable t
CROSS APPLY
(
SELECT CAST(CONCAT('<email '
,REPLACE(REPLACE(REPLACE(REPLACE(t.YourString,'{"email":{"',''),'}}',''),'","','" '),'":"',' ="')
,' />') AS XML)
) Casted(ToXml);
Related
I am new to JSON in SQL. I am getting the error "JSON text is not properly formatted. Unexpected character 'N' is found at position 0." while executing the below -
DECLARE #json1 NVARCHAR(4000)
set #json1 = N'{"name":[{"FirstName":"John","LastName":"Doe"}], "age":31, "city":"New York"}'
DECLARE #v NVARCHAR(4000)
set #v = CONCAT('N''',(SELECT value FROM OPENJSON(#json1, '$.name')),'''')
--select #v as 'v'
SELECT JSON_VALUE(#v,'$.FirstName')
the " select #v as 'v' " gives me
N'{"FirstName":"John","LastName":"Doe"}'
But, using it in the last select statement gives me error.
DECLARE #v1 NVARCHAR(4000)
set #v1 = N'{"FirstName":"John","LastName":"Doe"}'
SELECT JSON_VALUE(#v1,'$.FirstName') as 'FirstName'
also works fine.
If you're using SQL Server 2016 or later there is build-in function ISJSON which validates that the string in the column is valid json.
Therefore you can do things like this:
SELECT
Name,
JSON_VALUE(jsonCol, '$.info.address.PostCode') AS PostCode
FROM People
WHERE ISJSON(jsonCol) > 0
You are adding the Ncharacter in your CONCAT statement.
Try changing the line:
set #v = CONCAT('N''',(SELECT value FROM OPENJSON(#json1, '$.name')),'''')
to:
set #v = CONCAT('''',(SELECT value FROM OPENJSON(#json1, '$.name')),'''')
JSON_VALUE function may first be executed on all rows before applying the where clauses. it will depend on execution plan so small things like having top clause or ordering may have a impact on that.
It means that if your json data is invalid anywhere in that column(in the whole table), it will throw an error when the query is executed.
So find and fix those invalid json formats first. for example if that column has a ' instead of " it cannot be parsed and will cause the whole TSQL query to throw an error
I'm getting json file, which I load to Azure SQL databese. This json is direct output from API, so there is nothing I can do with it before loading to DB.
In that file, all Polish diactircs are escaped to "C/C++/Java source code" (based on: http://www.fileformat.info/info/unicode/char/0142/index.htm
So for example:
ł is \u0142
I was trying to find some method to convert (unescape) those to proper Polish letters.
In worse case scenario, I can write function which will replace all combinations
Repalce(Replace(Replace(string,'\u0142',N'ł'),'\u0144',N'ń')))
And so on, making one big, terrible function...
I was looking for some ready functions like there is for URLdecode, which was answered here on stack in many topics, and here: https://www.codeproject.com/Articles/1005508/URL-Decode-in-T-SQL
Using this solution would be possible but I cannot figure out cast/convert with proper collation and types in there, to get result I'm looking for.
So if anyone knows/has function that would make conversion in string for unescaping that \u this would be great, but I will manage to write something on my own if I would get right conversion. For example I tried:
select convert(nvarchar(1), convert(varbinary, 0x0142, 1))
I made assumption that changing \u to 0x will be the answer but it gives some Chinese characters. So this is wrong direction...
Edit:
After googling more I found exactly same question here on stack from #Pasetchnik: Json escape unicode in SQL Server
And it looks this would be the best solution that there is in MS SQL.
Onlty thing I needed to change was using NVARCHAR instead of VARCHAR that is in linked solution:
CREATE FUNCTION dbo.Json_Unicode_Decode(#escapedString nVARCHAR(MAX))
RETURNS nVARCHAR(MAX)
AS
BEGIN
DECLARE #pos INT = 0,
#char nvarCHAR,
#escapeLen TINYINT = 2,
#hexDigits TINYINT = 4
SET #pos = CHARINDEX('\u', #escapedString, #pos)
WHILE #pos > 0
BEGIN
SET #char = NCHAR(CONVERT(varbinary(8), '0x' + SUBSTRING(#escapedString, #pos + #escapeLen, #hexDigits), 1))
SET #escapedString = STUFF(#escapedString, #pos, #escapeLen + #hexDigits, #char)
SET #pos = CHARINDEX('\u', #escapedString, #pos)
END
RETURN #escapedString
END
Instead of nested REPLACE you could use:
DECLARE #string NVARCHAR(MAX)= N'\u0142 \u0144\u0142';
SELECT #string = REPLACE(#string,u, ch)
FROM (VALUES ('\u0142',N'ł'),('\u0144', N'ń')) s(u, ch);
SELECT #string;
DBFiddle Demo
Could someone please help me with this one?
So I need to write a user input function in which I need to concatenate two strings. When outputted, there must be a space between the two strings, note there is not a space in the two strings when inputting them. Test functions with the following, String 1: Spring, String 2: Break!
This is my solution:
create function concatenate(X CHAR,Y CHAR)
Returns CHAR(50)
Return concat(X, ' ', Y);
select concatenate('Spring','Break')
However, the problem is that sql only returns the first letter of each word, which is "S B". But I want it to be "Spring Break"
Any ideas on this one? Helps are very appreciated
Supply a length for the input parameters as well:
create function concatenate(X CHAR(24),Y CHAR(24))
Returns CHAR(50)
Return concat(X, ' ', Y);
select concatenate('Spring','Break')
You need to define the size when you declare the argument.
create function con(X char(50), Y char(50))
returns char(100)
You have to specify the size of CHAR(), otherwise it will use the default CHAR(1), and you can't get want you want.
eg:
create function hello(x char(10),y char(10))
returns char(30) deterministic
return concat(x,' ',y)`
select hello('Hello','World');
Hello World
Hi i am getting the string literal error when i am trying to add an attribute to the child node. How can i modify my code in order to add an attribute successfully.
declare #count int=(select mxGraphXML.value('count(/mxGraphModel/root/Cell/#Value )','nvarchar') from TABLE_LIST
where Table_ListID=1234 )
declare #index int=1;
while #index<=#count
begin
declare #Value varchar(100)= #graphxml.value('(/mxGraphModel/root/Cell/#Value )[1]','nvarchar');
SET #graphxml.modify('insert attribute copyValueID {sql:variable("#Value ")}
as first into (/mxGraphModel/root/Cell)['+convert(varchar,#index)+']');
end
set #index=#index+1;
end
You're using the addition operator where you should be using the CONCAT function. So
'insert attribute copyValueID {sql:variable("#Value ")}
as first into (/mxGraphModel/root/Cell)['+convert(varchar,#index)+']'
is being coerced into a number. Try:
CONCAT('insert attribute copyValueID {sql:variable("#Value ")}
as first into (/mxGraphModel/root/Cell)[',convert(varchar,#index),']')
instead.
Adam, you can do it in Microsoft T-SQL like this:
declare #sql nvarchar(max)
set #sql = 'set #myxml.modify(''
insert (
attribute scalableFieldId {sql:variable("#sf_id")},
attribute myTypeId {sql:variable("#my_type_id")}
) into (/VB/Condition/Field[#fieldId=sql:variable("#field_id")
and #fieldCode=sql:variable("#field_code")])['+
cast(#instance as varchar(3))+']'')'
exec sp_executesql
#sql
,N'#myxml xml output, #field_code varchar(20),
#field_id varchar(20), #sf_id int, #my_type_id tinyint'
,#myxml = #myxml output
,#field_code = #field_code
,#field_id = #field_id
,#sf_id = #sf_id
,#my_type_id = #my_type_id
See what I've done here? It's just a clever usage of Dynamic SQL to overcome Microsoft's moronic limitation of "string literal error".
IMPORTANT NOTE: yes, you can MOSTLY do this by using sql:variable() in SOME places BUT good luck trying to use it in the node number qualifier inside the square brackets! You can't do this without Dynamic SQL by design!
The trick is not mine actually, I got the idea from https://www.opinionatedgeek.com/Snaplets/Blog/Form/Item/000299/Read after banging my head against the wall for a while.
Feel free to ask questions if my sample does not work or something is not clear.
My applications pulls rows from a table which contain a column = StringA. The user enters 2 parameters, From and Thru strings, and if she enters the same string in both, and the string has an embedded dash, it's not working.
Why doesn't the following return True where ColumnA and StringA = 'medi-care', but it does return true where ColumnA and StringA = 'medicare' (no dash)?
IF ColumnA between StringA and StringA...
I also tried:
IF ColumnA <= StringA and ColumnA >= StringA...
Is this a bug? I tried appending a 'z' to the Thru parameter string - still didn't work for the string with a dash embedded. Can you suggest a way to make this work?
Is it possible that you actually have an en dash or em dash in one of them but not the other. Often times it is very difficult to tell. Ex:
Declare #StringA varchar(20)
Declare #ColumnA VarChar(20)
Select #StringA = 'medi-care',
#ColumnA = 'medi—care'
Select 'They Match'
Where #StringA = #ColumnA
Note that in this example, #ColumnA actually has an en dash instead of a dash. If you look closely, you may be able to tell, but it is very difficult to notice a difference unless you are specifically looking for it.
It works for me. I ran this on SQL 2008R2:
DECLARE #Test TABLE (
ColA varchar(7)
);
INSERT INTO #Test (ColA) VALUES ('a-z');
SELECT * FROM #Test WHERE ColA BETWEEN 'a-z' AND 'a-z';
And I got the result of 'a-z'. There must be something else in your data that is causing the non-match. Blank spaces or other invisible characters.