Describe relationship in maximo 7.5 with all detail including backend database process.
Suppose I create a relationship in maximo 7.5 asset management between owner_group and person_group "owner_group=:person_group"
then what is the actual meaning of this query. I am quite confused about it.
Can you please explain what is the process in database?
Maximo "relationships" are simply the "where clause" snippet of an SQL statement. The "child object" that you define is the table that "where clause" will be run against.
Maximo's backend always starts out with "select * from ". It will then append your child object's table name (which is almost always the object name itself). Then it appends " where " and your relationship text. In the end Maximo ends up running the query "select * from <object> where <relationship>" against the database. It will then load the results into an MBOSet of <object>s.
It isn't so much a join as it is a jump/switch to another table. Because it isn't a join, the query has no context of the object data you started from. To be able to use data from your originating object to filter down your destination object set, you can use field binds, those "colon fieldname" pieces of the where clause snippet. Maximo will do a find and replace on all of those binds it finds, replacing them with data from the appropriate fieldnames of your starting object. This is where the "parent object" setting of the relationship comes into play. That means Maximo will only run this query if you start from a record of a type equal to the "parent object" so that it knows where those field binds come from.
It might be worth mentioning that a relationship must start from a single object (MBO), not a set of objects (MBOSet) in order to allow this work. However, since a query returns one or more records in its result, you will always get a set of objects back (MBOSet) from the relationship. Also, because this is a find and replace like this before the query is run, Maximo does not take advantage of any database query plan caching feature; each query looks different to the database and is reparsed before being executed.
For example, if you want to create a relationship from Asset to Workorder to get all of the open workorders for an asset, you would do the following:
Create a relationship with a child object of WORKORDER because that is the record type you want to get back. You want all open workorders, so you start your relationship text with "status = 'OPEN'". You also only want workorders for the asset you are currently looking at, that means you want to use data from your current object so you need to use a field bind. In this case you want to filter the workorder records whose assetnum is the same your current record's assetnum, that means you add " and assetnum = :assetnum" to your relationship text/where clause (giving you "status = 'OPEN' and assetnum = :assetnum" for your total relationship string). The first assetnum without the colon means it is normal query text and will be run as is. The second assetnum with the colon tells Maximo this is a bind that needs to be replaced. The order doesn't matter, just the colon matters. Maximo will find the assetnum field of your current object (ASSET) and put the contents of that field (say assetnum "Blower0082") into the query in place of the ":assetnum". The end result of this defined relationship when run on this particular asset is that Maximo runs the following query against the database and loads any results into a WORKORDER MBOSet:
select * from WORKORDER where status = 'OPEN' and assetnum = 'Blower0082';
Your particular example of owner_group = :person_group doesn't make sense on its own; a parent object (so Maximo knows where to pull bind data from and when it is allowed to execute this query) and a child object (so that it knows what table it is selecting from) are needed too. I'm going to assume this has a parent object of PERSONGROUP and a child object of WORKORDER. Since "person_group" starts with a colon, Maximo is going to replace that string with the contents of the person_group field of your parent object, so the person_group field must exist on your parent object. Since owner_group does not have a colon in front of it, it is normal SQL syntax and therefore that field must exist on your child/destination object. This relationship would get you all workorders that are owned by the persongroup record you have loaded at the moment. Assuming that persongroup is "plumbers", it would run the following query and return all workorders currently owned by plumbers: select * from WORKORDER where owner_group = 'plumbers';
You can think of Maximo relationships in the same terms as a SQL join.
In System Configuration > Platform Configuration > Database Configuration -- every object can have relationship associated with a child object.
For example, if your Parent Object is Work Order, I will create a relationship called OWNERPERSON where the Child Object is PERSON.
The Where Clause personid = :owner means when the Child Object (PERSON) brings a result from the where clause personid = :owner then the related data can be displayed. :owner is the variable that is in WORKORDER.
If the :owner value in WORKORDER is Chip_Drapeau then you can display any from the PERSON Child Object where personid = Chip_Drepeau
Hope this helps.
Related
In Maximo, I want to retrieve the most recent status memo and add the WOSTATUS.MEMO field to Work Order Tracking Module via Application Designer. In the Work Order Tracking application, to see the same information, you'd go to an individual Work Order > Select Action > View > Work Order History.
You may have noticed the WOSTATUS relationship on the WORKORDER object and found that you can't control which of the many WOSTATUS records for this work order gets chosen for showing the memo. You'll need to make a copy of this relationship which specifically finds the latest record. To find that latest record, you could go for the WOSTATUS record with the CHANGEDATE matching the STATUSDATE on the work order or with the highest WOSTATUSID. Assuming you go for the former, because it doesn't require a subquery, you'll create a new relationship from WORKORDER to WOSTATUS called LASTSTATUS with a where clause like this:
wonum = :wonum and siteid = :siteid
and status = :status and changedate = :statusdate
You can then use the standard Relationship.Attribute syntax for the Attribute property of a Textbox in App Designer: LASTSTATUS.MEMO.
In case you were interested, here's the where clause you would use if you wanted to go for the WOSTATUSID instead:
wonum = :wonum and siteid = :siteid
and wostatusid = (
select max(wostatusid)
from wostatus
where wonum = :wonum and siteid = :siteid
)
(Some may argue the toss about whether the first line in the above query is needed. I would respond with the suggestion to test for performance / optimal execution plan in your database environment.)
I hope that helps.
I would like to create work order using escalation once the asset is moved to some other location (like REPAIR) using move/modify. I do understand that we can trigger CREATEWO however I am not sure on how to set the values on some fields in work order like worktype, workact , etc. Also I am unable to pick the correct record which has performed move modify ( unable to fetch the exact record using ASSETTRANS table).
Let me know if anyone has done this before, thanks in advance!
It sounds like you have an Escalation calling an Action that calls the AppAction CREATEWO. Assuming that's correct..
First, create a Relationship in DB Config between the ASSET object and WORKORDER that will find the most recent work order against this asset. You can look at the NEWWORKORDER relationships on WORKORDER and TICKET as an example. For reference, I will assume you name this relationship MYNEWWORKORDER.
Next, create some Actions against the ASSET object that use MYNEWWORKORDER.<ATTRIBUTENAME> in the Parameter/Attribute field, where <ATTRIBUTENAME> is the name of the attribute (e.g. WORKTYPE) you want to supply a value for in the Value field.
Once that is done, create an Action of type Action Group where CREATEWO is the first member and the Actions you just made are the succeeding members.
Finally, update the Escalation to call your new Action Group instead of the numbered one that the Escalation application created for you.
I am using Azure DocumentDB. I only have one collection with heterogenous document types. I am using a type parameter to distinguish between different document types. I am making use of their SQL-like query language to get documents as follows:
SELECT * FROM Collection c WHERE c.ID = 123
I am getting my connection information, including the Endpoint URI, AuthKey, Database name and Collection name, from a configuration file. It seems like I can use any value for "Collection c" and it essentially just becomes an alias for the whole collection. So what is the point of the FROM section of my query?
I think you already got it :)
FROM allows you to set an alias to refer to the collection in other clauses. This may make more sense to you when you include multiple references (e.g. using a JOIN to form a cross-product with nested array elements).
For example:
SELECT food.description, tag.name
FROM food
JOIN tag IN food.tags
WHERE food.id = "09052"
In the query above, we are using referencing both the collection as well as nested array elements within a projection.
You can try this query out on the query demo website.
I am trying to do exactly same thing as post in NSFetchResultsController + sectionNameKeyPath + section order, i.e. basically use 2 tables, let's say Categories <-->> Events. Category table consists of category field only, while Event consists of name, dateTimestamp.
I defined relationship 'category' in Events table and try to use that relationship as sectionNameKeyPath when creating fetchedResultsController:
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:#"category.category" cacheName:#"Root"];
Finally, I pre-populated Category table with some categories upon loading of the app (and verified with .dump that table is populated correctly)
Yet, I simulator fails on:
return [[self.fetchedResultsController sections] count];
I did extensive search and most people either suggest using one of the fields in the table as sectionNameKeyPath (this works!) or transient property (works too!) However, I just want to use relationship as it seems very logical to me in this case where events belong to some categories and there could be categories without events. Am I wrong in my assumption that relationship can be used as sectionNameKeyPath? The original link at the top of the question suggests it works, but guy does not know why or how. Documentation is very weak on what can be used as sectionNameKeyPath, so any help will be highly appreciated.
A relationship gets you a pointer to a managed object. It seems logical, though, that the sectionNameKeyPath parameter should be a key path that leads to a string, since NSFetchedResultsSectionInfo's name property is a string. The fetched results controller will follow that key path for each fetched object and group the objects into sections based on what they return for that key path, and it'll also use those strings as the names of their respective sections. You can't use a managed object for the name -- you have to use some string property of the managed object.
So, your Category entity must have an attribute that distinguishes one category from another, right? Use that as the key path and (as you've seen) everything will work out.
BTW, I think it's useful to try to get out of the database (rows/fields) mindset and try to think in object-oriented terms like entity and attribute. A big selling point of Core Data is that it provides an abstraction layer that hides the storage mechanism. Thinking in terms of tables is like thinking about blocks and sectors when you're reading or writing a file.
Caleb, thank you for your answer. I do believe my understanding was wrong to some degree. What I had was an entity Category and entity Event. Category has a string field 'category', thus 'category.category' path (first 'category' is relationship in the Event entity)
What I did not take in account, though, is that if there are no events, fetchresultscontroller cannot fetch anything (similar to 'left join')
What I wanted is to show categories even if there are no events. Relationship 'category' will not return anything in this case as there is nothing to return/sort/categorize.
What I had to do (wrong or right - not sure yet) is to treat [managed] object created from Category entity as a separate object in case there are no events and place in the table. When there is one event per category, I can switch to the original method of [automatic] showing events sorted by categories.
This is interesting issue of starting point (empty entities with relationships) where I feel core data is more confusing than traditional relationship database. I also believe that's why all books/articles/reports carefully stay away from this topic. In other words, I could not find analog of "left join" in core data. May be I am wrong because I am relatively new to all this. Below is the description of the entities:
Category <-->> Event
Category - parent
Category.category - attribute of type String
Category.event - relationship to Event entity
Event - child
Event.name - attribute of type String
Event.category - relationship to Category entity
Each event belongs to one category. Category may have multiple events.
Categories should be shown even if there are no events for this category.
I was trying to put Events under fetchresultscontroller. May be I should switch to Category first and then calculate cell based on category.event relationship, not the other way around - did not try that yet.
Is there a way to use form fields that does not correspond to database field for temporary processings?
I.e. I want to add:
temp fields item1, item2
database field sum
button with record hook that sets sum = item1 + item2
As far as I know it's simply not possible with ClearQuest.
I've tried to do something similar and was told by our IBM consultant that the only way is to create a DB field for all variables.
You can't attach data to form fields really - those are representations of the underlying data, not something scripts interact with directly.
Adding temporary data to the underlying record (entity) itself sounds unlikely as well. Perhaps it's possible to abuse the perl API and dynamically attach data to entity objects but I personally wouldn't try it, you're liable to lose your data at the whim of CQ then ;-)
That does not however mean it's impossible to have temporary data.
The best way seems to me to be using the session object, which is explicitly intended for that purpose.
From the helpfile:
IBM Rational ClearQuest supports the
use of sessionwide variables for
storing information. After you create
sessionwide variables, you can access
them through the current Session
object using functions or subroutines,
including hooks, that have access to
the Session object. When the current
session ends, all of the variables
associated with that Session object
are deleted. The session ends when the
user logs out or the final reference
to the Session object ceases to exist.
There's some helpful documentation on this subject in file:///C:/Program%20Files/Rational/ClearQuest/doc/help/cq_api/c_session_vars.htm (Presuming a default installation on a windows machine, of course.)
Translating the code example in there into what you seem to be wanting, first you store the data you have calculated inside the session object:
$session->SetNameValue("item1", $value1);
$session->SetNameValue("item2", $value2);
Then in your calculation hook you retrieve the stored values and set the value of that totals field like this:
my $item1 = GetNameValue("item1");
my $item2 = GetNameValue("item2");
my $sum = $item1 + $item2;
$entity->SetFieldValue("some_totals_record", $sum);
Adjust to taste of course ;-)
ClearQuest schema designers often include 'temporary' fields in their record types. They do this so they perform operations on hooks to generate another value.
For example, for the Notes fields, there is a 'temporary' Notes_entry field that the user types the most recent note into, and when the record is saved, the value is added to the Notes_Log field. The next time the record is edited the Notes_entry field is cleared so the user can type a new Notes_entry.