How to retreive full Asset Hierarchy in Azure Digital Twins using REST API - azure-iot-hub

I want to retrieve Asset Hierarchy of Twin Graph in Azure Digital Twins using REST API, is there any way to do so?

The situation you described in your question (and the comments) can be solved by writing multiple queries. To get the full graph as a hierarchy, you will first need to query the top-level items (in your case organisations) and then you can either run a query for every organisation or fit it all into one query. I'm making some assumptions about your hierarchy in these examples, you will need to adapt them to your ontology.
Getting all the organisations:
SELECT ROOM FROM DIGITALTWINS DT WHERE IS_OF_MODEL(DT, 'dtmi:example:organisation;1', exact)
Getting the hierarchy for one organisation:
SELECT Organisation, Factory, ProductionLine, Equipment
FROM DIGITALTWINS Organisation
JOIN Factory RELATED Organisation.contains
JOIN ProductionLine RELATED Factory.contains
JOIN Equipment RELATED ProductionLine.contains
WHERE Organisation.$dtId = 'Organisation1'
Getting the hierarchy for all previously found organisations:
SELECT Organisation, Factory, ProductionLine, Equipment
FROM DIGITALTWINS Organisation
JOIN Factory RELATED Organisation.contains
JOIN ProductionLine RELATED Factory.contains
JOIN Equipment RELATED ProductionLine.contains
WHERE Organisation.$dtId IN ['Organisation1', 'Organisation2', 'Organisation3', 'etc...']
There is a limitation of 5 JOIN statements to take into account. And both queries will return a row for every Equipment twin, so you will need to transform the result a bit.
In both the SDK and REST API, you can achieve this through the Query operation.

Related

Get all Many:Many relationships from reference/join-table

I am having difficulty querying for possible relationships in a Many:Many scenario.
I present my schema:
What I do know how to query with this schema is:
All Bands that a given User belongs to.
All Users that belong to a given Band.
What I am trying to do is:
Get all Band Members across all Bands that a given User belongs to.
ie, say I am in 5 bands, I want to know who all of my bandmates are.
My first questions are:
Is there a name for this type of query? Where I am more interested in the joined relationships than what I am joined to (just saying that made me want to put this whole system into a Graph DB :/ )? I'd like to learn proper terminology to help me google for problems down the road.
Is this a terrible idea in RDBMS land in general? I feel like this should be a common use case but I want to know if I'm totally approaching this wrong.
To recap:
I am looking to query the above schema with the expected output being one row per User as Band Members that a given User shares a Band with.
Terminology
Your terminology seems to be correct - "many to many", often written as "many:many" with a colon. Sometimes the middle table (band_members) is called the "bridge table".
You can probably drop band_members.id, since the two foreign keys also make up a composite primary key (and the primary key can actually be defined that way, since normally a User cannot be a member of the same Band twice. The only exception to that is if a User could have more than one role in the same Band).
Solution
On the surface of it, this sounds easy - we can see the relationships of the tables, and one would normally just use an INNER JOIN between them. There are three tables, so that would be two joins.
However, we have to conceptualise the problem correctly first. The problem we have is that the join between Users and Band Members (user ID) is actually to be used for two things:
which User is in what Band
filtering by User
So to do this we need to introduce one table with multiple purposes:
SELECT
Users.first_name, Users.last_name
FROM Users
INNER JOIN Band_Members Band_Members1 ON (Band_Members1.user_id = Users.Id)
INNER JOIN Band_Members Band_Members2 ON (Band_Members1.band_id = Band_Members2.band_id)
WHERE
Band_Members2.user_id = 1
You can see here that I have joined Band_Members twice, and when one does that, one has to alias them differently, so they can be separately referenced. The first instance does the obvious join between the Users table and the bridge table, and the second one does a link between "Users who are in Bands" and "Bands that I am in".
Of course, this solution requires that you know your User ID. If you had wanted to do a similar query but filter based on your name, then you would have to join to another (re-aliased) copy of the User table, so that you can differentiate between the two different purposes: "Users who are in bands" and "your User".

Azure WebJobs and models and SQL queries

I've got an Azure web app serving as a restful API. This back end defines the models being used by my system: Users, Teams, Projects, Items. A User can join many Teams, and the Team-->Project-->Item structure is a Grandparent-->Parent-->Child relationship.
I want to set up a daily email digest of updates for each User. I want this to be run in an Azure web job in the background, so as not to burden the customer facing API server.
When writing code in the API's controllers, I can write easy queries like this to get all the items that belong to a certain team (because the models and their hierarchical relationships are defined in the API):
var items = await (from x in db.Items
where x.Project.Team.TeamId == teamId
select x).ToListAsync();
However, in the web job, I can't do that - there's no models to speak of. I see a couple options:
Option A) Somehow use (or re-create) the models (and the DB context?) in the web job. If this is possible (and not a terrible idea), I think this is what I want.
Option B) Make peace with many, verbose SQL queries in the web job. I can query the User table for what Teams they're part of. Then, for each Team, I can query the Project table for a set of Projects. Then, for each Project, I can query the Items table for the Items in each project.
Is there a way to make a magical SQL query that works more efficiently than Option B's many calls to the database? I've tried things like the following, but haven't found a working scheme:
SELECT * FROM Items WHERE Projects WHERE Teams = teamId;
The best way from my point of view is Opition C) - refactor your solution and create separate project that will contain data context, models and all migrations. After that you can reference this project from your main app and from WebJob. You can also move some core database-related functions into this project to make them accessible from other projects.
Well, it appears what I wanted was a JOIN: What's the difference between INNER JOIN, LEFT JOIN, RIGHT JOIN and FULL JOIN?
I've really only ever used LINQ to pull from SQL databases, now I know how to do it the old fashioned, more verbose, less intuitive way :)
Here's the code that I'm using to pull all the items that belong to a given team:
SqlCommand command = new SqlCommand();
command.CommandText = #"SELECT Items.* FROM Items
JOIN Projects
ON Projects.ProjectId = Items.ProjectId
JOIN Teams
ON Teams.TeamId = Projects.TeamId
WHERE Teams.TeamId = '" + teamId.ToString() + "'";

Best practices on multiple SQL calls to avoid repeated results

Should multiple SQL calls be used when it can be done in one call, if the one call returns repeated data?
Like say, and this is a really oversimplified version of what I'm trying to do, we want to get a user info and their favorite bands. If I did it in one call, I could do:
SELECT userName, userAge, userGender, bandName FROM users NATURAL JOIN userBands NATURAL JOIN bands WHERE userID = 47
That will return to me a list of all the bands that user likes, but with every single one it will return the user name, age, and gender.
I could do two calls…
SELECT userName, userAge, userGender FROM users WHERE userID = 47
SELECT bandName FROM userBands NATURAL JOIN bands WHERE userID = 47
where the first just returns the basic info once, and the second just returns the list of bands. Is there any best practice with this? Is there another way to approach this that I'm not realizing? And what if (as it will be in my my real-world case) that it's not just two calls to separate the data, but 4 or 5? So it's a lot more calls, but it's also a lot more extraneous data returned to do it in one call.
I'm using PHP PDO if that makes a difference in the answer.
There is another answer, which is to combine the bands into a single column using group_concat():
SELECT u.userName, u.userAge, u.userGender, group_concat(b.bandName)
FROM users u NATURAL JOIN
userBands ub NATURAL JOIN
bands b
WHERE userID = 47
GROUP BY u.userName, u.userAge, u.userGender;
This gives you one row per user with the list of bands.
By the way, I would discourage you from using NATURAL JOIN. The query then depends on the metadata for the join keys -- and a small change to the table structures could have a big impact on lots of queries. Use either an explicit on clause or a using clause.
This makes sense when sending data across a network to a remote user. Why waste bandwidth sending the same data over and over again? For that purpose, a good method is to place the the data in some sort of XML structure.
<users>
<user>
<userdata1>...</userdata1>
<...>...</...>
<bands>
<band>...</band>
<band>...</band>
...
</bands>
</user>
</users>
No duplicated data is sent. Whether you form this structure from a single result set with the duplicated data or from the two result sets is up to you and your team. Play around with both and decide which is best under your specific situation.

SQL - how to get out chained data?

I have 4 tables which were auto generated for me:
User
Challenge
Exercise
Challenge_Exercise
One User may have many Challenges, and one Challenge will have many Exercises.
What I noticed is that the Challenge table has a reference to it's parent User (called user_id) but Exercise do not have a reference in it's table to Challenge; their relation is stored in Challenge_Exercise as Challenge_id and exercise_id.
My question is, how would I take out every Exercise that is linked to a specific user? For instance User with id = 1?
SELECT *
FROM excerise,
challenge_excerise,
challenge
WHERE challenge.user_id = 1
AND challenge_excerise.challenge_id = challenge.id
AND challenge_excerise.exercise_id = excercise.id
What I'm doing here is a join, you could also explicitly do it with inner joins (google it if you wanna know more).
This table is needed because you have a many to many relationship, which means each challenge can have multiple exercises, but also each exercise can have multiple challenges. It's a standard to make an extra table then, so you don't have redundant data, this table is often called junction table.
If you want background just google it, there are tons of data to this topic.

Joining fact tables in an MDX query

I am building and Anaysis Services project using VS 2005. The goal is to analyse advertising campaigns.
I have a single cube with 2 fact tables
factCampaign: which contains details of what people interviewed thought of an advertising campaign
factDemographics: which contains demographic information of the people interviewed
These fact tables have a common dimension dimRespodent which refers to the actual person interviewed
I have 2 other dimensions (I’ve left non relevant dimensions)
dimQuestion: which contains the list of questions asked
dimAnswer: which contains the list of possible answers to each question
dimQuestion and dimAnswer are linked to factDemogrpahics but not factCampaign
I want to be able to run queries to return results of what people thought about campaign (from factCampaign) but using demographic criteria (using dimQuestion and dimAnswer)
For example the how many Males, aged 18-25 recalled a particular campaign
I am new to OLAP and Analysis Services (2005) so please excuse me if what I am asking is too basic.
I have tried the following options
Linking the to factTables in the datasource view using the common RespondentKey. Queries run and return results but the same result is returned regardless of the demographic criteria chosen, i.e. it is being ignored.
Creating a dimension from factDemographics. I have tried to connect dimAnswer to factCampaign in Dimension Usage tabe of the Cube Structure but with out success. Either the project just stalls when I try to deploy it or I get the following error (note the attribute hierarchy enabled is set to true)
Errors in the metadata manager. The 'Answer Key' intermediate granularity attribute of the 'Fact Demographics' measure group dimension does not have an attribute hierarchy enabled.
I would appreciate any help that anyone can offer. Let me know if you require more info and again apologies if this is a basic question
What you probably need is a many-to-many relationship. There is a whitepaper here which goes through a number of scenarios for m2m relationships including one specifically around surveys and questionaires.
For anyone who is interested the solution was to alter the dimRespondent to include the questions and answers. And in the Dimension Usage tab of the Cube design to set dimRespondent to have a Regular relationship to both fact tables.