Dynamics CRM 2011 Linq Left Outer Join - linq

I am trying to get all records from an entity that do not join to another entity.
This is what I am trying to do in SQL:
SELECT * from table1
LEFT join table2
ON table1.code = table2.code
WHERE table2.code IS NULL
It results in all table1 rows that did not join to table2.
I have it working with Linq when joining on one field, but I have contact records to join on firstname, dob, and number.
I have a "staging" entity that is imported to; a workflow processes the staging records and creates contacts if they are new.
The staging entity is pretty much a copy of the real entity.
var queryable = from staging in linq.mdo_staging_contactSet
join contact in linq.ContactSet
on staging.mdo_code equals contact.mdo_code
into contactGroup
from contact in contactGroup.DefaultIfEmpty()
// all staging records are selected, even if I put a where clause here
select new Contact
{
// import sequence number is set to null if the staging contact joined to the default contact, which has in id of null
ImportSequenceNumber = (contactContactId == null) ? new int?(subImportNo) : null,
/* other fields get populated */
};
return queryable // This is all staging Contacts, the below expressions product only the new Contacts
.AsEnumerable() // Cannot use the below query on IQuerable
.Where(contact => contact.ImportSequenceNumber != null); // ImportSequenceNumber is null for existing Contacts, and not null for new Contacts
Can I do the same thing using method syntax?
Can I do the above and join on multiple fields?
The alternatives I found were worse and involved using newRecords.Except(existingRecords), but with IEnumerables; is there a better way?

You can do the same thing with method calls, but some tend to find it harder to read since there are some LAMBDA expressions in the middle. Here is an example that shows how the two are basically the same.
I've seen others ask this same questions and it boils down to choice by the developer. I personally like the LINQ approach since I also write a bunch of SQL and I can read the code easier.

Related

apex shuttle not displayed

I'm developing a functionality that fills a collection, using two select lists, with a shuttle . I'm not getting the correct way to display the name, and recover its correspondent id, on shuttle's select item at right side, when I change an specific list, after repeated interventions. I developed a procedure to update, insert or delete the selected items on shuttle, and I suppose its works well after several tests on sql commands.
My test case uses three tables : contracts, worksations and employees. I intend to insert the employees of any workstation of a given contract to another contract. That new contract, whose will receive the employees, must have its owns workstations, previously inserted. The general structure of the tables is:
contracts : pk_contract, number_contract, company_name etc...
workstations : pk_workstation, fk_contract, description etc...
employees: pk_employee, fk_workstation, employee_name, employee_sex etc...
I created an app to demonstrate it: https://apex.oracle.com/pls/apex/f?p=43921​
USER: test,
PASSWORD : TEST
Page 2- Migrate Employees - has three major regions, and 2 another to display information inserted :
Contracts with a select list, that must choice, preferrable the contract 070/2016, witch have workstation inserted, and a display field with static value, representing a contract, with workstations and employees attached.
workstations: with two select list, the gordian knot, I suppose : old workstations, related original contract's wokstations - related to display field, at contract's region; and new workstation, related to new contract selected above.
Employees: with a shuttle that represents, at left side, a bunch of employees, of a given original workstation and button witch do nothing.
Two more regions representing a classical report, only to inform the inserted, updated or deleted employees, from shuttles right size, using a collection. I made two report's regions because don't know make joins with collections and tables. This collection have the employee id, old_workstation id, new_workstation id, and new contract id, for further migration, not yet implemented.
I'm submiting almost all the fields, for recover its values on apex session's state.
Apparently, the collection works well, with a procedure, but when I choose again the original worksation the shuttle don't display, on right size, the previous inserted employee on that workstation. I configured on my list of values to not include extra values, when i change it, it shows the employee's ids, not their names, regards to source type listed here below:
SELECT e.pk_employee
FROM tb_employee e
INNER JOIN tb_workstation pt
ON pt.pk_workstation = e.fk_workstation
WHERE pt.typo IS NOT NULL
AND e.fk_workstation = p2_original_workstation
AND e.pk_employee IN (
SELECT to_number(c001) AS id_employee
FROM apex_collections
WHERE collection_name = 'WORKSTATION_EMPLOYEES');
My shutlle's lov has this rationale:
SELECT e.name, e.pk_employee
FROM tb_employee e
INNER JOIN tb_workstation pt
ON pt.pk_workstation = e.fk_workstation
WHERE pt.typo IS NOT NULL
AND e.fk_workstation = p2_original_workstation
AND e.pk_employee NOT IN (
SELECT to_number(c001) AS id_employee
FROM apex_collections
WHERE collection_name = 'WORKSTATION_EMPLOYEES');
Could anyone help me on this issue?
Regards!
I consider this a regular and fine solution. The shuttle permits recover entire registers on left side, changing it to another original workstation. After have removed the last "and" subclause, added at SHUTTLE an source sql query - return colon separated value, like this:
select e.pk_employee from employee e
inner join workstations w on w.pk_workstation = e.fk_workstation
where w.typo is not null and e.fk_workstation = :P2_ORIGINAL_WORKSTATION and e.pk_employee in (
SELECT TO_NUMBER(c001) as id_employee FROM APEX_collections WHERE collection_name = 'WORKSTATION_EMPLOYEES' and c002 = :P2_NEW_WORKSTATION);
But for purpose of goood usabilty, and my better interpretation that what shuttle obect does, I reinsert the 'and' clause with an 'and' more, reflecting that on lov has all employees of a given original workstation, except by the employees also inserted and that ones became from another new workstations. This particularité avoids the user thinks that a employee previously attached, on new workstation needed to be inserted:
select e.name, e.employee from employees e
inner join workstations w on w.pk_workstation = e.workstation
where w.typo is not null and e.worktation = :P2_ORIGINAL_WORKSTATION and e.pk_employee not in (
SELECT TO_NUMBER(c001) as id_employee FROM APEX_collections WHERE collection_name = 'WORKSTATION_EMPLOYEES' and c002 != :P2_NEW_WORKSTATION)
I could be consider use without these cited subclauses, using a disable jquery, in case of same original_wrkstion but at a diferent new, at left size, however its will be an improvement, and I need to understand the jqueries features. I consider this an basic and good, yet functional solution.

Active Record Join with most recent association object attribute

I have a Contact model which has many Notes. On one page of my app, I show several attributes of a table of contacts (name, email, latest note created_at).
For the note column, I'm trying to write a joins statement that grabs all contacts along with just their latest note (or even just the created_at of it
What I've come up with is incorrect as it limits and orders the contacts, not their notes:
current_user.contacts.joins(:notes).limit(1).order('created_at DESC')
If you just want the created_at value for the most recent note for each contact, you can first create a query to find the max value and then join with that query:
max_times = Note.group(:contact_id).select("contact_id, MAX(created_at) AS note_created_at").to_sql
current_user.contacts.select("contacts.*, note_created_at").joins("LEFT JOIN (#{max_times}) max_times ON contacts.id = max_times.contact_id")
If you want to work with the Note object for the most recent notes, one option would be to select the notes and group them by the contact_id. Then you can read them out of the hash as you work with each Contact.
max_times = Note.group(:contact_id).select("contact_id, MAX(created_at) AS note_created_at").to_sql
max_notes = Note.select("DISTINCT ON (notes.contact_id) notes.*").joins("INNER JOIN (#{max_times}) max_times ON notes.contact_id = max_times.contact_id AND notes.created_at = note_created_at").where(contact_id: current_user.contact_ids)
max_notes.group_by(&:contact_id)
This uses DISTINCT ON to drop dups in case two notes have exactly the same contact_id and created_at values. If you aren't using PostgreSQL you'll need another way to deal with dups.

Dynamics CRM 2011 - Filtering LINQ query with outer joins

I have a requirement to query for records in CRM that don't have a related entity of a certain type. Normally, I would do this with an Left Outer Join, then filter for all the rows that have NULLs in the right-hand side.
For example:
var query = from c in orgContext.CreateQuery<Contact>()
join aj in orgContext.CreateQuery<Account>()
on c.ContactId equals aj.PrimaryContactId.Id
into wonk
from a in wonk.DefaultIfEmpty()
where a.Name == null
select new Contact
{
FirstName = c.FirstName,
LastName = c.LastName,
};
This should return me any Contats that are not the Primary Contact of an account. However, this query ends up returning all contacts...! When you look at the SQL that gets generated in SQL Profiler it comes out like this:
SELECT cnt.FirstName, cnt.LastName
FROM Contact as cnt
LEFT OUTER JOIN Account AS acct
ON cnt.ContactId = acct.PrimaryContactId AND acct.Name is NULL
So, I get the Left Join OK, but the filter is on the Join clause, and not in a WHERE clause.and not as it should, like this:
SELECT cnt.FirstName, cnt.LastName
FROM Contact as cnt
LEFT OUTER JOIN Account AS acct
ON cnt.ContactId = acct.PrimaryContactId
WHERE acct.Name is NULL
Clearly, the results from this query are very different! Is there a way to get the query on CRM to generate the correct SQL?
Is this a limitation of the underlying FetchXML request?
Unfortunately, this is a limitation of CRM's LINQ and FetchXML implementations. This page from the SDK states outer joins are not supported:
http://technet.microsoft.com/en-us/library/gg328328.aspx
And while I can't find an official document, there are a lot of results out there for people mentioning FetchXML does not support left outer joins, for example:
http://gtcrm.wordpress.com/2011/03/24/fetch-xml-reports-for-crm-2011-online/
Try this:
var query = from c in orgContext.CreateQuery<Contact>()
where orgContext.CreateQuery<Account>().All(aj => c.ContactId != aj.PrimaryContactId.Id)
select new Contact
{
FirstName = c.FirstName,
LastName = c.LastName,
};
If you don't need to update the entity (e.g. to process all the corresponding validation rules and workflow steps), you can write less-ugly and more efficient queries by hitting the SQL Server directly.
Per CRM's pattern, the views take care of most of the common joins for you. For instance, the dbo.ContactBase and dbo.ContactExtensionBase tables are already joined for you in the view dbo.Contact. The AccountName is already there (called AccountIdName for some bizarre reason, but at least it's there).

Entity Framework 4 generated queries are joining full tables

I have two entities: Master and Details.
When I query them, the resulting query to database is:
SELECT [Extent2]."needed columns listed here", [Extent1]."needed columns listed here"
FROM (SELECT * [Details]."all columns listed here"...
FROM [dbo].[Details] AS [Details]) AS [Extent1]
LEFT OUTER JOIN [dbo].[Master] AS [Extent2] ON [Extent1].[key] = [Extent2].[key]
WHERE [Extent1].[filterColumn] = #p__linq__0
My question is: why not the filter is in the inner query? How can I get this query? I've tried a lot of EF and Linq expressions.
What I need is something like:
SELECT <anything needed>
FROM Master LEFT JOIN Details ON Master.key = Details.Key
WHERE filterColumn = #param
I'm having a full sequential scan in both tables, and in my production environment, I have milions of rows in each table.
Thanks a lot !!
Sometimes The entity Framework does not produce the best query. You can do a few of the following to optimize.
Modify the linq statement (test with
LINQPad)
Create a stored proc and map the stored proc to return an entity
Create a view that handles the join and map the view to a new
entity

How can I use a compound condition in a join in Linq?

Let's say I have a Customer table which has a PrimaryContactId field and a SecondaryContactId field. Both of these are foreign keys that reference the Contact table. For any given customer, either one or two contacts may be stored. In other words, PrimaryContactId can never be NULL, but SecondaryContactId can be NULL.
If I drop my Customer and Contact tables onto the "Linq to SQL Classes" design surface, the class builder will spot the two FK relationships from the Customer table to the Contact table, and so the generated Customer class will have a Contact field and a Contact1 field (which I can rename to PrimaryContact and SecondaryContact to avoid confusion).
Now suppose that I want to get details of all the contacts for a given set of customers.
If there was always exactly one contact then I could write something like:
from customer in customers
join contact in contacts on customer.PrimaryContactId equals contact.id
select ...
...which would be translated into something like:
SELECT ...
FROM Customer
INNER JOIN Contact
ON Customer.FirstSalesPersonId = Contact.id
But, because I want to join on both the contact fields, I want the SQL to look something like:
SELECT ...
FROM Customer
INNER JOIN Contact
ON Customer.FirstSalesPersonId = Contact.id OR Customer.SecondSalesPersonId = Contact.id
How can I write a Linq expression to do that?
It's rarely correct to use join in LINQ to SQL.
Since you want contacts, why not start your selection there? Presuming the association between Customer and Contact is two-way, you should be able to write something like:
IEnumerable<Guid> customerIds = // ...
var q = from contact in Context.Contacts
where customerIds.Contains(contact.Customer.Id)
select contact;
Use anonymous classes. EG
new { A.Foo, B.Bar } equals new { Foo = B.Baz, Bar = C.Ork }

Resources