How to get all the elements of an association? - associations

I have created a CDS views as follows:
define view YGAC_I_REQUEST_ROLE
with parameters
pm_req_id : grfn_guid,
#Consumption.defaultValue: 'ROL'
pm_item_type : grac_prov_item_type,
#Consumption.defaultValue: 'AP'
pm_approval : grac_approval_status
as select from YGAC_I_REQ_PROVISION_ITEM as provitem
association [1..1] to YGAC_I_ROLE as _Role on _Role.RoleId = provitem.ProvisionItemId
association [1..*] to YGAC_I_ROLE_RS as _Relation on _Relation.RoleId1 = provitem.ProvisionItemId
{
key ReqId,
key ReqIdItem,
Connector,
ProvisionItemId,
ActionType,
ValidFrom,
ValidTo,
_Role.RoleId,
_Role.RoleName,
_Role.RoleType,
_Role,
_Relation
}
where
ReqId = $parameters.pm_req_id
and ProvisionItemType = $parameters.pm_item_type
and ApprovalStatus = $parameters.pm_approval
Then I have consumed in ABAP:
SELECT
FROM ygac_i_request_role( pm_req_id = #lv_test,
pm_item_type = #lv_item_type,
pm_approval = #lv_approval
)
FIELDS reqid,
connector,
provisionitemid
INTO TABLE #DATA(lt_result).
How to get the list of _Relation according to selection above.

This is generally not possible like in ABAP SQL queries:
SELECT m~*, kt~*
FROM mara AS m
JOIN makt AS kt
...
This contradicts the whole idea of CDS associations, because they were created to join on-demand and to reduce redundant calls to database. Fetching all fields negates the whole idea of "lazy join".
However, there is another syntax in FROM clause which is enabled by path expressions that allows querying underlining associations both fully and by separate elements. Here is how
SELECT *
FROM ygac_i_request_role( pm_req_id = #lv_test )
\_Role AS role
INTO TABLE #DATA(lt_result).
This fetches all the fields of _Role association into internal table.
Note: remember, it is not possible to fetch all the published associations of current view simultaneously, only one path per query.
Possible workaround is to use JOIN
SELECT *
FROM ygac_i_request_role AS main
JOIN ygac_i_request_role
\_Role AS role
ON main~ProvisionItemId = role~RoleId
JOIN ygac_i_request_role
\_Relation AS relation
ON main~ProvisionItemId = relation~RoleId1
INTO TABLE #DATA(lt_table).
This creates deep-structured type with dedicated structure for every join association like this:
If you are not comfortable with such structure for you task, lt_table should be declared statically to put up all the fields in a flat way
TYPES BEGIN OF ty_table.
INCLUDE TYPE ygac_i_request_role.
INCLUDE TYPE ygac_i_role.
INCLUDE TYPE ygac_i_role_rs.
TYPES END OF ty_table.

Related

How do I sort by names or postcodes using a drop-down list to determine what to sort by?

Currently, I am trying to create some software on Progress OpenEdge that sorts by customer's names or account codes.
So essentially, a box will open up when the program runs, the user will select "Name" from the drop-down list, and the program will display all the names in the database from alphabetical order.
Or, they will pick "Account" from the drop-down list, and it will display all the account codes in numeric order. I have attached a picture of the program here:
And this is currently the code I am using to print the results:
However, I'm not sure what I need to add for the others. Would I need IF statements, such as:
OR IF [drop down list] = "Account" THEN or something like that?
Any help would be appreciated.
While you can perform a static order by with a convulted set of if statements, it is a lot cleaner with a dynamic query.
To expand on Tom and Stefan's answers, you can use a query. The ABL lets you create a lot of things with static constructs and use them as dynamic. I think that in this case, you want to so something like the below.
Note that you can do build the query string either using OPEN QUERY qry FOR EACH ... or QUERY qry:QUERY-PREPARE('FOR EACH ... ') ; both will work equally well.
What I think you'd want is
(a) having a static definition of the query (ie DEFINE QUERY) since the cost of adding the buffer(s) to the query is done at compile time, not run time, and
(b) accessing the buffer fields statically (ie slmast.name rather than b::name )
define query qry for slmast.
define variable wc as character no-undo.
if condition eq true then
wc = "WHERE kco = s-kco AND warecode = lv-warecode AND pcode = fi-pcode AND name = fi-name BY name".
else
wc = "WHERE TRUE".
/* alternate
if condition then
open query qry for each slmast no-lock WHERE kco = s-kco AND warecode = lv-warecode AND pcode = fi-pcode AND name = fi-name BY name.
else
open query qry for each slmast no-lock.
*/
query qry:query-prepare(wc).
open query qry.
query qry:get-first().
do while available slmast:
/* do stuff with the buffer */
{&OUT} slmast.name.
query qry:get-next().
end.
query qry:query-close().
Using static constructs as far as possible means that you have less cleanup code to write and the code becomes more readable (IMO).
There are multiple ways to loop through the query results: using DO WHILE NOT QUERY qry:QUERY-OFF-END works as well as AVAILABLE slmast or b:AVAILABLE (if using a purely dynamic query).
As Stefan says, a dynamic query is what you want. This might help get you started:
define variable wc as character no-undo.
define variable q as handle no-undo.
define variable b as handle no-undo.
/* run your UI to get selection criteria amd then
* create a WHERE clause as appropriate
*/
wc = "WHERE kco = s-kco AND warecode = lv-warecode AND pcode = fi-pcode AND name = fi-name BY name".
create buffer b for table "slmast".
create query q.
q:set-buffers( b ).
q:query-prepare( substitute( "FOR EACH slmast NO-LOCK &1", wc )).
q:query-open().
do while q:get-next():
display
b:buffer-field( "name" ):buffer-value
b:buffer-field( "acode" ):buffer-value
b:buffer-field( "pcode" ):buffer-value
b:buffer-field( "trunmtd" ):buffer-value
b:buffer-field( "turnytd" ):buffer-value
.
end.

Entity Framework using SQL Server datastore modelling a one-to-many relationship

Can I get some help diagnosing the following?
IQueryable<MyComponentsViewModel> vcvm = from v1 in pg.MyComponents
join item in Items on v1.Item equals item into vc
select new MyComponentsViewModel
{ Cost = item.Cost };
The following error appears:
Error CS1941: The type of one of the expressions in the join clause is
incorrect. Type inference failed in the call to 'GroupJoin'.
Is this error state occuring as item is not the same type as v1.Item?
The keys are shown below
ALTER TABLE [dbo].[MyComponents] WITH CHECK ADD CONSTRAINT
[FK_MyComponents_Items] FOREIGN KEY([ItemId])
REFERENCES [dbo].[Items] ([Itemid])
GO
ALTER TABLE [dbo].[MyComponents] CHECK CONSTRAINT [FK_MyComponents_Items]
I assume like, you need inner join and while you doing equals you need to use their matching column Ids to filter records like below
IQueryable<MyComponentsViewModel> vcvm =
from myComp in pg.MyComponents
join item in Items on myComp.ItemId equals item.Id
select new MyComponentsViewModel { Cost = item.Cost };
I was able to get it to work.
There was an ambiguity between an action named Items. Fully specify the namespace resolved this.
Additionally I had to add the statement "from subitem in vc.DefaultIfEmpty"
I'm still working to learn the full semantics of this
IQueryable<MyComponentsViewModel> vcvm =
from v1 in pg.MyComponents
join item in pg.Items on v1.Item equals item into vc
from subitem in vc.DefaultIfEmpty()
select new MyComponentsViewModel { Cost = subitem.Cost };

ASP.NET returns incorrect data from oData Controller

I have an ASP.NET Web API app using Oracle's Entity Framework driver. I have an entity defined for a view as follows:
CREATE OR REPLACE FORCE VIEW "PHASE_TWO"."EDIPRODUCT" ("ID", "STK_NUM", "TITLE", "ISBN", "UPC", "ITEMNO", "LONGFORMAT", "ABRIDGED", "WEB_TITLES_ID", "OCLC", "GENRE", "RELYEAR", "ORIG_REL", "LANG", "ORIG_STKNUM", "PUBLISHER", "PEOPLELIST", "SALES_ORG", "NOT_AVAIL") AS
SELECT sap_product.id,
sap_product.stk_num,
sap_product.longdesc AS title,
sap_product.isbn,
sap_product.upc,
sap_product.itemno,
sap_product.longformat,
sap_product.abridged,
mwt_product.web_titles_id,
mwt_product.oclc,
mwt_product.genre,
mwt_product.RELYEAR,
sap_product.orig_rel,
sap_product.lang,
sap_product.orig_stknum,
UPPER (publisher.name) publisher,
(SELECT LISTAGG (p.FULLNAME, ', ') WITHIN GROUP (
ORDER BY pp.rank) AS People
FROM people p
JOIN product_people pp
ON p.id = pp.peopleid
WHERE pp.stk_num = sap_product.stk_num
GROUP BY pp.STK_NUM
) PeopleList,
sppg.PRICING_TYPE as sales_org,
sap_product.not_avail
FROM sap_product
JOIN mwt_product ON sap_product.stk_num = mwt_product.stk_num
JOIN publisher ON mwt_product.publisherid = publisher.id
JOIN SAP_PRODUCT_PRICING_GROUP sppg on sppg.STK_NUM = mwt_product.stk_num and sppg.MARKED_FOR_DELETION = 0
WHERE mwt_product.WEB_PRODUCTS_ID > 0;
This view works as expected in SQL Developer. My getEDIPRODUCT function (yes, it's VB.NET) in my controller is as follows:
' GET: odata/EDIPRODUCTs
<EnableQuery>
Function GetEDIPRODUCT() As IQueryable(Of EDIPRODUCT)
Dim results As IQueryable
results = db.EDIPRODUCT
For Each _product In results
Console.Write(_product)
Next
Return results
End Function
I just added the for loop in order to inspect the results. What I see when I inspect the results is the same product record is returned for each row. The value for the ID is duplicate and the only other field that should have variant values (sppg.PRICING_TYPE as sales_org) also just repeats.
I have other views where this does not occur. The correct number of records are always returned, but the first record retrieved is always just repeated in each row of the result set. Any idea what could be going on here?
I never actually resolved this issue and am still interested in why this fails, but I rewrote the portion of the app that uses this view to use OData's $expand to retrieve the related data.

nature of SELECT query in MVC and LINQ TO SQL

i am bit confused by the nature and working of query , I tried to access database which contains each name more than once having same EMPid so when i accessed it in my DROP DOWN LIST then same repetition was in there too so i tried to remove repetition by putting DISTINCT in query but that didn't work but later i modified it another way and that worked but WHY THAT WORKED, I DON'T UNDERSTAND ?
QUERY THAT DIDN'T WORK
var names = (from n in DataContext.EmployeeAtds select n).Distinct();
QUERY THAT WORKED of which i don't know how ?
var names = (from n in DataContext.EmployeeAtds select new {n.EmplID, n.EmplName}).Distinct();
why 2nd worked exactly like i wanted (picking each name 1 time)
i'm using mvc 3 and linq to sql and i am newbie.
Both queries are different. I am explaining you both query in SQL that will help you in understanding both queries.
Your first query is:
var names = (from n in DataContext.EmployeeAtds select n).Distinct();
SQL:-
SELECT DISTINCT [t0].[EmplID], [t0].[EmplName], [t0].[Dept]
FROM [EmployeeAtd] AS [t0]
Your second query is:
(from n in EmployeeAtds select new {n.EmplID, n.EmplName}).Distinct()
SQL:-
SELECT DISTINCT [t0].[EmplID], [t0].[EmplName] FROM [EmployeeAtd] AS
[t0]
Now you can see SQL query for both queries. First query is showing that you are implementing Distinct on all columns of table but in second query you are implementing distinct only on required columns so it is giving you desired result.
As per Scott Allen's Explanation
var names = (from n in DataContext.EmployeeAtds select n).Distinct();
The docs for Distinct are clear – the method uses the default equality comparer to test for equality, and the default comparer sees 4 distinct object references. One way to get around this would be to use the overloaded version of Distinct that accepts a custom IEqualityComparer.
var names = (from n in DataContext.EmployeeAtds select new {n.EmplID, n.EmplName}).Distinct();
Turns out the C# compiler overrides Equals and GetHashCode for anonymous types. The implementation of the two overridden methods uses all the public properties on the type to compute an object's hash code and test for equality. If two objects of the same anonymous type have all the same values for their properties – the objects are equal. This is a safe strategy since anonymously typed objects are essentially immutable (all the properties are read-only).
Try this:
var names = DataContext.EmployeeAtds.Select(x => x.EmplName).Distinct().ToList();
Update:
var names = DataContext.EmployeeAtds
.GroupBy(x => x.EmplID)
.Select(g => new { EmplID = g.Key, EmplName = g.FirstOrDefault().EmplName })
.ToList();

Symfony2 / Doctrine multiple joins returns error

I'm building a system with messages that can be assigned to (one or more) users.
Table: Messages
id
text
Table: Assignments
id
remark
status
Table: Assignment_User
id
assignment_id
user_id
Table: Users
id
name
Now I want to retrieve all assigned messages for a specific user
$qb->select('DISTINCT m')
->from('MessageBundle:Assignment', 'a')
->join('MessageBundle:Message', 'm')
->join('MessageBundle:AssignmentUser', 'au')
->where('a.message = m')
->andWhere('au.assignment = a')
->andWhere('a.status = (:assigned)')
->setParameter('assigned', 'assigned')
->orderBy("mr.createdAt", "desc");
As soon as I add that second JOIN it throws an error...
Error: Expected Literal, got 'JOIN'
What would be the correct way to get all assigned messages for user X?
Doctrine should already understand how your tables relate through your entity mappings (if not, you might need to set those up first). Rather than trying to join a whole table and then specifying what the table is joined on (SQL-style), just join, for example, au.assignment as a.
At that point, you can also specify any additional conditions on your join, such as you'd want on a.status.
You might prefer to reorder the joins below depending on what associations you have set up in which directions (I don't know whether your Message has an assignments property, for example).
$qb->select('DISTINCT m')
->from('MessageBundle:AssignmentUser', 'au')
->innerJoin('au.assignment', 'a', 'WITH', 'a.status = (:assigned)')
->innerJoin('au.user', 'u')
->innerJoin('a.message', 'm')
->where('u.id = (:user_id)')
->setParameter('assigned', 'assigned')
->setParameter('user_id', $yourSpecificUserId)
->orderBy("m.createdAt", "desc");

Resources