Extracting value from JSON string provokes error message T-SQL - sql

Situation:
I am trying to join a table with ids to another table with ids and a column that is stored in base64 value. I decoded it and used JSON functions to extract a value from key 'a' of the JSON string. I got the famous error message:
JSON text is not properly formatted. Unexpected character '(' is found at position 1.
After browsing similar mistakes online, i added the ISJSON() > 0 as it should exclude the error message - but it didn't.
SELECT
u.ids,
base2.ExtractedValue
FROM #ids AS u
INNER JOIN (
SELECT base.Ids, JSON_VALUE(base.Base64Decoded, '$.a') as ExtractedValue
FROM (
SELECT
ids,
convert(
varchar(MAX),
CAST('' AS xml).value('xs:base64Binary(sql:column("value"))', 'VARBINARY(MAX)')
) AS Base64Decoded
FROM base64info
) AS base
WHERE ISJSON(base.Base64Decoded) > 0
AND base.Base64Decoded is not null
) AS base2 ON u.ids=base2.ids
WHERE base2.ExtractedValue IS NOT NULL
What am it doing wrong?

A case statement solves the issue as such:
CASE WHEN ISJSON(base.Base64Decoded) = 1 THEN JSON_VALUE(base.Base64Decoded, '$.a') END as ExtractedValue

Related

JSON Array in SQL - Extracting multiple values from JSON array

I have a JSON column [gca] that looks like this
[{
"i":"https://some.image.URL 1",
"u":"https://some.product.url",
"n":"Product 1 Name",
"q":"1",
"sk":"sku number 1",
"st":"$499.99"
},
{
"i":"https://some.image.URL 2",
"u":"https://some.product.url",
"n":"Product 2 Name",
"q":"1",
"sk":"sku number 2",
"st":"$499.99"
}]
I want to extract values specific to position. For example:
JSON_VALUE ([gca], '$[0].i') + ', ' + JSON_VALUE ([gca], '$[1].i')
So the result would be a string
image url 1, image url 2
I tried the cross apply solution from this answer, but I get this error:
JSON text is not properly formatted. Unexpected character 'h' is found at position 0
Expected results
-- https://i.stack.imgur.com/UaRjQ.png
Your statement is correct and based on the JSON data in the question, the reason for this "... JSON text is not properly formatted. Unexpected character 'h' is found at position 0' error ... " error is somewhere else.
Your JSON text is a valid JSON array, so you have two possible approaches to get your expected results:
if this JSON array has always two items, you should use JSON_VALUE() to access each item
if the count of the items is not known, you should use OPENJSON() with additional CROSS APPLY operator and string aggregation function.
Table:
CREATE TABLE #Data (
[gca] nvarchar(max)
)
INSERT INTO #Data
([gca])
VALUES
(N'[{"i":"https://some.image.URL 1","u":"https://some.product.url","n":"Product 1 Name","q":"1","sk":"sku number 1","st":"$499.99"},{"i":"https://some.image.URL 2","u":"https://some.product.url","n":"Product 2 Name","q":"1","sk":"sku number 2","st":"$499.99"}]'),
(N'[{"i":"https://some.image.URL 1","u":"https://some.product.url","n":"Product 1 Name","q":"1","sk":"sku number 1","st":"$499.99"},{"i":"https://some.image.URL 2","u":"https://some.product.url","n":"Product 2 Name","q":"1","sk":"sku number 2","st":"$499.99"}]')
Statements:
-- For fixed structure with two items
SELECT JSON_VALUE ([gca], '$[0].i') + ', ' + JSON_VALUE ([gca], '$[1].i') AS [http]
FROM #Data
-- For JSON array with multiple items and SQL Server 2017+
SELECT j.*
FROM #Data d
CROSS APPLY (
SELECT STRING_AGG([http], ',') AS [http]
FROM OPENJSON(d.[gca]) WITH ([http] varchar(max) '$.i')
) j
-- For JSON array with multiple items
SELECT STUFF(j.[http], 1, 1, N'') AS [http]
FROM #Data d
CROSS APPLY (
SELECT CONCAT(',', [http])
FROM OPENJSON(d.[gca]) WITH ([http] varchar(max) '$.i')
FOR XML PATH('')
) j([http])
Output:
-------------------------------------------------
http
-------------------------------------------------
https://some.image.URL 1,https://some.image.URL 2
https://some.image.URL 1,https://some.image.URL 2
Note, that JSON support was intoduced in SQL Server 2016 and STRING_AGG() was introduced in SQL Server 2017. For string aggregation in earlier versions use FOR XML PATH.

how to convert the output of sub query into numeric

select rptName
from RptTable
where rpt_id in (
select LEFT(Reports, NULLIF(LEN(Reports)-1,-1))
from repoAccess1
where uid = 'VIKRAM'
)
this is my sql query In which i have use the sub query to access selected field
in this sub query returns
select LEFT(Reports, NULLIF(LEN(Reports)-1,-1))
from repoAccess1
where uid = 'VIKRAM'
Returns
1,2
that means the query should be like
select rptName
from RptTable where rpt_id in (1,2)
But i m getting this error
Msg 8114, Level 16, State 5, Line 1
Error converting data type nvarchar to numeric.
could anyone tell me ow to modify to get exact ans
It's a little hard to tell without the concrete table definitions, but I'm pretty sure you're trying to compare different data types to each other. If this is the case you can make use of the CAST or the CONVERT function, for example:
SELECT
[rptName]
FROM [RptTable]
WHERE [rpt_id] IN
(
SELECT
CONVERT(int, LEFT([Reports], NULLIF(LEN([Reports]) - 1, -1)))
FROM [repoAccess1]
WHERE [uid] = 'VIKRAM'
)
UPDATE: Since you have updated your question: The LEFT function returns results of either varchar or nvarchar data type. So the resulting query would be
SELECT
[rptName]
FROM [RptTable]
WHERE [rpt_id] IN('1', '2')
Please note the apostrophes (is this the correct term?) around the values. Since [rpt_id] seems to be of data type int the values cannot implicitly be converted. And that's where the aforementioned CAST or CONVERT come into play.
If I understand correctly, the subquery is returning a single row with a value of '1,2'. This is not a number, hence the error.
Before continuing, let me emphasize that storing values in comma delimited string is not the SQL-way of doing things. You should have one row per id, with proper types and foreign keys defined.
That said, sometimes we are stuck with other people's really bad design decisions. If this is the case, you can use LIKE:
select rptName
from RptTable r
where exists (select 1
from repoAccess1 a
where a.uid = 'VIKRAM' and
',' + a.reports + ',' like '%,' + cast(r.rpt_id as varchar(255)) + ',%'
);
select rptName
from RptTable
where rpt_id in (
select CAST(LEFT(Reports, NULLIF(LEN(Reports)-1,-1)) AS INT) as Val
from repoAccess1
where uid = 'VIKRAM'
)
Your query would work fine when (LEFT(Reports, NULLIF(LEN(Reports)-1,-1)) ) returns either 1 or 2 since SQL Server implicitly converts the varchar value to numeric.
It seems there might be a data issue. One of the data returned by LEFT function is non-numeric. In order to find that particular record you can use isnumeric function. Try like this,
SELECT rptName
FROM RptTable
WHERE rpt_id IN (
SELECT LEFT(Reports, NULLIF(LEN(Reports) - 1, - 1))
FROM repoAccess1
WHERE uid = 'VIKRAM'
AND ISNUMERIC(LEFT(Reports, NULLIF(LEN(Reports) - 1, - 1))) = 1
)

Convert exponential to number in sql

I have a large amount of card tokens (16 digits) uploaded from xml file to sql-server. The problem is I see them as expression, sample below:
3.3733E+15
3.3737E+15
3.3737E+15
3.3737E+15
3.37391E+15
3.37391E+15
3.37398E+15
3.37453E+15
3.37468E+15
3.37468E+15
3.3747E+15
3.37486E+15
3.37486E+15
3.37567E+15
3.3759E+15
3.3759E+15
Any suggestion to change them to a 16 digit number? I have tried to change the data type, but got error"Conversion failed when converting the varchar value '3.37201E+15' to data type int"
Thanks for help!
Edit:
#X.L.Ant see my code below. I create this table from another one, which is just purely inserted from xml file. Is this may cause an error because some rows are empty in column TOKEN?
CREATE TABLE MULTICURRENCY_CHECK
(
TOKEN varchar(255)
)
/*Merges all card tokens into 1 column, as in xml they are spread across different columns*/
INSERT INTO MULTICURRENCY_CHECK
(
TOKEN
)
SELECT no FROM gpstransactionsnew2
UNION ALL
SELECT no19 FROM gpstransactionsnew2
UNION ALL
SELECT no68 FROM gpstransactionsnew2
UNION ALL
SELECT no93 FROM gpstransactionsnew2
UNION ALL
SELECT no107 FROM gpstransactionsnew2
UNION ALL
SELECT no121 FROM gpstransactionsnew2
SELECT REPLACE(TOKEN, 'OW1', ' ')
FROM MULTICURRENCY_CHECK
/*Converts exponential expression to number*/
SELECT CONVERT(numeric(16,0), CAST(TOKEN AS FLOAT))
FROM MULTICURRENCY_CHECK
Try to cast your string to float before converting it :
SELECT CONVERT(numeric(16,0), CAST(TOKEN AS FLOAT))
FROM MULTICURRENCY_CHECK
See this fiddle.
I don't know what's the format of those numbers in your XML source, but with the data you provide, you'll end up with 33733 for instance followed by a bunch of zeroes. If you have a bigger precision in your XML, maybe you should tweak your importing settings to keep this precision instead of trying to deal with that in the DB.
EDIT:
Try testing your strings with ISNUMERIC to avoid the casting errors you're getting. Adding a raw output of your column will allow you to check which value fails to convert (i.e. converts to 0).
SELECT TOKEN,
CONVERT(NUMERIC(16, 0), CAST(CASE
WHEN ISNUMERIC(TOKEN) = 1
THEN TOKEN
ELSE 0
END AS FLOAT))
FROM MULTICURRENCY_CHECK
For SQL Server 2012+, use TRY_CONVERT().
The use of ISNUMERIC() in xlecoustillier's edited answer does not protect against conversion failures.
Given the following scenario:
CREATE TABLE test(a varchar(100));
insert into test values ('3.3733E+15'),
('3.3737E+15'),
('3.37391E+30'), --fails conversion. included to demonstrate the nature of TRY_CONVERT().
('3.37398E+15'),
('3.37453E+15'),
('3.37468E+15'),
('3.3747E+15'),
('3.37486E+15'),
('3.37567E+15'),
('3.3759E+15');
SELECT TRY_CONVERT(numeric(16,0), CAST(a AS FLOAT))
FROM test
Results in only valid converted values:
---------------------------------------
3373300000000000
NULL
3373910000000000
3373980000000000
3374530000000000
3374680000000000
3374700000000000
3374860000000000
3375670000000000
3375900000000000
However:
SELECT a,
CONVERT(NUMERIC(16, 0), CAST(CASE
WHEN ISNUMERIC(a) = 1
THEN a
ELSE 0
END AS FLOAT))
FROM test
Fails with:
Conversion failed when converting the varchar value '3.3733E+15' to
data type int.
The issue is that all values in the 'a' column return 1 when passed to the ISNUMERIC() function.
SELECT CASE WHEN ISNUMERIC(a) = 1 THEN 'Yes' ELSE 'No' END as IsValueNumeric
FROM test
Try it on SQLFiddle and/or compare with xlecoustillier's sqlfiddle
SELECT colmn_name || '' FROM table_name
This should work.

Conversion failed when converting the varchar value 'ADMIN' to data type int

I have sql like below. Home hints:
Results in K table (from WITH) gives number codes of specified range (ex.: 1-90).
The main select part works fine until I use
sum(F._IsInIntersection) [CountIntersection]
It causes error
Msg 245, Level 16, State 1, Line 11
Conversion failed when converting the varchar value 'ADMIN' to data type int.
If I comment this line, it is OK.
I do not know, why this error occurs the line with SUM() contains only int values. No ADMIN value (as mentioned in the error) exists anywhere...
Can anyone see any problems with the query?
;with K as (
select cast(c.Code as int) code
from rmCategory c, rmCategoryGroup cg, rmIdentification ic
where 1=1
and c.CategoryGroupID=cg.ID
and c.ID=ic.ID0
)
select
F.STAV_AKT [Code]
, count(F.STAV_AKT) [Count]
, sum(F._IsInIntersection) [CountIntersection]
from
(
select
f.*
, case when f.KVALIF IN (select code from K) and f.KVALIF is not null then 1 else 0 end _IsInIntersection
from frmFormData_208629_MaMo2_IMPORT f, rmIdentification i
where 1=1
and f.ID=i.id0
and i.isValid=1
and f.sysDAVKA=5
) F
group by F.STAV_AKT
order by F.STAV_AKT
I also tried CASTing parameter inside of sum(cast(F._IsInIntersection as int)) [CountIntersection] to be sure to use int values instead of the boolean ones, but no change.
Martin, I'm suspecting that c.Code also contains the value 'ADMIN' in the part
cast(c.Code as int) code
It runs good without the SUM because it's part of a CTE and is not materialized when the column is not used.
If for example I run the following code example in Fiddle it runs fine, but if I also select the column Code it fails when trying to CAST 'ADMIN' to INT (Fiddle).
;WITH cte as
(
select CAST(a AS INT) AS Code
,'Dummy' AS Column2
FROM (
SELECT 1 AS a
UNION ALL SELECT 'a'
) t
)
SELECT Column2
--,Code
FROM cte
So try to figure out what exactly the values for column Code are by using e.g.
SELECT DISTINCT Code FROM rmCategory
What is the datatype of f.sysDAVKA?
Try running
SELECT f.sysDAVKA
FROM frmFormData_208629_MaMo2_IMPORT f
WHERE f.sysDAVKA = 'ADMIN'
My gut feeling is you will find at least one row. If this is true, then changing your WHERE clause to
where 1=1
and f.ID=i.id0
and i.isValid=1
and f.sysDAVKA=CAST(5 AS char(1))
should resolve the problem.

select case statement error in mssql

SELECT top 1
case
when VR = -99999.99
then 0
else cast((VR*1.732) as decimal(38,3))
end
FROM pseb.dbo.datasource
where FeederID=5003
order by datetime desc
The above query is working fine, but I need to return varchar value '--' instead of returning 0
if I do like that
SELECT top 1
case
when VR = -99999.99
then '--'
else cast((VR*1.732) as decimal(38,3))
end
FROM pseb.dbo.datasource
where FeederID=5003
order by datetime desc
means it returns the following error:
Msg 8114, Level 16, State 5, Line 1 Error converting data type varchar
to numeric.
please help me to solve it
The problem is that you are returning two different data types from the same column. The rule with SQL Server that numeric types take precedence over string types, i.e. in a situation like yours a string gets converted to a number, not the other way around.
So to solve this you can cast your number to a string.
One option is to do something like this:
SELECT top 1
case when VR = -99999.99 then '--'
else
cast
(
cast((VR*1.732) as decimal(38,3)
)
as varchar(50))
end
FROM pseb.dbo.datasource where FeederID=5003 order by datetime desc