Propel 1.6 Greatest n per group - greatest-n-per-group

I need to build propel query that can get the latest purchase of client
Please use these table/column names in your answer:
customer: id, name
purchase: id, customer_id, date
from this question here SQL join: selecting the last records in a one-to-many relationship
I need to build propel query based one of these queries:
SELECT c.*, p1.*
FROM customer c
JOIN purchase p1 ON (c.id = p1.customer_id)
LEFT OUTER JOIN purchase p2 ON (c.id = p2.customer_id AND
(p1.date < p2.date OR p1.date = p2.date AND p1.id < p2.id))
WHERE p2.id IS NULL;
or
SELECT c.*, p.*
FROM customer c INNER JOIN
(
SELECT customer_id,
MAX(date) MaxDate
FROM purchase
GROUP BY customer_id
) MaxDates ON c.id = MaxDates.customer_id INNER JOIN
purchase p ON MaxDates.customer_id = p.customer_id
AND MaxDates.MaxDate = p.date

Well, given your question (finding latest purchase for a given client) then you just want to do a search on the purchase table ordering by date and limiting to one:
$lastPurchase = PurchaseQuery::create()
->filterByCustomerId($customerId)
->orderByDate(Criteria::DESC)
->limit(1)
->find();
The answer you link to, however, is finding all customers AND their latest purchase. In that case I would probably use a custom SQL query (although you technically can do this with Propel functions, it would be more difficult and would not get you very much since you won't be hydrating objects (at least, not easily).
UPDATE
Here's some information on doing a manual query. Note that when doing so, the data returned will NOT be Propel objects, but you can do on-demand hydration if needed.
Example:
$sql = "SELECT c.*, p1.*
FROM customer c
JOIN purchase p1 ON (c.id = p1.customer_id)
LEFT OUTER JOIN purchase p2 ON (c.id = p2.customer_id AND
(p1.date < p2.date OR p1.date = p2.date AND p1.id < p2.id))
WHERE p2.id IS NULL;";
$conn = Propel::getConnection(CustomerPeer::DATABASE_NAME);
$stmt = $conn->prepare($query);
if ($stmt->execute()) {
while($result = $stmt->fetch()) {
$someField = $result['...'];
}
}

Related

How to filter database table by a multiple join records from another one table but different types?

I have a products table and corresponding ratings table which contains a foreign key product_id, grade(int) and type which is an enum accepting values robustness and price_quality_ratio
The grades accept values from 1 to 10. So for example, how would the query look like, if I wanted to filter the products where minimum grade for robustness would be 7 and minimum grade for price_quality_ratio would be 8?
You can join twice, once per rating. The inner joins eliminate the products that fail any rating criteria,
select p.*
from products p
inner join rating r1
on r1.product_id = p.product_id
and r1.type = 'robustness'
and r1.rating >= 7
inner join rating r2
on r2.product_id = p.product_id
and r2.type = 'price_quality_ratio'
and r2.rating >= 8
Another option is to use do conditional aggregation. This requires only one join, then a group by; the rating criteria are checked in the having clause.
select p.product_id, p.product_name
from products p
inner join rating r
on r.product_id = p.product_id
and r.type in ('robustness', 'price_quality_ratio')
group by p.product_id, p.product_name
having
min(case when r.type = 'robustness' then r.rating end) >= 7
and min(case when r.type = 'price_quality_ratio then r.rating end) >= 8
The JOIN proposed by #GMB would've been my first suggestion as well. If that gets too complicated with having to maintain too many rX.ratings, you can also use a nested query:
SELECT *
FROM (
SELECT p.*, r1.rating as robustness, r2.rating as price_quality_ratio
FROM products p
JOIN rating r1 ON (r1.product_id = p.product_id AND r1.type = 'robustness')
JOIN rating r2 ON (r2.product_id = p.product_id AND r2.type = 'price_quality_ratio')
) AS tmp
WHERE robustness >= 7
AND price_quality_ratio >= 8
-- ORDER BY (price_quality_ratio DESC, robustness DESC) -- etc

Optimise With Query in PostgreSQL

I have a working PostgreSQL query, but it is taking a considerable amount of time to execute. I need help optimising it.
I have:
Removed inner queries as much as possible.
Removed the unnecessary data from the query.
Created a with query which gets the required data from the beginning
I need help to optimise this query
with data as (
select
e.id,
e.name,
t.barcode,
tt.variant,
t.cost_cents::decimal / 100 as ticket_cost,
t.fee_cents::decimal / 100 as booking_fee
from
tickets t
inner join events e on t.event_id = e.id
inner join ticket_types tt on t.ticket_type_id = tt.id
where
t.status = 2
and e.source in ('source1', 'source2')
)
select
d.name,
count(distinct d.barcode) as issued,
(select count(distinct d2.barcode) from data d2 where d2.id = d.id and d2.variant is null) as sold,
sum(d.ticket_cost) as ticket_revenue,
sum(d.booking_fee) as booking_fees
from
data d
group by
id,
name
Better to detect slow parts with using EXPLAIN .
It will show cost of all parts
You can speed up joins by creating proper indexes.
Also, remove the subquery
(select count(distinct d2.barcode) from data d2 where d2.id = d.id and d2.variant is null)
from the SELECT clause and add a join to d2 table something like this:
select
d.name,
count(distinct d.barcode) as issued,
count(distinct d2.barcode) as sold,
sum(d.ticket_cost) as ticket_revenue,
sum(d.booking_fee) as booking_fees
from
data d
left join data d2 on (d2.id = d.id and d2.variant is null)
group by
d.id,
d.name

Is this Correlated SubQuery Correct

I am trying to wrap my head around sub-queries (correlated). Although the book explains (MS SQL Server 2012) it I'm still a little confused. Using three tables, Orders, Products and OrderDetails I want to find the sum of all products that were shipped to USA. I came up with the following query but do not quite understand how I got there except the correlation is referencing the 'where' in the outer query. Can someone correct my work and offer better explanation than book?
SELECT p.productid, p.productname, SUM(qty * od.unitprice) AS TotalAmount
FROM Production.Products AS p
JOIN Sales.OrderDetails AS od
ON p.productid = od.productid
WHERE N'USA' IN
(SELECT o.shipcountry
FROM Sales.Orders AS o
JOIN Sales.OrderDetails AS od
ON o.orderid = od.orderid
WHERE shipcountry = N'USA')
GROUP BY p.productid, p.productname;
SELECT
p.ProductID
, p.ProductName
, SUM(qty * od.UnitPrice) as [Total_Amount]
FROM
Production.Products productid
JOIN Sales.OrderDetails as od on p.productid = od.product
JOIN SalesOrders as o on o.OrderID = od.OrderID
WHERE
o.Shipcountry in ('USA')
Group BY
p.ProductID,
P.ProductName
The following should work - you technically dont require the sub query. Will only bog down the query.

Get Greatest date across multiple columns with entity framework

I have three entities: Group, Activity, and Comment. Each entity is represented in the db as a table. A Group has many Activities, and an Activity has many comments. Each entity has a CreatedDate field. I need to query for all groups + the CreatedDate of the most recent entity created on that Group's object graph.
I've constructed a sql query that gives me what I need, but I'm not sure how to do this in entity framework. Specifically this line: (SELECT MAX(X)
FROM (VALUES (g.CreatedDate), (a.CreatedDate), (c.CreatedDate)) Thanks in advance for your help. Here's the full query:
WITH GroupWithLastActivityDate AS (
SELECT DISTINCT
g.Id
,g.GroupName
,g.GroupDescription
,g.CreatedDate
,g.ApartmentComplexId
,(SELECT MAX(X)
FROM (VALUES (g.CreatedDate), (a.CreatedDate), (c.CreatedDate)) AS AllDates(X)) AS LastActivityDate
FROM Groups g
LEFT OUTER JOIN Activities a
on g.Id = a.GroupId
LEFT OUTER JOIN Comments c
on a.Id = c.ActivityId
WHERE g.IsActive = 1
)
SELECT
GroupId = g.Id
,g.GroupName
,g.GroupDescription
,g.ApartmentComplexId
,NumberOfActivities = COUNT(DISTINCT a.Id)
,g.CreatedDate
,LastActivityDate = Max(g.LastActivityDate)
FROM GroupWithLastActivityDate g
INNER JOIN Activities a
on g.Id = a.GroupId
WHERE a.IsActive = 1
GROUP BY g.Id
,g.GroupName
,g.GroupDescription
,g.CreatedDate
,g.ApartmentComplexId
I should add that for now I've constructed a view with this query (plus some other stuff) which I'm querying with a SqlQuery.

MS Access INNER JOIN most recent entry

I'm having some trouble trying to get Microsoft Access 2007 to accept my SQL query but it keeps throwing syntax errors at me that don't help me correct the problem.
I have two tables, let's call them Customers and Orders for ease.
I need some customer details, but also a few details from the most recent order. I currently have a query like this:
SELECT c.ID, c.Name, c.Address, o.ID, o.Date, o.TotalPrice
FROM Customers c
INNER JOIN Orders o
ON c.ID = o.CustomerID
AND o.ID = (SELECT TOP 1 ID FROM Orders WHERE CustomerID = c.ID ORDER BY Date DESC)
To me, it appears valid, but Access keeps throwing 'syntax error's at me and when I hit OK, it selects a piece of the SQL text that doesn't even relate to it.
If I take the extra SELECT clause out it works but is obviously not what I need.
Any ideas?
You cannot use AND in that way in MS Access, change it to WHERE. In addition, you have two reserved words in your column (field) names - Name, Date. These should be enclosed in square brackets when not prefixed by a table name or alias, or better, renamed.
SELECT c.ID, c.Name, c.Address, o.ID, o.Date, o.TotalPrice
FROM Customers c
INNER JOIN Orders o
ON c.ID = o.CustomerID
WHERE o.ID = (
SELECT TOP 1 ID FROM Orders
WHERE CustomerID = c.ID ORDER BY [Date] DESC)
I worked out how to do it in Microsoft Access. You INNER JOIN on a pre-sorted sub-query. That way you don't have to do multiple ON conditions which aren't supported.
SELECT c.ID, c.Name, c.Address, o.OrderNo, o.OrderDate, o.TotalPrice
FROM Customers c
INNER JOIN (SELECT * FROM Orders ORDER BY OrderDate DESC) o
ON c.ID = o.CustomerID
How efficient this is another story, but it works...