I posted a question about this yesterday, which was very helpfully answered by solutionist, but I now have a follow up question along the same lines.
I want to get two sets of data out from the JSON.
I have three data sets, one is the header, one is a set of customer records and the last is an error object.
The structure I am looking to try and get is:
Header
Customers
Error
but what I'm getting is:
Header
Customer, Error
Customer, Error
Now in this example, the error is null so I'm happy either with the error being omitted because it's null, or coming back as an empty object, but I want a single one, not one per customer.
This is what I currently get:
select isjson('{"APIResult":[{"ID":200,
"Status_Message":"Success",
"Developer_Message":"Customers found",
"User_Message":"Customers found",
"Return_Code":0,
"Customer":[{"ID":"A6B10FA0-85AD-422D-8A7E-07EEA541593E",
"First_Name":"Bbb",
"Paternal_Last_Name":"Bbbbb",
"Email_Address":"bbbb#example.com",
"Error":[{}]
},
{"ID":"91EE2FD6-2C40-4CFD-ABB7-2CDAE3624487",
"First_Name":"Aaaa",
"Paternal_Last_Name":"Aaaaa",
"Email_Address":"aaaaa#example.com",
"Error":[{}]
}]
}]
}')
And this is what I am trying to get:
select isjson('{"APIResult":[{"ID":200,
"Status_Message":"Success",
"Developer_Message":"Customers found",
"User_Message":"Customers found",
"Return_Code":0,
"Customer":[{"ID":"A6B10FA0-85AD-422D-8A7E-07EEA541593E",
"First_Name":"Bbb",
"Paternal_Last_Name":"Bbbbb",
"Email_Address":"bbbb#example.com"
},
{"ID":"91EE2FD6-2C40-4CFD-ABB7-2CDAE3624487",
"First_Name":"Aaaa",
"Paternal_Last_Name":"Aaaaa",
"Email_Address":"aaaaa#example.com"
}],
"Error":[{}]
}]
}')
Both are valid JSON (I have tested this this time, as you can see), but I can't work out how to get the second format.
This is the query I am running to get the output:
declare #returncode int = 0
,#VerboseMsg nvarchar(4000) = 'Customers found'
,#DisplayMsg nvarchar(4000) = 'Customers found'
CREATE TABLE #Customers (CustomerID UNIQUEIDENTIFIER DEFAULT(NEWID()), FirstName NVARCHAR(50), PaternalLastName NVARCHAR(50), EmailAddress NVARCHAR(250))
INSERT INTO #Customers (FirstName, PaternalLastName, EmailAddress)
VALUES ('Aaaa', 'Aaaa', 'aaaa#example.com'), ('Bbbb', 'Bbbb', 'bbbb#example.com')
SELECT st.APIStatus AS 'ID'
,st.StatusMessage AS 'Status_Message'
,#VerboseMsg AS 'Developer_Message'
,#DisplayMsg AS 'User_Message'
,st.ReturnCode AS 'Return_Code'
,[Customer].CustomerID AS 'ID'
,[Customer].FirstName AS 'First_Name'
,[Customer].PaternalLastName AS 'Paternal_Last_Name'
,[Customer].EmailAddress AS 'Email_Address'
,[Error].ErrorCode AS 'Error_Code'
,[Error].ErrorMsg AS 'Error_Message'
FROM (SELECT 200 AS APIStatus, 'Success' AS StatusMessage, #ReturnCode AS ReturnCode) st
LEFT JOIN (SELECT CustomerID
,FirstName
,PaternalLastName
,EmailAddress
,#ReturnCode AS ReturnCode
FROM #Customers
) As [Customer]
ON st.ReturnCode = [Customer].ReturnCode
LEFT JOIN (SELECT #ReturnCode AS ErrorCode, #DisplayMsg AS ErrorMsg WHERE #returncode != 0) AS [Error]
ON [Error].ErrorCode = st.ReturnCode
FOR JSON AUTO, ROOT('APIResult')
Is it the case that the only way I can get this output is by stitching things together myself? JSON is still quite new to me, and I am struggling to get to grips with the makeup of it.
In this setup I would place error code after return code and would use subqueries with JSON PATH like this:
SELECT st.APIStatus AS 'ID'
,st.StatusMessage AS 'Status_Message'
,#VerboseMsg AS 'Developer_Message'
,#DisplayMsg AS 'User_Message'
,st.ReturnCode AS 'Return_Code'
, (SELECT #ReturnCode AS ErrorCode, #DisplayMsg AS ErrorMsg FOR JSON PATH) AS [Error]
,[Customer].CustomerID AS 'ID'
,[Customer].FirstName AS 'First_Name'
,[Customer].PaternalLastName AS 'Paternal_Last_Name'
,[Customer].EmailAddress AS 'Email_Address'
FROM (SELECT 200 AS APIStatus, 'Success' AS StatusMessage, #ReturnCode AS ReturnCode) st
LEFT JOIN (SELECT CustomerID
,FirstName
,PaternalLastName
,EmailAddress
,#ReturnCode AS ReturnCode
FROM #Customers
) As [Customer]
ON st.ReturnCode = [Customer].ReturnCode -- why?
FOR JSON AUTO, ROOT('APIResult')
Note that if you don't have any record in Error it will be omitted, but you can use null for that case, also I have noticed that you used #ReturnCode twice and I don;t know if that is intentionally or mistake.
Related
So I have 2 tables and one contains an string with an ID I want to replace with an string by another table.
I came up with this SQL, which should work, but it seems like an LEFT JOIN isn't allowed in this case.
UPDATE sales_channel_api_context AS api
SET api.payload = REPLACE(
api.payload,
SUBSTRING(
api.payload,
LOCATE('paymentMethodId":"', api.payload)+18,
32
),
LOWER(HEX(c.default_payment_method_id))
)
LEFT JOIN customer AS c
ON c.id = api.customer_id
WHERE api.payload LIKE '%paymentMethodId%' AND api.customer_id IS NOT NULL;
Does anyone know an SQL Query that does exactly this, without creating another table?
An temp table can be used but an new permanent table is no solution.
ChatGPT gave me a working solution and it is as follow:
UPDATE sales_channel_api_context
JOIN customer c ON c.id = sales_channel_api_context.customer_id
SET payload =
CASE
WHEN payload LIKE '%paymentMethodId%' THEN
REPLACE(
payload,
SUBSTRING(
payload,
LOCATE('paymentMethodId":"', payload) + 18,
32
),
LOWER(HEX(c.default_payment_method_id))
)
ELSE payload
END
WHERE sales_channel_api_context.customer_id IS NOT NULL;
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"}
I have to generate a JOSN object trough a SQL query. Finally i have to save this JSON object to a variable to return it later. So I came up with this code snippet for an ajax callback:
DECLARE
json_result JSON_OBJECT_T;
BEGIN
SELECT JSON_OBJECT('if' VALUE ID,
'data' VALUE DATA_BLOB,
'upload_date' VALUE UPLOAD_DATE FORMAT JSON) INTO json_result FROM TEST_TABLE WHERE ID = 0;
apex_json.open_object;
apex_json.write('success', true);
apex_json.write('result', json_result);
apex_json.close_object;
EXCEPTION
WHEN OTHERS THEN
apex_json.open_object;
apex_json.write('success', false);
apex_json.write('message', sqlerrm);
apex_json.close_object;
END;
But unlikely there is a error:
PL/SQL: ORA-00932: inconsistent datatypes: expected UDT got CHAR
I can not find out what is wrong here.
Help is highly appreciated.
Best regards,
Filip.
You shouldn't need apex_json, this can be done directly in sql. Here an example on the EMP table.
SELECT JSON_OBJECT (
KEY 'success' VALUE 'true',
KEY 'employees' VALUE (
SELECT JSON_ARRAYAGG(
JSON_OBJECT (
KEY 'ename' VALUE e.ename,
KEY 'empno' VALUE e.empno,
KEY 'job' VALUE e.job
)
)
FROM emp e
)
) AS employees
FROM dual;
So to assign this value to a page item, create a computation of type "SQL Query (returns single value)" with the code above on any process point before rendering.
I have something very odd happening and nothing seems to work. I have a SP in MySql that returns some results. When I run the SP in MySql workbench everything is correct. The query is quite long. But this is the LEFT JOIN is somehow creating the issues. I have other inner joins/left joins but they are fine.
SELECT DISTINCT Id.ReelTag
, Id.ECSPartNo
, WM.ShortDescription AS Description
, Id.ReelTagSerial
, group_concat(DISTINCT RA.UniqueID) AS UniqueID
, group_concat(DISTINCT coalesce(RA.OrdNo, Std.OrdNo)) AS OrdNo
, Id.Received
, IFNULL(Std.ReelQuantity,0) AS OriginalQuantity
, IFNULL(Id.Quantity,0) AS CurrentQuantity
, IFNULL(yest.Quantity,0) AS YesterdayQuantity
, IFNULL(cuts.Quantity,0) AS QuantityChanged
, cuts.OrdNo AS OrdNoChange
, IFNULL(CC.ShipQuantity,0) AS ShipQuantity
, CC.OrdNo AS OrdNo_Allocated
, IFNULL(Id.Quantity,0) - IFNULL(yest.Quantity,0) AS changeAOF_Yesterday
FROM InventoryDtl Id .....
LEFT JOIN (
SELECT
SourceReel
, SUM(CASE WHEN Action = 'Insert' THEN TotalQuantity
WHEN Action = 'Delete' THEN -TotalQuantity
ELSE 0 END) AS Quantity
, group_concat(DISTINCT CASE WHEN Action = 'Insert' THEN concat(OrdNo,'(Cut)') ELSE concat(OrdNo,'(UnCut)') END) AS OrdNo
FROM (
SELECT
Action
, SourceReel
, OrdNo
, SUM(Quantity) AS TotalQuantity
FROM CableCuts_Log CD
WHERE 1=1
AND 1 = CASE
WHEN SourceReel IS NOT NULL AND OrdNo LIKE 'E9%' AND Quantity > 0 AND DaTediff(Now(),LogDate) = 0 THEN 1
ELSE 0 END
GROUP BY Action, SourceReel, OrdNo
) CC
WHERE 1=1
GROUP BY SourceReel
) cuts ON Id.ReelTag = cuts.SourceReel
Again, When I run this in MySql workbench it's fine and loads in a second if that makes a difference.
But when I call my API to call the SP using ...
let inventoryReport = await models.sequelize
.query(
`call rpt_DailyInventoryReport($location, $byECSPartNo);`,
{ bind: {location: req.body.location, byECSPartNo: null} },
)
for(i=0;i<inventoryReport.length;i++) {
if(inventoryReport[i].ReelTagSerial == '6906' || inventoryReport[i].ReelTagSerial == '6858') {
console.log(inventoryReport[i]);
}
}
and exporting that into an Excel using ExcelJS, the 2 "cuts" columns from the query are essentially NULLs, because the return value of the "cuts" select are coming up NULLS, which is why it's giving the IFNULL value instead. Again this work in MySql workbench.
These are the values the API throws out.
{
"ReelTagSerial": 6858,
"CurrentQuantity": 700,
"YesterdayQuantity": 2500,
"QuantityChanged": 0,
"OrdNoChange": null,
"ShipQuantity": 0,
"OrdNo_Allocated": null,
"changeAOF_Yesterday": -1800
},
{
"ReelTagSerial": 6906,
"CurrentQuantity": 2730,
"YesterdayQuantity": 3330,
"QuantityChanged": 0,
"OrdNoChange": null,
"ShipQuantity": 0,
"OrdNo_Allocated": null,
"changeAOF_Yesterday": -600
},
Here are the 2 rows that their values should be.
{
"ReelTagSerial": 6906,
"CurrentQuantity": 2730,
"YesterdayQuantity": 3330,
"QuantityChanged": 600,
"OrdNoChange": E92021(Cut),
"ShipQuantity": 0,
"OrdNo_Allocated": null,
"changeAOF_Yesterday": -600
}
{
"ReelTagSerial": 6858,
"CurrentQuantity": 700,
"YesterdayQuantity": 2500,
"QuantityChanged": 1800,
"OrdNoChange": E912345(Cut),
"ShipQuantity": 0,
"OrdNo_Allocated": null,
"changeAOF_Yesterday": -1800
},
I have tried creating a temp table and defining the field type of varchar, text, mediumint for the QuantityChanged field.
At first I thought maybe ExcelJs was not liking the key/value pairs but then I simply console logged the 2 rows and they are returning like that from Sequelize. I have tried casting the 2 fields with every datatype possible.
, CAST(cuts.OrdNo AS char) AS OrdNoChange
, CAST(cuts.OrdNo AS binary) AS OrdNoChange
Now I am just immediately sending the return result as a response back to Postman and seeing all the rows to make sure for whatever reason those values are not being set in other rows. But they seem to be all good.
If I simply rearrange the columns so that the two bad fields get values of other fields, they do populate with their values, so that's about as far as I got. Or if I put just 1000 or a string type it returns correctly, so IT MUST be something to do with those 2 data types from the "cuts" query.
I have tried returning simply returning these from the "cuts" join query
SELECT
DISTINCT SourceReel
#, CAST(SUM(CASE WHEN Action = 'Insert' THEN TotalQuantity
# WHEN Action = 'Delete' THEN -TotalQuantity
# ELSE 0 END) AS UnSigned) AS Quantity
, SUM(TotalQuantity) AS Quantity
, OrdNo
#, group_concat(DISTINCT CASE WHEN Action = 'Insert' THEN concat(OrdNo,'(Cut)') ELSE concat(OrdNo,'(UnCut)') END) AS OrdNo
Nothing is working. Spent hours troubleshooting and searching...
Need some help please :)
Thanks in advance.
I was able to get around this by creating a table and inserting into it during the SP call. Then in NodeJs I created a model of that table and just used to find everything in the table immediately after I called the SP.
let inventoryReport = await models.Rpt_DailyInventory_Temp.findAll();
Then just TRUNCATE the table every time the SP is called.
So I am using Kentico CMS Desk 7 to generate reports for my company. In Kentico you create parameters and then create a table using sql and those parameters with the # symbol so whatever the user enters into that parameter, it will be the value of a parameter variable like #Status. I am wanting to add the ability for the user to either enter in one value, multiple values, or no values into the parameters, but I do not know how to implement the multiple values. I am a little new to SQL so bear with me. This is the SQL code I have right now:
select
ClaimNumber as 'Claim Number',
CustomerName as 'Customer Name',
DollarAmount as 'Dollar Amount',
[ReasonCode] as 'Reason code',
rt.[ReasonTypeName] as 'Reason type',
PlantNumber as 'Selling Company',
Status as 'Status'
from TABLE1 as c
join TABLE2 as u on u.UserID = c.DocumentCreatedByUserID
left join TABLE3 as rt on rt.ItemId = c.ReasonType
where ClaimDate between #FromDate and #ToDate
and ReasonCode like #ReasonCode
and ReasonType like #ReasonType
and (#SellingCompany = '' or PlantNumber = #SellingCompany)
and Status like #Status
order by ClaimNumber;
The parameter that I am trying to do this with is the selling company parameter denoted as #SellingCompany. Right now, this works for users not entering in any value and users entering in only one value, but I would like for users to have the ability to input multiple values separated by commas. I feel like an IN operator might work, but I am inexperienced in SQL and I don't know how I would implement this. I can't publish the data obviously because there is customer information, but this statement works as it is and I just need to know how to implement what I'm wanting to do. Thanks guys!
Have you tried this?
select
ClaimNumber as 'Claim Number',
CustomerName as 'Customer Name',
DollarAmount as 'Dollar Amount',
[ReasonCode] as 'Reason code',
rt.[ReasonTypeName] as 'Reason type',
PlantNumber as 'Selling Company',
Status as 'Status'
from TABLE1 as c
join TABLE2 as u on u.UserID = c.DocumentCreatedByUserID
left join TABLE3 as rt on rt.ItemId = c.ReasonType
where ClaimDate between #FromDate and #ToDate
and ReasonCode like #ReasonCode
and ReasonType like #ReasonType
and (#SellingCompany = '' or PlantNumber IN (#SellingCompany))
and Status like #Status
order by ClaimNumber;
I use this SQL function specifically when I need to cast a delimited string to a table value to use with the IN operator.
CREATE FUNCTION [dbo].[ParseIDListToTable]
(#vc_Ids nvarchar(MAX))
RETURNS #Id_table TABLE
(ID nvarchar(15))
BEGIN
DECLARE #in_Index1 AS INT, --Used to store ID delimiter(',') position in string
#vc_ID AS NVARCHAR(15)
/* initialize working variables */
SET #in_Index1 = CHARINDEX(',',#vc_Ids)
/* loop through ids in delimited string */
WHILE (#in_Index1 > 0 OR LEN(#vc_Ids) > 0)
BEGIN
/* parse out single id for processing */
IF #in_Index1 > 0
BEGIN
SET #vc_ID = Left(#vc_Ids,#in_Index1 - 1)
SET #vc_Ids = Right(#vc_Ids,Len(#vc_Ids) - #in_Index1)
END
ELSE
BEGIN
SET #vc_ID = #vc_Ids
SET #vc_Ids = ''
END
INSERT #Id_table (ID)
VALUES(#vc_ID)
/* prepare to loop */
SET #in_Index1 = CHARINDEX(',',#vc_Ids)
END
/* return the ids */
RETURN
END
Then I use it in my SELECT statement like so
WHERE PlantNumber IN (SELECT * FROM dbo.ParseIDListToTable('Microsoft,Apple,Dell'))
This should return the results you're looking for.