I'm writing a stored procedure to get list of IOT parameter data under a specific machine and the result of stored procedure should have a specific JSON format. the problem is, when I tried to add a parameter data under another machine I found out the subquery result append all its results under all machines. I want to Alter the query so each machine should have just its parameter data under it and not changing the JSON format.
This is the Stored procedure:
declare #jsonTwo nvarchar(max)=(
Select JSON_QUERY((
select CAST((
select
MAE.MachineName as MachineName
,
(select IOTR.MachineCode, IOTP.IotParameterName, IOTR.CreatedAt, datename(WEEKDAY, IOTR.CreatedAt) as FilterRange,
avg(IOTP.IotParameterValue) as ParameterValue,MP.UpperControlLimit , MP.LowerControlLimit
from IOTMachineParameters IOTP
inner join IOTMachineReadings IOTR ON IOTP.IotMachineID = IOTR.Id
inner join MachineAndEquipments MAE on MAE.MachineCode = IOTR.MachineCode
inner join MachineParameters MP on IOTP.IotParameterName = MP.ParamterName
where
MP.ParameterType = 'PARAMETERIZED'
and IotP.IsChecked = 1
and IOTR.CompanyCode = 'DA-1663079927040'
and MAE.MachineCode = IOTR.MachineCode
and IOTP.IotMachineID = IOTR.Id
and IOTR.CreatedAt >= '2022-09-01' and IOTR.CreatedAt <= '2022-11-16 10:11:00.0000000'
group by IOTR.MachineCode,IOTP.IotParameterName,IOTR.CreatedAt,datename(WEEKDAY, IOTR.CreatedAt),MAE.MachineName,MP.LowerControlLimit,MP.UpperControlLimit
for json path) as MachineReadings
from MachineAndEquipments MAE inner join IOTMachineReadings IOTR ON MAE.MachineCode = IOTR.MachineCode
inner join IOTMachineParameters IOTP ON IOTP.IotMachineID = IOTR.Id
where MAE.CompanyId = 'DA-1663079927040'
and IOTP.IotMachineID = IOTR.Id
group by MAE.MachineName
for json path ,Include_null_values)as nvarchar(max))as part1 for json path, without_array_wrapper)));
select #jsonTwo as data
when i run the MachineReadings subquery it returns all the records
reults of MachineReadings subquery
The tables are:
Tables of the query
so is there a way to like filter out the subquery results based on the outer selection of Machine Name.
I expected each object, has Machine name and a list of IOT parameter reading data under this machine name only. Instead I found that each Machine name has the same exact list of IOT parameter reading data
Targeted JSON format:
{
"part1":[
{
"MachineName":"Machine X",
"MachineReadings":[
{
"MachineCode":"Machine-012",
"MachineName":"Machine X",
"IotParameterName":"t",
"CreatedAt":"2022-11-14T11:11:42",
"FilterRange":"Monday",
"ParameterValue":20.000000,
"UpperControlLimit":0.00,
"LowerControlLimit":0.00
}
]
},
{
"MachineName":"Machine Y",
"MachineReadings":[
{
"MachineCode":"Machine-789",
"MachineName":"Machine Y",
"IotParameterName":"a test",
"CreatedAt":"2022-11-16T10:11:00",
"FilterRange":"Wednesday",
"ParameterValue":3.000000,
"UpperControlLimit":0.00,
"LowerControlLimit":0.00
},
{
"MachineCode":"Machine-789",
"MachineName":"Machine Y",
"IotParameterName":"new parameter",
"CreatedAt":"2022-11-15T10:09:51",
"FilterRange":"Tuesday",
"ParameterValue":13.500000,
"UpperControlLimit":0.00,
"LowerControlLimit":0.00
}
]
}
]
}
This is The Resulted JSON Format:
{
"part1":[
{
"MachineName":"rtyy",
"MachineCode":"Machine-012",
"MachineReadings":[
{
"MachineCode":"Machine-012",
"MachineName":"rtyy",
"IotParameterName":"t",
"CreatedAt":"2022-11-14T11:11:42",
"FilterRange":"Monday",
"ParameterValue":20.000000,
"UpperControlLimit":0.00,
"LowerControlLimit":0.00
},
{
"MachineCode":"Machine-789",
"MachineName":"the other 789",
"IotParameterName":"a test",
"CreatedAt":"2022-11-16T10:11:00",
"FilterRange":"Wednesday",
"ParameterValue":3.000000,
"UpperControlLimit":0.00,
"LowerControlLimit":0.00
},
{
"MachineCode":"Machine-789",
"MachineName":"the other 789",
"IotParameterName":"new parameter",
"CreatedAt":"2022-11-15T10:09:51",
"FilterRange":"Tuesday",
"ParameterValue":13.500000,
"UpperControlLimit":0.00,
"LowerControlLimit":0.00
}
]
},
{
"MachineName":"the other 789",
"MachineCode":"Machine-789",
"MachineReadings":[
{
"MachineCode":"Machine-012",
"MachineName":"rtyy",
"IotParameterName":"t",
"CreatedAt":"2022-11-14T11:11:42",
"FilterRange":"Monday",
"ParameterValue":20.000000,
"UpperControlLimit":0.00,
"LowerControlLimit":0.00
},
{
"MachineCode":"Machine-789",
"MachineName":"the other 789",
"IotParameterName":"a test",
"CreatedAt":"2022-11-16T10:11:00",
"FilterRange":"Wednesday",
"ParameterValue":3.000000,
"UpperControlLimit":0.00,
"LowerControlLimit":0.00
},
{
"MachineCode":"Machine-789",
"MachineName":"the other 789",
"IotParameterName":"new parameter",
"CreatedAt":"2022-11-15T10:09:51",
"FilterRange":"Tuesday",
"ParameterValue":13.500000,
"UpperControlLimit":0.00,
"LowerControlLimit":0.00
}
]
}
]
}
The issue could be there's no relationship between the correlated subquery MachineReadings and tables in outer query FROM clause. Thus, the result of MachineReadings is applied to every row returned by outer query.
Formatted your code below for easy reference:
declare #jsonTwo nvarchar(max)=(
Select
JSON_QUERY((
select
CAST((
select
MAE.MachineName as MachineName
,
(
select
IOTR.MachineCode,
IOTP.IotParameterName,
IOTR.CreatedAt,
datename(WEEKDAY, IOTR.CreatedAt) as FilterRange,
avg(IOTP.IotParameterValue) as ParameterValue,
MP.UpperControlLimit ,
MP.LowerControlLimit
from
IOTMachineParameters IOTP
inner join IOTMachineReadings IOTR ON
IOTP.IotMachineID = IOTR.Id
inner join MachineAndEquipments MAE on
MAE.MachineCode = IOTR.MachineCode
inner join MachineParameters MP on
IOTP.IotParameterName = MP.ParamterName
where
MP.ParameterType = 'PARAMETERIZED'
and IotP.IsChecked = 1
and IOTR.CompanyCode = 'DA-1663079927040'
and MAE.MachineCode = IOTR.MachineCode
and IOTP.IotMachineID = IOTR.Id
and IOTR.CreatedAt >= '2022-09-01'
and IOTR.CreatedAt <= '2022-11-16 10:11:00.0000000'
group by
IOTR.MachineCode,
IOTP.IotParameterName,
IOTR.CreatedAt,
datename(WEEKDAY, IOTR.CreatedAt),
MAE.MachineName,
MP.LowerControlLimit,
MP.UpperControlLimit
for json path) as MachineReadings
from
MachineAndEquipments MAE
inner join IOTMachineReadings IOTR ON
MAE.MachineCode = IOTR.MachineCode
inner join IOTMachineParameters IOTP ON
IOTP.IotMachineID = IOTR.Id
where
MAE.CompanyId = 'DA-1663079927040'
and IOTP.IotMachineID = IOTR.Id
group by
MAE.MachineName
for json path ,
Include_null_values)as nvarchar(max))as part1 for json path,
without_array_wrapper)));
select #jsonTwo as data
Related
Update: See the "Update" section below for the latest.
I have been working with Knex.js to build SQL queries in Node.js, and have the following code. This code works on a sort of graph data model (nodes and links), where there is a links table which has everything (links link to links). Given this code, I am wondering how I can make it one query instead of one query per attribute which is how it is now. The getTableName() function returns a string_links table for string values, and <x>_links tables for the other datatypes, while the "basic" links table is just called links.
Essentially how this works is, first query the top level where the parent_id is equal to some "type" ID, say we are querying "user" objects, the type would be "user". So let instance = ... is getting all the instance links from this user type. Then we go through each field of a query (a query for now is just boolean-valued map, like { email: true, name: true }). For each field of the query, we make a query to find all those nodes, linked off the instance, as so-called property links.
There are two types of properties, but don't need to go into too much detail on that. Essentially there are complex properties with audit trails and simple properties without audit trails. That is what is meant by the interactive branch in the logic.
How can I make this into one SQL query? The SQL query it prints out for an example is like this:
select "id" from "links" where "parent_id" = '47c1956bz31330c' and "name" = 'link' limit 1
select "value" from "string_links" where "parent_id" = (select "value" from "links" where "parent_id" = '47c1956bz31330cv' and "name" = 'name' limit 1) and "name" = 'value' limit 1
select "value" from "text_links" where "parent_id" = (select "value" from "links" where "parent_id" = '47c1956bz31330cv' and "name" = 'website' limit 1) and "name" = 'value' limit 1
select "value" from "integer_links" where "parent_id" = (select "value" from "links" where "parent_id" = '47c1956bz31330cv' and "name" = 'revenue' limit 1) and "name" = 'value' limit 1
select "value" from "boolean_links" where "parent_id" = '47c1956bz31330' and "name" = 'verified' limit 1
The original Node.js for Knex.js is here, but really I'm just concerned with how to write this as one regular SQL query, and I can figure out how to make it in Knex.js from there:
async function selectInteractiveInstance(user, name, query) {
const type = model.types[name]
const typeId = await baseSchemaController.selectType(name)
let instance = await knex.from(`links`)
.select('id')
.where('parent_id', typeId)
.where('name', 'instance')
.first()
// { id: 123, props: { ... } }
instance.props = {}
for (let field in query) {
let data = query[field]
let attrSchema = type[field]
const tableName = baseSchemaController.getTableName(attrSchema.type)
if (attrSchema.interactive) {
const query1 = knex
.from(`links`)
.select('value')
.where('parent_id', instance.link)
.where('name', field)
.first()
const record = await knex
.from(tableName)
.select('value')
.where('home', query1)
.where('name', 'value')
.first()
if (record) {
instance.props[field] = record.value
}
} else {
const record = await knex
.from(tableName)
.select('value')
.where('parent_id', instance.id)
.where('name', field)
.first()
if (record) {
instance.props[field] = record.value
}
}
}
return instance
}
The reason for asking is because the number of queries of this function is equal to the number of properties on the object, and I would like to avoid that, but not really that great at SQL yet. I don't see a straightforward or clear path on how to make this into one query, or know if it's possible.
It's also an issue for the following reason. If I want to grab 100 links, and their "fields" (in the primitive link tables), such that the primitive link values match a certain value, then you need to query all field tables simultaneously to see if the query can be satisfied.
Update
I finally landed on a query that works in the optimistic case:
select
"x"."id" as "id",
"s1"."value" as "name",
"s2"."value" as "inc_id",
"s3"."value" as "website",
"s4"."value" as "revenue",
"s5"."value" as "verified"
from "links" as "x"
inner join "links" as "c1" on "c1"."parent_id" = "x"."id"
inner join "string_links" as "s1" on "s1"."parent_id" = "c1"."value"
inner join "links" as "c2" on "c2"."parent_id" = "x"."id"
inner join "string_links" as "s2" on "s2"."parent_id" = "c2"."value"
inner join "links" as "c3" on "c3"."parent_id" = "x"."id"
inner join "text_links" as "s3" on "s3"."parent_id" = "c3"."value"
inner join "links" as "c4" on "c4"."parent_id" = "x"."id"
inner join "integer_links" as "s4" on "s4"."parent_id" = "c4"."value"
inner join "boolean_links" as "s5" on "s5"."parent_id" = "x"."id"
where "x"."parent_id" = '47c1956bz31330'
and "x"."name" = 'link'
and "c1"."name" = 'name'
and "s1"."name" = 'value'
and "c2"."name" = 'inc_id'
and "s2"."name" = 'value'
and "c3"."name" = 'website'
and "s3"."name" = 'value'
and "c4"."name" = 'revenue'
and "s4"."name" = 'value'
and "s5"."name" = 'verified'
This returns an object similar to what I am looking for, joining the same table several times, along with the primitive tables.
However, if any of the values are not linked (are socalled "null" in this context), then the inner join will fail and it will return nothing. How can I still have it return a subset of the object properties, whatever it can find? Is there anything like optional inner joins or anything like that?
Use LEFT JOIN and move possibly unsatisfied predicates to ON clause. Kind of
select
"x"."id" as "id",
"s1"."value" as "name",
"s2"."value" as "inc_id",
"s3"."value" as "website",
"s4"."value" as "revenue",
"s5"."value" as "verified"
from "links" as "x"
left join "links" as "c1" on "c1"."parent_id" = "x"."id" and "c1"."name" = 'name'
left join "string_links" as "s1" on "s1"."parent_id" = "c1"."value" and "s1"."name" = 'value'
left join "links" as "c2" on "c2"."parent_id" = "x"."id" and "c2"."name" = 'inc_id'
left join "string_links" as "s2" on "s2"."parent_id" = "c2"."value" and "s2"."name" = 'value'
left join "links" as "c3" on "c3"."parent_id" = "x"."id" and "c3"."name" = 'website'
left join "text_links" as "s3" on "s3"."parent_id" = "c3"."value" and "s3"."name" = 'value'
left join "links" as "c4" on "c4"."parent_id" = "x"."id" and "c4"."name" = 'revenue'
left join "integer_links" as "s4" on "s4"."parent_id" = "c4"."value" and "s4"."name" = 'value'
left join "boolean_links" as "s5" on "s5"."parent_id" = "x"."id" and "s5"."name" = 'verified'
where "x"."parent_id" = '47c1956bz31330'
and "x"."name" = 'link'
Edit: FYI. So my PrimaryImage is actually an int and is used as a foreign key to my Images table. I just wanted to explain that so it is clear why I set it to on f.PrimaryImage = i.Id. For some reason all my rows are getting populated with every i.Id, i.EntityId, i.ImageTypeId, & i.ImageUrl instead of just where my f.PrimaryImage = i.Id.
I am writing a SQL stored procedure to SELECT ALL and I want to combine my last 4 columns Id, EntityId, ImageTypeId, ImageUrl into one new column PrimaryImage as a single JSON object. I was able to successfully do that with my Skills column but for that I needed it as an array that holds JSON objects so simply using "FOR JSON AUTO" took care of that. But like I said for PrimaryImage, I need that as a single JSON object that contains my Id, EntityId, ImageTypeId, & ImageUrl. I included a picture of my result after executing this proc and right below my table I drew a representation of what I want the column to look like. Just to clarify I have four tables my Friends, FriendSkills, Skills, & Images tables which I've used join statements organize accordingly. Basically my schema needs to look like this:
{
"id": 0000,
"userId": "String"
"bio": "String",
"title": "String",
"summary": "String",
"headline": "String",
"statusId": "String",
"slug": "String",
"skills": [{id: 0, name: "String"},{id: 0, name: "String"}],
"primaryImage": {
"id": 0,
"entityId": 0,
"imageTypeId": 0,
"imageUrl": "String"
}
}
Here is my stored procedure
ALTER PROC [dbo].[Friends_SelectAllV2]
AS
/* --- Test Proc ------
Execute dbo.Friends_SelectAllV2
*/
BEGIN
Select f.Id
,f.UserId
,f.DateAdded
,f.DateModified
,f.Title
,f.Bio
,f.Summary
,f.Headline
,f.Slug
,f.StatusId
,Skills = ( SELECT s.Id,
s.Name
From dbo.Skills as s inner join dbo.FriendSkills fs
on s.Id = fs.SkillId
Where f.Id = fs.FriendId
FOR JSON AUTO
),
PrimaryImage = (SELECT i.Id,
i.EntityId,
i.ImageTypeId,
i.ImageUrl
From dbo.Friends f left join dbo.Images as i
on f.PrimaryImage = i.Id
Where f.PrimaryImage = i.Id
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
)
END
You don't actually need another subquery if the inner property is a single object, you can use FOR JSON PATH with explicit path syntax.
Select f.Id
,f.UserId
,f.DateAdded
,f.DateModified
,f.Title
,f.Bio
,f.Summary
,f.Headline
,f.Slug
,f.StatusId
,Skills = ( SELECT s.Id,
s.Name
From dbo.Skills as s inner join dbo.FriendSkills fs
on s.Id = fs.SkillId
Where f.Id = fs.FriendId
FOR JSON AUTO
),
i.Id AS [PrimaryImage.Id],
i.EntityId AS [PrimaryImage.EntityId],
i.ImageTypeId AS [PrimaryImage.ImageTypeId],
i.ImageUrl AS [PrimaryImage.ImageUrl]
From dbo.Friends f
left join dbo.Images as i on f.PrimaryImage = i.Id
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER;
If however there are multiple images, and you need a subquery, the issue here is that you are putting the whole Friends table in again, instead of correlating the outside table. You would also remove WITHOUT_ARRAY_WRAPPER.
Select f.Id
,f.UserId
,f.DateAdded
,f.DateModified
,f.Title
,f.Bio
,f.Summary
,f.Headline
,f.Slug
,f.StatusId
,Skills = ( SELECT s.Id,
s.Name
From dbo.Skills as s inner join dbo.FriendSkills fs
on s.Id = fs.SkillId
Where f.Id = fs.FriendId
FOR JSON AUTO
),
PrimaryImage = (SELECT i.Id,
i.EntityId,
i.ImageTypeId,
i.ImageUrl
FROM dbo.Images as i
WHERE f.PrimaryImage = i.Id
FOR JSON PATH
)
From dbo.Friends f
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER;
I've got a PSQL function that has 3 joins in it and the data is returned in a json object. I have a 4th table that I need to get data from but it has a one-to-many relationship with the table I wish to join on.
This is my current code:
select json_agg(row_to_json(s)) as results from (
select g.*,row_to_json(o.*) as e_occurence,
row_to_json(d.*) as e_definition,
row_to_json(u.*) as e_e_updates,
cardinality(o.m_ids) as m_count
from schema.e_group g
join schema.e_occurrence o on g.id = o.e_group_id
join schema.e_definition d on g.e_id = d.id
left join schema.e_e_updates u on d.id = u.e_id
) s
This gets me an array of objects that follows this rough structure:
[
{
"id": 11308158,
"e_id": 16,
"created_on": "2020-09-09T12:08:07.556062",
"event_occurence": {
"id": 9081887,
"e_id": 16,
"e_group_id": 11308158
},
"e_definition": {
"id": 16,
"name": "Placeholder name"
},
"e_e_updates": {
"id": 22,
"user_id": "7281057e-2876-1673-js7d-7cqj611b4557",
"e_id": 16
},
"m_count": 0
}
]
My problem is that the table e_e_updates can have multiple records for each corresponding e_definition.id.
Clearly the join will not work as hoped in this instance as I'd like e_e_updates to be an array of all the linked rows.
Is there an alternative means of solving this issue?
Basically, you need another level of aggregation. This should do what you want:
select json_agg(row_to_json(s)) as results
from (
select
g.*,
row_to_json(o.*) as e_occurence,
row_to_json(d.*) as e_definition,
u.u_arr as e_e_updates,
cardinality(o.m_ids) as m_count
from schema.e_group g
join schema.e_occurrence o on g.id = o.e_group_id
join schema.e_definition d on g.e_id = d.id
left join (
select e_id, json_agg(row_to_json(*)) u_arr
from schema.e_e_updates
group by on e_id
) u on d.id = u.e_id
) s
You could also do this with a subquery:
select json_agg(row_to_json(s)) as results
from (
select
g.*,
row_to_json(o.*) as e_occurence,
row_to_json(d.*) as e_definition,
(
select json_agg(row_to_json(u.*))
from schema.e_e_updates u
where u.e_id = d.id
) as e_e_updates,
cardinality(o.m_ids) as m_count
from schema.e_group g
join schema.e_occurrence o on g.id = o.e_group_id
join schema.e_definition d on g.e_id = d.id
) s
I am writing a post api in c# to select some values in Azure Cosmos db and is using direct sql queries.
The aim to get the highest value against each id from the request.
request body:
[
{
"userid":"1"
},
{
"userid":"4"
}
]
Db looks like:
{
"userid":"1",
"value":"10",
"Date":"10-9-19"
}
{
"userid":"1",
"value":"20",
"Date":"11-8-19"
}
{
"userid":"4",
"value":"30",
"Date":"10-9-19"
}
{
"userid":"4",
"value":"40",
"Date":"11-9-19"
}
Expected output:
[
{
"userid":"4",
"value":"40",
"Date":"11-9-19"
},
{
"userid":"1",
"value":"20",
"Date":"11-8-19"
}
]
I tried to get the id's into an array then used 'IN' operator, but it would be helpful and appreciated is there more simple query would help.
try the following to get the results.
As per your data, this will work.
SELECT userid,
MAX(value) value,
MAX(Date) Date
FROM YourTable
GROUP BY userid
ORDER BY userid
If you want related date for the MAX(Value), then try this.
SELECT Y.userid, Y.Value, Y.Date
FROM YourTable Y
JOIN
(
SELECT userid,
MAX(value) value
FROM YourTable
GROUP BY userid
)D ON D.userid = Y.userid AND D.value = Y.value
Can anyone help me to translate my sql query to linq? I am not so good in linq can anyone help me out with it please .
select
s.UploadFilename ,
a.ID,
isnull(s.Status_id,1) as 'Status_id' ,
a.CaseID,
a.RecordType,
a.ColumnName,
a.FieldName,
a.OldValue,
a.NewValue,
a.ModifiedBy,
A.ModifiedOn,
isnull(u.UserName,'') as 'UserName'
from [dbo].[AuditTrail] as A
left join Case_Status as s
on s.Case_Id=A.CaseID
left join [dbo].[User] as u
on a.ModifiedOn =u.UserID
where A.CaseID=5338
This is not the answer, my query is big, it was not accepting this query as comment.
This query is not returning records :
var AuditTrailFile = (from AT in db.AuditTrails
join ATCS in db.AssnAuditTrailCaseStatus
on new {ID = AT.ID} equals new { ID = (int)(ATCS.AuditTrialID) }
into ATCS_join
from ATCS in ATCS_join.DefaultIfEmpty()
join CS in db.Case_Status on new { Case_StatusId = (int)(ATCS.CaseStatusID) }
equals new { Case_StatusId = CS.Case_StatusId } into CS_join
from CS in CS_join.DefaultIfEmpty()
where
AT.CaseID == CaseID
orderby
AT.ModifiedOn descending
select new
{
ID = (int?)AT.ID,
Case_StatusId = CS.Case_StatusId != null ? CS.Case_StatusId : 1, //Case_StatusId comes for row whose FieldName=Status Else No Case_StatusId
AT.CaseID,
AT.RecordType,
AT.ColumnName,
AT.FieldName,
AT.OldValue,
AT.NewValue,
AT.ModifiedBy,
UserName = AT.User != null ? AT.User.UserName : "",
AT.ModifiedOn,
UploadFilename = CS.UploadFilename
}).ToList();