Convert SQL Query to LINQ-to-SQL - sql

I need help converting SQL query to LINQ to SQL
select top 5 customer_id, customer_name, product_id
from Customer
Join Product on product_id = product_id
where (customer_active = 'TRUE')
order by checksum(newid())
How can I do that in LINQ to SQL. Thanks

This was solved. Thanks to 'CodeNotFound' for this answer
https://stackoverflow.com/a/43850748/1655774
db.Customer.Where(p => p.customer_active == true).Select(p => new CustomerViewModel
{
Customer_id= p.customer_id,
Customer_name = p.customer_name,
Product_id = p.Product.product_id
}).OrderBy(c => SqlFunctions.Checksum(Guid.NewGuid())).Take(5).ToList();

try this code
( from p in Customer
join q in Product on p.product_id equals q.product_id
join q in Product on p.product_id equals q.product_id
where customer_active ==true select new
{
customer_id=p.customer_id,
customer_name=p.customer_name,
product_id=q.product_id
}).OrderBy(c => SqlFunctions.Checksum(Guid.NewGuid())).Take(5).ToList();

you should use this way to remove Boolean condition and reduce code
if you need to check bool condition in Ef
1.For True Condition
db.Customer.Where(p => p.customer_active).select(m=>m).tolist();
1.For False Condition
db.Customer.Where(p => !p.customer_active).select(m=>m).tolist();
just for suggestion

Related

How to do Subquery for same Table in Yii2 Eloquent

how to create this query using model in yii2
select *,p1.plan_id from product p1
where id in (select max(p2.id) from product p2 where p2.plan_id = p1.plan_id)
form the following table Product
id product_name plan_id
-------------------------------
1 bottle 1
2 book 2
3 book 2
4 bottle 1
5 notbook 3
You could refactor you query using an inner join eg:
$sql = "select *
from product p1
inner join (
select plan_id, max(id) max_id
from product
group by plain_id
) t on t.plan_id = p1.plan_id and p1.id = t.max_id";
and in some case could be useful findBySql, assuming your model is name Product
$models = Product::findBySql($sql)->all();
This should be the exact query you would do, but it won't work for MySQL:
// select *, p1.plan_id from product p1
// where id in (select max(p2.id) from product p2 where p2.plan_id = p1.plan_id)
$subquery = (new \yii\db\Query)->select('[[p2]].[[id]]')
->from(['p2' => 'product'])
->where('[[p2]].[[plan_id]] = [[p1]].[[plan_id]]')
->orderBy('[[p2]].[[id]] DESC')->limit(1); // equiv to max(p2.id)
$query = (new \yii\db\Query)->from(['p1' => 'product'])
->where(['p1.id' => $subquery])
->one();
// PS: Yii will quote fields wrapped in `[[]]` double square-brackets.
Using joins
So, you should use innerJoin (or other variants) to achieve the same result working for any database:
$query = (new \yii\db\Query)->from(['p1' => 'product'])
->innerJoin(['product p2'], '[[p2]].[[plan_id]] = [[p1]].[[plan_id]]')
->orderBy('[[p2]].[[id]] DESC')->limit(1); // equiv to max(p2.id)
->one();

I need to do a sub-select in DQL

I have this query:
$query = $this->createQueryBuilder('l')
->select('l')
->leftJoin('l.processedLeads', 'pl')
->where('pl.company <> :company')
->andWhere('pl.company IS NULL')
->setParameters(array('company' => $company))
->getQuery();
But i need it formed like the following:
SELECT * FROM leads WHERE NOT IN
( SELECT * FROM processed_leads WHERE lead = :lead AND company = :company)
Can i do a sub-select in a join where 2 parameters of the join (lead_id and company) do not exist?
As in, only select the leads that do not exist in processedLeads with the specific company?
I'm not familiar with the QueryBuilder, but I would recommend something like this in DQL:
$query = $em->createQuery('SELECT l FROM Project\model\leads l WHERE l NOT IN
(SELECT p.lead FROM Project\model\processed_leads p WHERE p.lead = ? AND company = ?)')
->setParameters(array($lead, $company))
->getResult;
Hopefully, this helps you. I would use the NOT INstatement. In case the sub query returns a null value, there is just no lead in the processed_leads for this company and you'll get all leads - which should be ok in this case.

LINQ vs. SQL Joins And Groupings (w/ Specific Case)

In SQL, when you do a bunch of joins, it treats all of the joined objects as one "super-object" to be selected from. This remains the case when you group by a particular column, as long as you include anything you select in the grouping (unless it is produced by the grouping, such as summing a bunch of int columns).
In LINQ, you can similarly do a bunch of joins in a row, and select from them. However, when you perform a grouping, it behaves differently. The syntax in query-style LINQ only allows for grouping a single table (i.e., one of your joins), discarding the others.
For an example case suppose we have a few tables:
Request
-------
int ID (PK)
datetime Created
int StatusID (FK)
Item
----
int ID (PK)
string Name
RequestItem
-----------
int ID (PK)
int ItemID (FK)
int RequestID (FK)
int Quantity
Inventory
---------
int ID (PK)
int ItemID (FK)
int Quantity
LU_Status
---------
int ID (PK)
string Description
In our example, LU_Status has three values in the database:
1 - New
2 - Approved
3 - Completed
This is a simplified version of the actual situation that lead me to this question. Given this schema, the need is to produce a report that shows the number of requested items (status not "Completed"), approved items (status "Approved"), distributed items (status "Completed"), and the number of items in stock (from Inventory), all grouped by the item. If this is a bit vague take a look at the SQL or let me know and I'll try to make it clearer.
In SQL I might do this:
select i.Name,
Requested = sum(ri.Quantity),
Approved = sum(case when r.StatusID = 2 then ri.Quantity else 0 end)
Distributed = sum(case when r.StatusID = 3 then ri.Quantity else 0 end)
Storage = sum(Storage)
from RequestItem as ri
inner join Request as r on r.ID = ri.RequestID
inner join Item as i on i.ID = ri.ItemID
inner join (select ItemID, Storage = sum(Quantity)
from Inventory
group by ItemID)
as inv on inv.ItemID = ri.ItemID
group by i.Name
This produces the desired result.
I began to rewrite this in LINQ, and got so far as:
var result = from ri in RequestItem
join r in Request on ri.RequestID equals r.ID
join i in Item on ri.ItemID equals i.ID
join x in (from inv in Inventory
group inv by inv.ItemID into g
select new { ItemID = g.Key, Storage = g.Sum(x => x.Quantity) })
on ri.ItemID equals x.ItemID
group...????
At this point everything had been going smoothly, but I realized that I couldn't simply group by i.Name like I did in SQL. In fact, there seemed to be no way to group all of the joined things together so that I could select the necessary things from them, so I was forced to stop there.. I understand how to use the group syntax in simpler situations (see the subquery), but if there's a way to do this sort of grouping in LINQ I'm not seeing it, and searching around here and elsewhere has not illuminated me.
Is this a shortcoming of LINQ, or am I missing something?
You can create an anonymous type in a grouping that contains all data you need:
var result = from ri in RequestItem
join r in Request on ri.RequestID equals r.ID
join i in Item on ri.ItemID equals i.ID
join x in (from inv in Inventory
group inv by inv.ItemID into g
select new { ItemID = g.Key, Storage = g.Sum(x => x.Quantity) })
on ri.ItemID equals x.ItemID
group new
{
i.Name,
r.StatusId,
ri.Quantity,
x.Storage,
}
by i.Name into grp
select new
{
grp.Key,
Requested = grp.Where(x => x.StatusID == 2).Sum(x => x.Quantity),
Distributed = grp.Where(x => x.StatusID == 3).Sum(x => x.Quantity),
Storage = grp.Sum(x => x.Storage)
}
(not tested, obviously, but it should be close).
The easiest way is to use group new { ... } by ... construct and include all the items from the joins that you need later inside the { ... }, like this
var query =
from ri in db.RequestItem
join r in db.Request on ri.RequestID equals r.ID
join i in db.Item on ri.ItemID equals i.ID
join x in (from inv in db.Inventory
group inv by inv.ItemID into g
select new { ItemID = g.Key, Storage = g.Sum(x => x.Quantity) }
) on ri.ItemID equals x.ItemID
group new { ri, r, i, x } by i.Name into g
select new
{
Name = g.Key,
Requested = g.Sum(e => e.ri.Quantity),
Approved = g.Sum(e => e.r.StatusID == 2 ? e.ri.Quantity : 0),
Distributed = g.Sum(e => e.r.StatusID == 3 ? e.ri.Quantity : 0),
Storage = g.Sum(e => e.x.Storage)
};

How to get certain SQL query results at the top of the list

I'm convinced this will make me slap myself when I see the answer but here goes...
Say I need a list of all products from a certain vendor but they need to order by a specific variable productType at the top and below it doesn't really matter but all products have to be in the list.
So basically
SELECT * FROM Products p WHERE p.VendorID = 1 AND p.ProductType = 'widget'
as the first rows to display. Then,
SELECT * FROM Products p WHERE p.VendorID = 1 AND p.ProductType <> 'widget'
underneath that.
I'm using LINQ if that helps any but I can't even get this in regular SQL queries
In Sql:
SELECT *
FROM Products p
WHERE p.VendorID = 1
ORDER BY CASE WHEN p.ProductType = 'widget' THEN 1 ElSE 2 END
And in Linq:
IQueryable<Product> query =
from p in dc.Products
where p.VendorID == 1
orderby p.ProductType == "widget" ? 1 : 2
select p;
One option is:
SELECT *,
CASE p.ProductType WHEN 'widget' THEN 0 ELSE 1 END AS ProductPriority
FROM Products p
WHERE p.VendorID = 1
ORDER BY ProductPriority
Create an extra field in the select part that indicates if they should be on top. For instance.
SELECT *, (p.ProductType = 'widget') AS thisfirst FROM Products p WHERE p.VendorID = 1 ORDER BY thisfirst
Just order by a case statement like so:
select * from products
order by case producttype when 'widget' then 1 end nulls last;
I guess you're using LINQ to SQL, so I think it would be better to do this using a LINQ query:
db.Products
.Where(p => p.VendorID == 1)
.OrderBy(p => p.ProductType == 'widget')
An alternate approach to Brian's would be a union query:
Select ...
From (
Select ..., 0 As SortOrder
from Products
Where VendorId = 1
And ProductType = 'Widget'
Union All
Select ..., 1
from Products
Where VendorId = 1
And ProductType <> 'Widget'
) As Z
Order By Z.SortOrder

LINQ 2 SQL: top 1 post per member ordered by created data

Okay so I have the sql to work this out as asked in the stackoverflow question here.
Does anyone know how to translate this to linq 2 sql? I'm guessing that the easiest way is to add a stored procedure, but I am curious to see if it can be linq-a-fied :P
select p.*
from post p join
(
select memberId, max(createdDate) as maxd
from post
group by memberId
) as p2 on p.memberid = p2.memberid and p.createdDate=p2.maxd
order by p.createdDate desc
I'm not totally sure this is the most efficient way to run this query (maybe it is, but I've got a feeling there's a better way. Haven't thought of it yet).
from
post in Nt_Post
join
memberdates in (
from
p_inner in Nt_Post
group
p_inner by p_inner.MemberId into grouped
select new {
MemberId = grouped.Key,
ActivationDate = grouped.Max(m => m.ActivationDate)
})
on
new { post.MemberId, post.ActivationDate }
equals
new { memberdates.MemberId, memberdates.ActivationDate }
orderby post.ActivationDate
select post;
Here is the query working within LinqPad on my database (not createdDate is actually activationDate and the Post table is Nt_Post. Thanks to Rex M for commming up with the solution :P
var q =
from
post in Nt_Post
join
memberdates in (
from
p_inner in Nt_Post
group
p_inner by p_inner.MemberId into grouped
select new {
MemberId = grouped.Key,
ActivationDate = grouped.Max(m => m.ActivationDate)
})
on
new { post.MemberId, post.ActivationDate }
equals
new { memberdates.MemberId, memberdates.ActivationDate }
orderby post.ActivationDate
select post;
q.Dump();
The sql generated is:
SELECT [t0].[Id], [t0].[Title], [t0].[Teaser], [t0].[Text], [t0].[ActivationDate], [t0].[CreatedDate], [t0].[LastModifiedDate], [t0].[IsActive], [t0].[Permalink], [t0].[MemberId], [t0].[HomePageVisibility], [t0].[Image], [t0].[ImageContentType], [t0].[HasNotifiedRTR]
FROM [nt_Post] AS [t0]
INNER JOIN (
SELECT MAX([t1].[ActivationDate]) AS [value], [t1].[MemberId]
FROM [nt_Post] AS [t1]
GROUP BY [t1].[MemberId]
) AS [t2] ON ([t0].[MemberId] = [t2].[MemberId]) AND ([t0].[ActivationDate] = [t2].[value])
ORDER BY [t0].[ActivationDate]