SQL Server - OPENJSON error - JSON text is not properly formatted - json

Azure SQL Server 2017 -
We have a table dbo.MailArchive with a field called Mail_Body which contains the body of an email. The data always looks like this from record to record, just with different numbers, and Status message:
Status: Completed
Successful actions count: 250
Page load count: 250
But copy/pasting the above to test with will make it seem like there's no issue. YOu can use this to replicate the problem:
DECLARE #YourString varchar(8000) = 'Status: Completed
Successful actions count: 250
Page load count: 250' + CHAR(13) + CHAR(10) + CHAR(13) + CHAR(10);
SELECT CONCAT('{"', REPLACE(REPLACE(#YourString, ': ', '":"'), CHAR(13) + CHAR(10), '","'), '"}')
Further, this is what the body of the email looks like if I view it in Word with hidden characters turned on:
This is the format that the data gets exported to the database in.
I'm trying to use OPENJSON to parse this data by line break, as such:
SELECT Mail_Body,
j.*
FROM dbo.MailArchive d
CROSS APPLY OPENJSON (CONCAT('{"', REPLACE(REPLACE(d.Mail_Body, ': ', '":"'), CHAR(13) + CHAR(10), '","'), '"}'))
WITH (
Status varchar(100) '$.Status',
Successful_Actions_Count int '$."Successfull actions count"',
Request_Count int '$."Request count"'
) j
I get the following error when executing this:
JSON text is not properly formatted. Unexpected character ',' is found
at position 246.
Based on some advice I've received thus far, I'm thinking it might have something to do with that line break at the end of the body. But I can't figure out the right syntax to account for it.

This is an expensive fix, as REVERSE isn't cheap, but you could use it and PATINDEX to find the first characters that aren't a line break or carriage return, remove them, and then parse that:
DECLARE #YourString varchar(8000) = 'Status: Completed
Successful actions count: 250
Page load count: 250' + CHAR(13) + CHAR(10) + CHAR(13) + CHAR(10);
DECLARE #PI varchar(7) = '%[^' + CHAR(13) + CHAR(10) + ']%';
SELECT j.Status,
j.Successful_Actions_Count, --NULL as not in sample data
j.Request_Count --NULL as not in sample data
FROM (VALUES(#YourString))V(YS)
CROSS APPLY(VALUES(REVERSE(V.YS),PATINDEX(#PI,REVERSE(V.YS)))) PI(SY,I)
CROSS APPLY(VALUES(REVERSE(STUFF(PI.SY,1,PI.I,''))))S(FixedString)
CROSS APPLY OPENJSON (CONCAT('{"', REPLACE(REPLACE(S.FixedString, ': ', '":"'), CHAR(13) + CHAR(10), '","'), '"}'))
WITH (Status varchar(100) '$.Status',
Successful_Actions_Count int '$."Successfull actions count"',
Request_Count int '$."Request count"') j;
This assumes that there could be 0 to many sets of CHAR(13) + CHAR(10) at the end of the string. If it is only ever 2 sets, simply using SUBSTRING and LEN would be easier.

Related

BCP SQL exporting to CSV

Im trying to run a Store procedure query to export to CSV file using bcp as a daily task on SQL server
using a normal query works fine for example
select #csv = 'bcp "select * from Table" queryout '+#FileAndPath2+' -c -t, -T -S' +##servername
However when I add my query which is a list of transactions data within a date range it seems to crash
#p_companyId uniqueidentifier = '189be460-99d1-42e9-b4ed-8de6f8724ce8',
#p_Path varchar(300) = 'C:\temp\','
#p_Date datetime = getutcdate
set #FileAndPath2=#p_Path + CONVERT(nvarchar(30), #p_Date, 112) + '_' + CONVERT(varchar(36), #p_companyId) + '_transactionslog.csv';
declare #csv varchar(8000)
declare #csvSQL varchar(8000)
set #csvSQL = 'SELECT TOP (100) [KICSDEV].dbo.MOVIEDETAIL.Title , [KICSDEV].dbo.MEMBERMOVIEPURCHASELOG.MemberId, [KICSDEV].dbo.MEMBERMOVIEPURCHASELOG.CreateDateTime as ''DateTime'' FROM [KICSDEV].dbo.MEMBERMOVIEPURCHASELOG INNER JOIN [KICSDEV].dbo.MOVIEDETAIL ON [KICSDEV].dbo.MEMBERMOVIEPURCHASELOG.MovieDetailId = [KICSDEV].dbo.MOVIEDETAIL.MovieDetailId INNER JOIN [KICSDEV].dbo.MEMBER ON [KICSDEV].dbo.MEMBERMOVIEPURCHASELOG.MemberId = [KICSDEV].dbo.MEMBER.MemberId INNER JOIN [KICSDEV].dbo.CINEMA ON [KICSDEV].dbo.MEMBER.CinemaId = [KICSDEV].dbo.CINEMA.CinemaId WHERE ([KICSDEV].dbo.CINEMA.CompanyId = '+ #p_companyId + ' and [KICSDEV].dbo.MEMBERMOVIEPURCHASELOG.CreateDateTime >= ' + #p_Date +' and [KICSDEV].dbo.MEMBERMOVIEPURCHASELOG.CreateDateTime < DATEADD (day , 1 , '+#p_Date+'))'
select #csvSQL
select #csv = 'bcp "'+ #csvSQL +'" queryout '+#FileAndPath2+' -c -t, -T -S' +##servername
exec master..xp_cmdshell #csv
When I run it comes up as "The data types varchar and uniqueidentifier are incompatible in the add operator." error
When i change the Company to the string instead of the variable in the query it works fine but errors on this.
set #csvSQL = 'SELECT TOP (100) [KICSDEV].dbo.MOVIEDETAIL.Title , [KICSDEV].dbo.MEMBERMOVIEPURCHASELOG.MemberId, [KICSDEV].dbo.MEMBERMOVIEPURCHASELOG.CreateDateTime as ''DateTime'' FROM [KICSDEV].dbo.MEMBERMOVIEPURCHASELOG INNER JOIN [KICSDEV].dbo.MOVIEDETAIL ON [KICSDEV].dbo.MEMBERMOVIEPURCHASELOG.MovieDetailId = [KICSDEV].dbo.MOVIEDETAIL.MovieDetailId INNER JOIN [KICSDEV].dbo.MEMBER ON [KICSDEV].dbo.MEMBERMOVIEPURCHASELOG.MemberId = [KICSDEV].dbo.MEMBER.MemberId INNER JOIN [KICSDEV].dbo.CINEMA ON [KICSDEV].dbo.MEMBER.CinemaId = [KICSDEV].dbo.CINEMA.CinemaId WHERE ([KICSDEV].dbo.CINEMA.CompanyId = ''189be460-99d1-42e9-b4ed-8de6f8724ce8'' and [KICSDEV].dbo.MEMBERMOVIEPURCHASELOG.CreateDateTime >= ' + #p_Date +' and [KICSDEV].dbo.MEMBERMOVIEPURCHASELOG.CreateDateTime < DATEADD (day , 1 , '+#p_Date+'))'
Conversion failed when converting date and/or time from character string.
I think it something to do with all the delimiters and data types.
Several problems here. Think of the problem this way. You are building a command line parameter for a command line utility, so everything has to be built into a string.
Step 1: Make sure everything is a string before concatenating the query
You are missing some casts Cast(#p_companyId as VarChar(36)) and CAST( #p_Date as VarChar(25)) you also need to quote the cast of the company id and dates in the formatted string. I recommend making a new variable to have the UTC date as a string #p_DateAsStr varchar(25) = CAST( #p_Date as VarChar(25) ), instead of repeating that over and over.
Step 2: String values in the query need to be quoted
Since you are calling BCP you have to format the query as a string, string parameters need to be quoted. You have to use the single quotes because the string to BCP is in double quotes, for instance:
set #csvSQL = 'SELECT .... WHERE CompanyId = '''+ Cast(#p_companyId as VarChar(36)) + ''....'
Step 3: Convert any strings in the query back to native types as needed by built in functions
We are OK with the GUID specified as a string (if we quote it), but for DataAdd we need to convert back from string to date Like this
CreateDateTime < DATEADD (day , 1 , CAST(''' +#p_DateAsStr+''' as DateTime))
[Update] added a quoted string for the date

Extracting Number From String SQL

I have a normal SQL statement:
SELECT VALUE_ID, UF_CRM_TASK FROM b_uts_tasks_task
Now this returns a a different field everytime but they take the form of the following:
a:1:{i:0;s:7:"CO_2012";} or a:1:{i:0;s:5:"CO_12";} or a:1:{i:0;s:7:"CO_2017";}
Basically they're different everytime. What I need is to just get the number after the CO_ part. I have tried TRIM but because everything changes in the leading and trailing section I don't think this would work.
I have looked on Stack Overflow for a while and cannot find it. I know how to do it in PHP:
$data = $row['UF_CRM_TASK'];
$companyID = substr($data, strpos($data, "CO_") + 1);
$newCompanyID = preg_replace('/[^0-9.]+/', '', $companyID);
But not SQL. Thanks in advance
In MYSQL is a bit ugly:
/*SUBSTRING_INDEX BASED ON CO_ AND THE LAST " - in 2 SUBSTRINGS*/
SELECT `VALUE_ID`, SUBSTRING_INDEX(SUBSTRING_INDEX(`UF_CRM_TASK`, 'CO_', -1), '"', 1) AS `COMPANY_ID` FROM `b_uts_tasks_task`
In PHP you can just unserialize():
$data = unserialize($row['UF_CRM_TASK']);
$companyID = str_replace('CO_', '', $data[0]);
eg:
$data = unserialize('a:1:{i:0;s:5:"CO_12";}');
echo str_replace('CO_', '', $data[0]);
//==> 12
You need to use CharIndex and SubString (Microsoft SQL) or
This is the sample code I made for my Microsoft SQL server:
declare #companyIdString varchar(50) = 'a:1:{i:0;s:7:"CO_2012";}'
print 'Company ID in a string: ' + #companyIdString
print 'Find first position: ' + Cast(charindex('"CO_', #companyIdString) as varchar(2))
print 'Locate the second position (the last "): ' + Cast(charindex('"', #companyIdString, charindex('"CO_', #companyIdString)+4) as varchar(2))
print 'Extracted Company Id: ' + substring(#companyIdString,charindex('"CO_', #companyIdString)+4, charindex('"', #companyIdString, charindex('"CO_', #companyIdString)+4) - charindex('"CO_', #companyIdString) - 4)
select
#companyIdString as CompanyIdString,
substring(#companyIdString,charindex('"CO_', #companyIdString)+4, charindex('"', #companyIdString, charindex('"CO_', #companyIdString)+4) - charindex('"CO_', #companyIdString) - 4) as CompanyId
I also made the same code on a mySQL server:
set #companyIdString := 'a:1:{i:0;s:7:"CO_2012";}';
select
#companyIdString as CompanyIdString,
substring_index(substring_index(substring_index(#companyIdString, '"', 2), '"', -1), '_', -1) as CompanyId
The substring_index starts by locating the second " (string is now a:1:{i:0;s:7:"CO_2012), then it searches backward with the -1 to locate the first " (string is now CO_2012). And then it searches backward for the underscore (string is now 2012).

MySQL Multiple lines string puts 0

I'm trying to insert a multiple line string into my MySQL db.
Example:
INSERT INTO `dressuurpaardje`.`Marks` (`markID`, `testID`, `markPosition`, `techinicalMark`, `directiveIdeas`, `maxMark`, `coefficient`, `overflow`)
VALUES (NULL, 1, 'M', 'Proceed in passage ' + CHAR(10) + CHAR(13) + ' Transition collected walk - passage', NULL, 10, NULL, NULL);
I use ' + CHAR(10) + CHAR(13) + ' with the intention to get a newline in the string. But this puts the string "0" in my db
Any suggestions?
Use '\n' in your query, and let the text interpreter add the line (depends what you use, but that's what I would recommend)
Use CONCAT(string1, CHAR(10), CHAR(13), string2), as mentionned also by Mazatwork.
Be aware that CHAR10 + CHAR13 is wrong. A line feed / carriage return is either:
chr10 or \n (unix)
chr13 + chr10 or \r\n (windows)

< converted to < in sql server

i have some data in my table
when i select that data with this query
select TblActionHistories.Comments from TblActionHistories WHERE TblActionHistories.Entity=CAST('Task' AS VARCHAR) AND EntityId=CAST(32 AS VARCHAR)
it's coming fine like this
Task- <b>T1</b> has been added by Swapnil Sharma
Task- <b>T1</b> status changed to <b>In Progress</b> by Swapnil Sharma<br/>
but now i want my above result # separated using stuff so i am using this
SELECT STUFF((SELECT '#' + ISNULL(CAST(TblActionHistories.Comments AS VARCHAR(MAX)),'') FROM TblActionHistories WHERE TblActionHistories.Entity=CAST('Task' AS VARCHAR) AND EntityId=CAST(32 AS VARCHAR) for xml path ('')),1,1,'')
it gives me this
Task- <b>T1</b> has been added by Swapnil Sharma#Task- <b>T1</b> status changed to <b>In Progress</b> by Swapnil Sharma<br/>
you can clearly see that all the special char. like < > converted to < > respectively
please help me out with this i want them to come in their original format
well i found the solution as suggested by IvanG
SELECT STUFF((SELECT '#' + ISNULL(CAST(TblActionHistories.Comments AS VARCHAR(MAX)),'') FROM TblActionHistories WHERE TblActionHistories.Entity=CAST('Task' AS VARCHAR) AND EntityId=CAST(32 AS VARCHAR) for xml path(''), root('MyString'), type ).value('/MyString[1]','varchar(max)') ,1,1,'')
ref. to this article
http://blogs.lobsterpot.com.au/2010/04/15/handling-special-characters-with-for-xml-path/
Try casting to NVARCHAR instead of VARCHAR
SELECT STUFF((SELECT '#' + ISNULL(CAST(TblActionHistories.Comments AS NVARCHAR(MAX)),'') FROM TblActionHistories WHERE TblActionHistories.Entity=CAST('Task' AS NVARCHAR) AND EntityId=CAST(32 AS VARCHAR) for xml path ('')),1,1,'')

Indexing issue in sql server

hey guys,
i have a query in sql server which takes atleast 10-15 seconds to execute, and when this is called in asp.net, it is more worst there, it just throws request timeout error.
Below is the query i am using.
SELECT C.Id,
C.Summary,
C.Title,
C.Author,
CONVERT(VARCHAR(12), C.PublishDate, 104)
AS 'DATE',
'/Article/' + SUBSTRING(dbo.RemoveSpecialChars(C.Title), 0, 10) + '/' + CAST(CA.CategoryId AS VARCHAR(MAX)) + '/' + CAST(C.Id AS VARCHAR(MAX)) +
'.aspx' AS
'URL'
FROM CrossArticle_Article C
INNER JOIN CrossArticle_ArticleToCategory CA
ON C.Id = CA.ArticleId
WHERE C.Title LIKE '%' + #KEYWORD + '%'
OR C.Summary LIKE '%' + #KEYWORD + '%'
OR C.Article LIKE '%' + #KEYWORD + '%'
SELECT ##ROWCOUNT
Below are the Fields Specification.
Id int Primary Key
Summary nvarchar(1000)
Title nvarchar(200)
Author nvarchar(200)
PublishDate DateTime
CategoryId int PrimaryKey
i think this can be resolved by using Indexing on these columns using include.. i checked over net, but didnt find any solution..
i would appreciate if i could get help for the same.
Thanks and Regards
Abbas Electricwala
Ordinary column indexing most likely cannot help your query, unfortunately. LIKE conditions can only be assisted by indexes when they are in the form of value% (meaning that you can only have a wildcard on the end of the expression; the prefix must be static).
I am assuming that you already have an index on CrossArticle_Article.Id and CrossArticle_ArticleToCategory.ArticleId. If not, you should add those.