Advanced queries in linq to entities or stored procedures - entity-framework

I'm currently building an application using entity framework. Normally I would use a stored procedure to get specific data from my database but now i'm experimenting with Entity Framework.
Now i'm facing a small challenge. I have an incident log table with a primary key, an incident id, and some data fields. I need to get all the newest rows for each incident. The sql is quite easy:
select * from incidentLog t
join (select incidentId,max(id) as id from incidentLog group by incidentId) tmp on t.id=tmp.id
How can I convert this to linq to entity?
Can I do it in one operation at all or should I use a stored procedure instead?

This should do the trick:
var query =
(from i in context.IncidentLogs
group i by i.IncidentId into g
let maxID = g.Max(i => i.id)
select g.Where(i => i.id == maxID)).ToList();

Related

Strategy for selecting records for DataSet

In Most common cases, we have two tables (& more) in DB termed as master (e.g. SalesOrderHeader) & chirld (e.g. SalesOrderDetail).
We can read records from DB by one Select with INNER JOIN and additional constaints WHERE for lessen volume data for loading from DB (using "Addater.Fill(DataSet)")
#"SELECT d.SalesOrderID, d.SalesOrderDetailID, d.OrderQty,
d.ProductID, d.UnitPrice
FROM Sales.SalesOrderDetail d
INNER JOIN Sales.SalesOrderHeader h
ON d.SalesOrderID = h.SalesOrderID
WHERE DATEPART(YEAR, OrderDate) = #year;"
Did I understand right, in this case we receive one table in DataSet, w/o primary and foreign keys, and w/o possibility to set constraint between master and child tables.
This Dataset can be useful only for different queries regarding columns and record which exist in DataSet?
We can't using DbCommandBuilder for creating SQLCommands for Insert, Update, Delete based on the SelectCommand which was used for filling DataSet? And simply to Update data in these table in DB?
If we want to organize the local data moddification in tables by using the disconnect layer of ADO.NET, we must populate DataSet by two Select
"SELECT *
FROM Sales.SalesOrderHeader;"
"SELECT *
FROM Sales.SalesOrderDetail;"
After that we must create the primary keys for both table, and set constraint between master and child table. Create by DbCommandBuilder SQLCommands for Insert, Update, Delete.
In that case we will have possibility to any modification data in these tables remotely and after Update records in DB (using "Addater.Update(DataSet)").
If we will use one SelectCommand to load data in two tables in DataSet, can we use that SelectCommand for DbCommandBuilder for creating other SQLCommands for "Update" and Update all tables in DataSet by one "Addater.Update(DataSet)" or we must create separate Addapter for Update every table?
If I for economy resources will load only part of records (see below) from table (e.g. SalesOrderDetail). Do I right understand, that in this case, I can have a possible problems, when I will send new records to DB (by Update), because news records can conflict with existen in DB by primary key (some records which have other value in OrderDate field)?
"SELECT *
FROM Sales.SalesOrderDetail
WHERE DATEPART(YEAR, OrderDate) = #year;"
There is nothing preventing you from writing your own Insert, Update and Delete commands for your first select statement with the join. Of course you will have to determine a way to assure that the foreign keys exist.
Insert Into SalesOrderDetail (SalesOrderID, OrderQty, ProductID, UnitPrice) Values ( #SalesOrderID, #OrderQty, #ProductID, #UnitPrice);
Update SalesOrderDetail Set OrderQty = #OrderQty Where SalesOrderDetailID = #ID;
Delete From SalesOrderDetail Where SalesOrderDetailID = #ID;
You would execute these with ADO.net commands instead of using the adapter. I wrote the sample code in vb.net but I am sure it is easy to change to C# if you prefer.
Private Sub UpdateQuantity(Quant As Integer, DetailID As Integer)
Using cn As New SqlConnection("Your connection string"),
cmd As New SqlCommand("Update SalesOrderDetail Set OrderQty = #OrderQty Where SalesOrderDetailID = #ID;")
cmd.Parameters.Add("#OrderQty", SqlDbType.Int).Value = Quant
cmd.Parameters.Add("#ID", SqlDbType.Int).Value = DetailID
cn.Open()
cmd.ExecuteNonQuery()
End Using
End Sub

How to avoid a JPQL query to execute to many SQL query?

I have two objects Client and Procedure. A client can have a list of Procedure and a procedure can only be linked to one Client.
In the Client, I have a transient attribute nbProcedure, that exists to store the number of procedure fo a Client.
I use Spring data JPA, with the following Query :
#Query("SELECT new Client(c, count(p))
FROM Procedure p
INNER JOIN p.client c
WHERE c.userId = ?1
GROUP by c.id")
fun getByUserIdOrderByNameWithNbProcedure(userId: String): List<Client>
I see in the log that this query is executed but after that there is one query executed per row to select all properties of the Client.
How can I avoid all the queries by row and only keep one query executed?
I think, I miss a configuration or a misuse of new Client(c, count(p))
This can be because of not initialized lazy fetch associations in the entity. Can use JOIN FETCH instead of JOIN to avoid multiple queries.

IN clause with large list in OpenJpa causing too complex statement

I have to create a named query where I need to group my results by some fields and also using an IN clause to limit my results.
The it looks something like this
SELECT new MyDTO(e.objID) FROM Entity e WHERE e.objId IN (:listOfIDs) GROUP BY e.attr1, e.attr2
I'm using OpenJPA and IBM DB2. In some cases my List of IDs can be very large (>80.000 IDs) and then the generated SQL statement becomes too complex for DB2, because the final generated statement prints out all IDs, like this:
SELECT new MyDTO(e.objID) FROM Entity e WHERE e.objId IN (1,2,3,4,5,6,7,...) GROUP BY e.attr1, e.attr2
Is there any good way to handle this kind of query? A possible Workaround would be to write the IDs in a temporary table and then using the IN clause on this table.
You should put all of the values in a table and rewrite the query as a join. This will not only solve your query problem, it should be more efficient as well.
declare global temporary table ids (
objId int
) with replace on commit preserve rows;
--If this statement is too long, use a couple of insert statements.
insert into session.ids values
(1,2,3,4,....);
select new mydto(e.objID)
from entity e
join session.ids i on
e.objId = i.objId
group by e.attr1, e.attr2;

zf many to many relationship, how to find values stored in intermediary table without a second lookup

I have a many to many relationship between two tables which is represented with an intermediary table. I am using the ZF1 table relationships methodology to model the database in my application and this works fine. One thing I am struggling with is to pull data from the intermediary table when performing a many to many lookup. For exmaple:
productsTable
product_id,
product_name
customerTable
customer_id,
customer_name
salesTable
customer_id,
product_id,
date_of_sale
In this case where the sales table is the intermediary table and the many to many relationship is between customers and products. I add the referenceMap to the sales table model for products and customers and the dependent table "sales" to the product table model and the customer table model.
I can then successfully use the following code to get all the products for a given customer (or vice-versa).
$productTable = new productsTable();
$product = $productTable->find(1)->current();
$customers = $product->findManyToManyRowset('customerTable','salesTable');
But it does not include the "date_of_sale" value in the rowset returned. Is there a way of including the values from the intermediary table without doint a separate database lookup. Ican't see anything in the zf docs.
Any help would be cool.
I hope to eventually replace the zend_table with a datamapper implementation as it seems highly inefficient in terms of the number of db queries it executes which could be hugely reduced with slightly more complex SQL join queries rather than multiple simple selects but for now I'm stuck with this.
Thanks.
You can use JOIN queries in your code to make it in one call. From what I understand, you want to build this query
SELECT p.*, c.*, s.date_of_sale FROM sales AS s
INNER JOIN products AS p ON p.product_id = s.product_id
INNER JOIN customers AS c ON c.customer_id = s.customer_id
WHERE p.product_id = '1';
To achieve this, you can refer to Complex SQL query into Zend to see how I translate it to a Zend_Db Query or just use something like
$select = $this->select()->setIntegrityCheck(false);
$select->from(array('s'=>'sales'), array('date_of_sale'));
$select->join(array('p'=>'products'), 'p.product_id = s.product_id', '*');
$select->join(array('c'=>'customers'), 'c.customer_id = s.customer_id', '*');
$select->where('p.product_id = '.$productId);
Obviously, it would be better to quoteInto the where clause but I'm just too lazy.
Hope this helps !

how to query a many to many for the absence of records using EF5

Assuming a pure many to many where the primary key is the composite of two foreign keys. For example "Employee Privileges" in Northwind has EmployeeID and PrivilegeID. This m2m obviously relates the Employees table to the Privilege table.
I need to write a query with EF that returns all Employees that are NOT associated with a specified PrivilegeID, regardless of whether or not it is related to other PrivilegeIDs.
I have an EF query that works for Sql Server using Except() but it doesn't work for Oracle.
If I were writing the query in t-sql, I would write a query that filters Employee Privileges with the required Privileges "see EmpPrivQuery below". I would then RIGHT join EmpPrivQuery to Employee as seen in EmployeesMissingPrivileges to complete the filter.
We currently have Employee and Privilege models with relationship properties to create the many to many table. The project uses EF code first to create the schema.
Can this be done with EF join syntax? Can it be done without creating an Entity model for the EmployeePrivilege table?
EmpPrivQuery:
SELECT [Employee Privileges].[Employee ID], [Employee Privileges].[Privilege ID]
FROM [Privileges] INNER JOIN [Employee Privileges] ON Privileges.[Privilege ID] = [Employee Privileges].[Privilege ID]
WHERE (((Privileges.[Privilege Name])="P3"));
EmployeesMissingPrivileges:
SELECT EmpPrivQuery.[Employee ID], Employees.*
FROM Employees LEFT JOIN EmpPrivQuery ON Employees.ID = EmpPrivQuery.[Employee ID]
WHERE (((EmpPrivQuery.[Employee ID]) Is Null));
This block was in original post but clouds the question. Retained for comment context.
I'm using Devart dotConnect as as the Oracle data provider.
The error that Oracle is throwing is ORA-00904: "Extent1"."EmployeeID": invalid identifier. In adapting the code base that was written for sql server to work against oracle, this is a common error that I've needed to resolve. In most cases I have been able to resolve it by re-writing the query to move relationships to other tables out of the WHERE predicate (where it is very easy to be dynamic) up into the main body of the query using SelectMany(). This tends to flatten the query being sent to the database server and Oracle appears to require that.
You can use EF to create basically the same queries posted in the question. I started by creating a poco model EmployeePrivilege with properties: int PrivilegeID & int EmployeeID. I did not add this to the DbContext.
var EmpPrivQuery = ctx.Privileges
.Where(p => p.PrivilegeName == "P3")
.SelectMany(p => p.Employees, (p, e) => new EmployeePrivilege{EmployeeID = e.EmployeeID, PrivilegeID = p.PrivilegeID}
.Distinct();
var employeesMissingPrivilege = from e in Employees
join epq in EmpPrivQuery
on e.EmployeeID equals epq.EmployeeID
into jointable
where jointable.Count()==0
select e;
I just realized you can also get the same result without creating the poco EmployeePrivilege as follows:
var EmpPrivQuery = ctx.Privileges
.Where(p => p.PrivilegeName == "P3")
.SelectMany(p => p.Employees.Select(e => e.EmployeeID)
.Distinct();
var employeesMissingPrivilege = from e in Employees
join epq in EmpPrivQuery
on e.EmployeeID equals epq
into jointable
where jointable.Count()==0
select e;
Both of these EF queries return Employees missing specified privileges against both Sql Server and Oracle (using Devart's dotConnect for Oracle).
Many posts that I read referred to using DefaultIfEmpty() to achieve a left outer join. The queries above work, however, please post if there is a better way to this result using DefaultIfEmpty().