Rails find all Customers that have exactly 1 Order - activerecord

I have a data model where a Customer has many Orders. I now need to extract all of the customers that have only placed 1 order and I am scratching my head trying to figure out what to do.

Use this
Customer.joins(:orders).group(:id).having("count(orders.id) = 1")
this will create a query like:
SELECT "customers".* FROM "customers" INNER JOIN "orders" ON
"orders"."customer_id" = "customers"."id" GROUP BY id HAVING
count(orders.id) = 1
You would get all of the customers that have placed exactly 1 order.

To avoid ambiguous reference to the ID field, this would be used:
Customer.joins(:orders).group("customers.id").having("count(orders.id) = 1")
which would generate the following SQL:
SELECT "customers".* FROM "customers" INNER JOIN "orders" ON "orders"."customer_id" =
"customers"."id" GROUP BY customers.id HAVING count(orders.id) = 1

Consider Customer and Order are ActiveRecord classes and consider you have a code line
belongs_to :customer in your Order class definition. And also consider that Order table have a foreign_key or index column named customer_id do as below to get this Customer objects.
Customer.joins(:orders).where("count(orders.id) = 1")

Related

LINQ Left Outer Join only the first record

I'm working on a LINQ query that joins three tables. For the Orders and OrderInfo table I expect a single record in each table for a given order id. However for the ShipRate table, there could be 0, 1 or more records for a given order id. So for this table I am using a left outer join. The query shown below is working if 0 or 1 records exist in the ShipRate table, but for instances where the number of records is > 1, I need to select only the most recent ShipRate record. I tried to do this by replacing the line:
from shipRate in sr.DefaultIfEmpty()
with this:
from shipRate in sr.OrderByDescending(r => r.CreateDate).Take(1).DefaultIfEmpty()
but the query takes forever, as if it is loading the entire ShipRate table. Where have I gone wrong?
var query = (from order in db.Orders
join info in db.OrderInfo
on order.OrderId equals info.OrderId
join shipRate in db.ShipRate
on info.OrderId equals shipRate.OrderId
into sr
from shipRate in sr.DefaultIfEmpty()
where order.OrderId == orderId
select new
{
OrderId = order.OrderId,
OrderDetail = info.OrderDetail,
Carrier = shipRate.Carrier
}).SingleOrDefault();
With a proper model definition your query would be like:
var query = (from order in db.Orders
where order.OrderId == orderId
select new
{
OrderId = order.OrderId,
OrderDetail = order.OrderInfo.OrderDetail,
Carrier = order.OrderInfo.ShipRates.OrderBy(sr =>sr.CreateDate).FirstOrDefault()
}).SingleOrDefault();
I can't be sure though, because you didn't supply sample data and model.
Cetin Basoz's answer is a good one: ideally you'd set up your model in a way that allows you to use navigation properties. If you're using a model generated from your database schema, that typically means setting up foreign and primary keys properly.
If you can't do that, you should still be able to get a similar effect by writing SQL like this:
var query = (from order in db.Orders
where order.OrderId == orderId
let orderInfo = db.OrderInfo.FirstOrDefault(info => order.OrderId == info.OrderId)
let currentShipRate = db.ShipRate
.Where(shipRate => info.OrderId == shipRate.OrderId)
.OrderByDescending(shipRate => shipRate.CreateDate)
.FirstOrDefault()
select new
{
OrderId = order.OrderId,
OrderDetail = orderInfo.OrderDetail,
Carrier = currentShipRate.Carrier
}).SingleOrDefault();
However, LINQ to SQL isn't nearly as good at building advanced queries as Entity Framework, and the symptoms you're describing might be an indication that it's actually doing multiple database round-trips instead of a join. I'd recommend logging the query that you're producing (prior to the .SingleOrDefault()) either by calling .ToString() on the query or by executing your query in LINQPad and clicking on the SQL tab. That might give you a clue as to why the query is misbehaving.
There seems to be a one-to-one relation between Orders and OrderInfos: every Order has exactly one OrderInfo, and every OrderInfo is the info of exactly one Order, namely the Order that the foreign key OrderId refers to.
On the other hand, there seems to be a one-to-many relation between Orders and ShipRates. Every Order has zero or more ShipRates, every ShipRate is a ShipRate of exactly one Order, namely the Order that the foreign key OrderId refers to.
You want several properties of "Orders, each Order with its one and only OrderInfo and its zero or more ShipRates"
Whenever you have a one-to-many relation, and you want "items with their zero or more sub-items", like Schools with their Students, Customers with their Orders, or in your case: Orders with their ShipRates, consider to use one of the overloads of Queryable.GroupJoin
In the other direction: if you want an item with its one and only other item that the foreign key refers to, like Student with the School he attends, Order with the Customer who created the Order, or Order with its one and only OrderInfo, use Queryable.Join.
I mostly use the overload of GroupJoin that has a parameter resultSelector, so I can select exactly what properties I want.
int orderId = ...
var ordersWithShipRates = dbContext.Orders.GroupJoin(dbContext.ShipRates,
order => order.Id, // from every Order take the primary key
shipRate => shipRate.OrderId, // from every ShipRate take the foreign key to Order
// parameter resultSelector: from every Order, with its zero or more ShipRates
// make one new
(order, shipRatesOfThisOrder) => new
{
// Select the Order properties that you plan to use:
Id = order.Id,
Date = order.Date,
...
ShipRates = shipRatesOfThisOrder.Select(shipRate => new
{
// Select the ShipRate properties that you plan to use:
Id = shipRate.Id,
Value = shipRate.Value,
...
})
.ToList(),
// A simple join to get the one and only OrderInfo
OrderInfo = dbContext.OrderInfos.Where(orderInfo => orderInfo.Id == order.Id)
.Select(orderInfo => new
{
// Select the orderInfo properties that you plan to use
Name = orderInfo.Name,
...
})
.FirstOrDefault(),
});

ORA-00904: invalid column name but I am using the correct column name

Can someone see where I am going wrong in the below query? I am getting the error message that the GROUP BY column doesn't exist, but it clearly does as I see that column name in the output when I don't use the GROUP BY.
SELECT
(SELECT customer_address.post_code FROM customer_address WHERE customer_address.address_type = 0 AND customer_address.customer_no = orders.customer_no) postcode, SUM(orders.order_no) orders
FROM
orders, customer_address
WHERE
orders.delivery_date = '27-MAY-15'
GROUP BY
postcode;
The answer is: You cannot use an alias name in GROUP BY.
So:
GROUP BY (SELECT customer_address.post_code ...);
Or:
select postcode, sum(order_no)
from
(
SELECT
(SELECT customer_address.post_code FROM customer_address WHERE customer_address.address_type = 0 AND customer_address.customer_no = orders.customer_no) postcode,
orders.order_no
FROM orders, customer_address
WHERE orders.delivery_date = '27-MAY-15'
)
GROUP BY postcode;
EDIT:
However, your query seems wrong. Why do you cross-join orders and customer_address? By mistake I guess. Use explicit joins (INNER JOIN customer_address ON ...), when using joins to avoid such errors. But here I guess you'd just have to remove , customer_address.
Then why do you add order numbers? That doesn't seem to make sense.

LINQ Joining 2 tables

I have two tables in the database one contains a list of all possible grocery values. For Example
Milk
Cheese
Bread
Meat
the second table contains Items from Grocery that are selected. For Example:
Milk
Cheese
I want a result that has all possible grocery items with Milk and Cheese selected.
Any ideas?
Here are the tables.
The GroceryList Table:
ID INT PK
Description Varchar(50)
The ShoppingList Table:
ID INT PK
GroceryListID int FK to GroceryList.ID
So the resulting Entity would be all items from GroceryList and if they exist in ShoppingList then selected is marked as true:
ShoppingList.ID
Grocerylists.Description
Selected
Based on understanding you can do something like this
//first get the list of product which satisfy your condition
var ids = (from p ShoppingList
select p.GroceryListID ).ToList();
//second filter the Grocery products by using contains
var myProducts = from p in GroceryList
where ids.Contains(p.ID)
Select p;
or
if you want to get info about join than this image would help you
Inner Join
Outer Join
Try to understand and which may help you to resolve your query
Edit: Still sounds like you want to do a left join. In your case:
var LINQResult = from g in Datacontext.GroceryList
from s in DataContext.ShoppingList
.Where(c=>c.ID == g.ID)
.DefaultIfEmpty()
select new {
g.ID,
g.Description,
s.ID // Will be null if not selected.
};
For more examples:
Left Join on multiple tables in Linq to SQL

Linq Sum for Multiple Joins

Can someone help me out with the following Linq statement? I'm trying to get join 4 tables through Linq, group by the last table and sum a property of the first.
var payments = (
from payment in db.Payments
join payees in db.cmsMembers on payment.PayeeID equals payees.nodeId
join payeeGroups in db.cmsMember2MemberGroups on payees.nodeId equals payeeGroups.Member
join groups in db.umbracoNodes on payeeGroups.MemberGroup equals groups.id
group groups by new {groups.text, payment} into groupedPayments
select new
{
Group = groupedPayments.Key.text,
Count = groupedPayments.Count(),
Amount = groupedPayments.Sum(?)
}
).ToList();
The issue I'm having is that the in the groupedPayments.Sum(..) call I'm only getting access to the "groups" object. This is fine for the Group name and count aspect but the SUM has to be performed on the payment.Amount property.
I've managed to resolve my issue. I hadn't realised that the object in the group declaration would become the actual object witin the resulting group item. By altering the original statement to the following:
group payment by groups into groupedPayments
it seems that each instance of groupedPayments.Key in the select statement now corresponds to the payment row which is what I required.

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