Count row and get latest row by date from multiple tables - sql

I have 2 tables, Customer and CustomerActivity as showed in the picture below:
I want to output a table that:
has all columns from Customer table where CustomerType = 'Existing Customer', plus 2 more columns:
totalActivity (count activityID) - shows total activity number of each customer.
latestActivity (max checkinTime) - shows the most recent activity datetime
So far I have these 2 queries but I don't know how to combine/join and filter them to get what I need. Anyone can help with 1 query (and some explanation would be perfect)
SELECT customerId, firstName, birthDate, customerType
FROM Customer
WHERE Customer.customerType = 'Existing Customer'
SELECT t1.activityId, t1.checkinTime, t1.customerId
FROM CustomerActivity t1
inner join (
SELECT customerId, max(checkinTime) as Lastest
FROM CustomerActivity
group by customerId
) t2 on t1.customerId = t2.customerId and t1.checkinTime = t2.Lastest

You're actually close. Here is what your query should look like:
SELECT
c.customerId,
c.firstName,
c.lastName,
c.birthDate,
c.customerType,
ca.totalActivity,
ca.latestActivity
FROM Customer c
INNER JOIN(
SELECT
customerId,
latestActivity = MAX(checkinTime),
totalActivity = COUNT(*)
FROM CustomerActivity
GROUP BY customerId
) ca
ON ca.customerId = c.customerId
WHERE
c.customerType = 'Existing Customer'
The subquery (inside the INNER JOIN) retrieves the total number of activities by using COUNT(*) and latest activity using MAX(checkinTime) of each customer. After that, you would want to join it to the Customer table on customerId. You then add a WHERE clause to filter for 'Existing Customer' only.

I haven't tested it against an actual schema, but something like this should work (this approach will show customers even if they have no activity, simply change the left join to an inner join if you only want customers with activity):
SELECT c.CustomerID
, c.FirstName
, c.BirthDate
, c.CustomerType
, COUNT(ca.ActivityID) AS TotalActivity
, MAX(ca.CheckinTime) AS MostRecentActivity
FROM Customer c
LEFT JOIN CustomerActivity ca ON c.CustomerID = ca.CustomerID
WHERE c.CustomerType = 'Existing Customer'
GROUP BY c.CustomerID
, c.FirstName
, c.BirthDate
, c.CustomerType

You can get what you want without group by, by using row_number() and window fu instead:
SELECT c.*, ca.numActivities, ca.activityId as LastActivity
FROM Customer c JOIN
(select ca.*,
count(*) over (partition by ca.CustomerId) as numActivities
row_number() over (partition by ca.CustomerId order by checkinTime desc) as seqnum
from CustomerActivity ca
) ca
on c.customerId = ca.customerId and ca.seqnum = 1
WHERE c.customerType = 'Existing Customer';
This version will let you get whatever columns you like from the most recent activity row.
EDIT:
In your original question, I thought you wanted the latest activity. If you just want the latest datetime, then aggregation works:
SELECT c.*, ca.numActivities, ca.lastActivityDateTime
FROM Customer c JOIN
(select ca.*,
count(*) as numActivities
max(checkinTime) as lastActivityDateTime
from CustomerActivity ca
) ca
on c.customerId = ca.customerId
WHERE c.customerType = 'Existing Customer';

Select c.customerId, c.firstName, c.lastName, c.birthDate, c.customerType, gca.latestCheckIn, gca.count
from customer as c,
(select ca.customerId, max(ca.checkInTime) as latestCheckIn, count(*) as checkinCount
from customerActivity as ca
group by ca.customerId) as gca
where gca.customerId = c.customerId AND c.customerType = 'Existing Customer'
If you clarify more about customer with no activity, one can change the query to using left join

Related

Join another table then show max from other table

I'm very new to sql so struggling a lot here.
I have 2 tables, Customer, and Transaction
Transaction
tID
cID
carID
eID
tDate
PickupDate
ReturnDate
Amount_Due
Customer
cID
fName
lName
Address
Postcode
email
DOB
I need to show all Customer information for the customer with the highest Amount_Due.
I think I have the first part correct but cannot get the Join working correctly.
SELECT c.cID
,c.fName
,c.lName
,c.Address
,c.Postcode
,c.email
,c.DOB
,[Transaction].Amount_Due
FROM Customer c, [Transaction]
LEFT JOIN
(
SELECT MAX(Amount_Due) AS Amount_Due
FROM [Transaction]
GROUP BY Amount_Due
) t
ON c.cID = t.cID
There are more than one method of solving this if I understand it correctly. e.g:
SELECT
c.cID
, c.fName
, c.lName
, c.Address
, c.Postcode
, c.email
, c.DOB
, t.Amount_Due
FROM Customer c
INNER JOIN (SELECT
cID
, MAX(Amount_Due) AS Amount_Due
FROM [Transaction]
GROUP BY cID
) t ON c.cID = t.cID
WHERE t.Amount_Due = (SELECT MAX(Amount_Due) FROM transactions)
or:
with cte as (
select cID, amount_due, max(Amount_Due) over(partition by 1) as max_amount_due from transactions
)
select
*
FROM Customer c
INNER JOIN cte on c.cID = cte.cID
and cte.amount_due = cte.max_amount_due
SELECT c.cID
,c.fName
,c.lName
,c.Address
,c.Postcode
,c.email
,c.DOB
,t.Amount_Due
FROM Customer c
LEFT JOIN
(
SELECT cID, MAX(Amount_Due) AS Amount_Due
FROM [Transaction]
GROUP BY cID
) t
ON c.cID = t.cID

How can i get all the MAX values from a certain column in a dataset in PostgreSQL

I'm asked to find the top user for different countries, however, one of the countries has 2 users with the same amount spent so they should both be the top users, but I can't get the max value for 2 values in this country.
Here is the code:
WITH t1 AS (
SELECT c.customerid,SUM(i.total) tot
FROM invoice i
JOIN customer c ON c.customerid = i.customerid
GROUP BY 1
ORDER BY 2 DESC
),
t2 AS (
SELECT c.customerid as CustomerId ,c.firstname as FirstName,c.lastname as LastName, i.billingcountry as Country,MAX(t1.tot) as TotalSpent
FROM t1
JOIN customer c
ON c.customerid = t1.customerid
JOIN invoice i ON i.customerid = c.customerid
GROUP BY 4
ORDER BY 4
)
SELECT *
FROM t2
BILLINGCOUNTRY is in Invoice, and it has the name of all the countries.
TOTAL is also in invoice and it shows how much is spent for each purchase by Customer (so there are different fees and taxes for each purchase and total shows the final price payed by the user at each time)
Customer has id,name,last name and from its' ID I'm extracting the total of each of his purchases
MAX was used after finding the sum for each Customer and it was GROUPED BY country so that i could find the max for each country, however I can't seem to find the max of the last country that had 2 max values
Use rank() or dense_rank():
SELECT c.*, i.tot
FROM (SELECT i.customerid, i.billingCountry, SUM(i.total) as tot,
RANK() OVER (PARTITION BY i.billingCountry ORDER BY SUM(i.total) DESC) as seqnum
FROM invoice i
GROUP BY 1, 2
) i JOIN
customer c
ON c.customerid = i.customerid
WHERE seqnum = 1;
The subquery finds the amount per customer in each country -- and importantly calculates a ranking for the combination with ties having the same rank. The outer query just brings in the additional customer information that you seem to want.
here is how it worked for me since i was restricted from using many Commands such RIGHT JOIN and RANK() (As what Gordon Linoff suggessted) so i had to create a 3rd case for the anamoly and join it using union. this solution works only on this case, the good solution is the one posted by Gordon Linoff:
WITH t1 AS (
SELECT c.customerid,SUM(i.total) tot
FROM invoice i
JOIN customer c ON c.customerid = i.customerid
GROUP BY 1
ORDER BY 2 DESC
),
t2 AS (
SELECT c.customerid as CustomerId ,c.firstname as FirstName,c.lastname as LastName, i.billingcountry as Country,MAX(t1.tot) as TotalSpent
FROM t1
JOIN customer c
ON c.customerid = t1.customerid
JOIN invoice i ON i.customerid = c.customerid
GROUP BY 4
ORDER BY 4
) ,
t3 AS (
SELECT DISTINCT c.customerid as CustomerId ,c.firstname as FirstName,c.lastname as LastName, i.billingcountry as Country,t1.tot as TotalSpent
FROM t1
JOIN customer c
ON c.customerid = t1.customerid
JOIN invoice i ON i.customerid = c.customerid
WHERE i.billingcountry = 'United Kingdom'
ORDER BY t1.tot DESC
LIMIT 2
)
SELECT *
FROM t2
UNION
SELECT * FROM t3
ORDER BY t2.country

need help to write a query about this db

I have this DB and I need help with this query
Find the customer ID, first name, last name and the movie Name
of the customer that bought the most ticket in that day
I found the customer who bought the highest number of tickets in that day, now I need to find the movies that he bought tickets for
SELECT c.*, COUNT(*) 'bought'
FROM customer c JOIN ticket t ON c.customerId=t.customerId
GROUP BY c.customerId
HAVING bought=(SELECT MAX(T1.CNT)
FROM (SELECT COUNT(*) AS CNT
FROM customer c JOIN ticket t ON c.customerId=t.customerId
GROUP BY c.customerId) AS T1)
So without building the database and populating it with dummy data I can't test this, but I think I found a solution for you.
SELECT C.CUSTOMERID, C.FIRSTNAME, C.LASTNAME, M.TITLE
FROM CUSTOMER C JOIN TICKET T ON C.CUSTOMERID = T.CUSTOMERID JOIN SHOWS S ON T.SHOWNUMBER = S.SHOWNUMBER JOIN MOVIE M ON M.MOVIEID = S.MOVIEID
WHERE C.CUSTOMERID IN(
SELECT C1.CUSTOMERID FROM(
SELECT CUST.CUSTOMERID, COUNT(*) 'BOUGHT'
FROM CUSTOMER CUST JOIN TICKET TICK ON CUST.CUSTOMERID = TICK.CUSTOMERID
GROUP BY CUST.CUSTOMERID
HAVING BOUGHT = (SELECT MAX(T1.CNT) FROM (SELECT COUNT(*) AS CNT FROM CUSTOMER CUSTO JOIN TICKET TICKE ON CUSTO.CUSTOMERID = TICKE.CUSTOMERID GROUP BY CUSTO.CUSTOMERID)AS T1) AS C1);

How to get two ties of maximum values in my query?

I have a database of a music store and I need to extract the max value of purchases from a customer specific to each country. While using MAX function, I noticed that I have two ties of maximum values in 'United Kingdom'. So, I need my query to return both the customers for this country.
With t1 As (
Select i.CustomerId, c.FirstName, c.LastName,
i.BillingCountry Country, Sum(i.Total) Totals
From Invoice i
Join Customer c
On c.CustomerId = i.CustomerId
GROUP BY 1, 4)
Select CustomerId, FirstName, LastName, Country, Max(Totals) TotalSpent
From t1
Group By 4;
This is the output
This is what the output should be
I tried using TOP but apparently by workspace does not accept this function. So, Please suggest a solution that does not use this function.
Thanks in advance.
I would consider something like
With t1 As (
Select i.CustomerId, c.FirstName, c.LastName,
i.BillingCountry Country, Sum(i.Total) Totals
From Invoice i
Join Customer c
On c.CustomerId = i.CustomerId
GROUP BY 1, 4)
Select CustomerId, FirstName, LastName, Country, Totals TotalSpent
From t1
WHERE t1.Total = (SELECT MAX(Totals) FROM t1 t2 WHERE t1.Country = t2.Country)
Group By 4;
(I've changed MAX(Totals) to Totals in your main query's SELECT statement and added the WHERE clause)
or
With t1 As (
Select i.CustomerId, c.FirstName, c.LastName,
i.BillingCountry Country, Sum(i.Total) Totals
From Invoice i
Join Customer c
On c.CustomerId = i.CustomerId
GROUP BY 1, 4),
t2 as (
SELECT Country, MAX(Totals) as Totals
FROM t1
GROUP BY Country
)
Select t1.CustomerId, t1.FirstName, t1.LastName, t1.Country, t1.Totals TotalSpent
From t1 INNER JOIN t2
on t1.Country = t2.Country and t1.Totals = t2.Totals
Group By 4;
(I've added the t2 CTE, joined it into your main query, and adjusted the main SELECT accordingly)
In both cases, I'm trying to select all the customer information where that customer's total is equal to the maximum total for their country. In principle, this should work no matter how many ties there are.
Use window functions!
select CustomerId, FirstName, LastName,
Country, Totals
from (select i.CustomerId, c.FirstName, c.LastName,
i.BillingCountry as Country, sum(i.Total) as Totals,
rank() over (partition by i.BillingCountry over sum(i.Total) desc) as seqnum
from Invoice i join
Customer c
on c.CustomerId = i.CustomerId
group by i.CustomerId, c.FirstName, c.LastName,
i.BillingCountry
) ic
where seqnum = 1;

selecting customers based on account status

I need to generate a report of customers with the account status of COLLECTIONS and has more than 1 consecutive DECLINED charged.
Customer Table
=====================
CustomerID
FirstName
LastName
Email
Status (Collections)
Charge
=====================
ChargeID
CustomerID
DateCharged
Amount
ACK (Declined)
SELECT Customer.CustomerID, Customer.FirstName, Customer.LastName,
Customer.Status, Charge.ChargeID, Charge.Amount, Charge.DateCharged, Charge.ACK
FROM Customers
INNER JOIN Charge
ON Customer.CustomerID=Charge.CustomerID
AND Charge.ACK = 'Declined'
A customer has to have at least 2 DECLINED charges before they are sent to collections. How can I check to see if a customer has 2 declines?
Normally, I would say just do an aggregation with a having clause.
However, your query suggests that you also want the details on the declines. Instead, using a window function to calculate the total number of declines:
SELECT *
FROM (SELECT c.CustomerID, c.FirstName, c.LastName,
c.Status, ch.ChargeID, ch.Amount, ch.DateCharged, ch.ACK,
sum(case when ch.ACK = 'Declined' then 1 else 0 end) over
(partition by c.customerId) as NumDeclines
FROM Customers c INNER JOIN
Charge ch
ON c.CustomerID = ch.CustomerID
) cc
WHERE ACK = 'Declined' and NumDeclines >= 2
ORDER BY CustomerId, DateCharged;
If you just want the customers and not the detail, then do:
SELECT c.CustomerID, c.FirstName, c.LastName, c.Status
FROM Customers c INNER JOIN
Charge ch
ON c.CustomerID = ch.CustomerID
WHERE ACK = 'Declined'
GROUP BY c.CustomerID, c.FirstName, c.LastName, c.Statu
HAVING count(*) >= 2;
Per my comment, this is how I would accomplish the task at hand.
SELECT
*
FROM (
SELECT
Row_Number() OVER (PARTITION BY Customer.CustomerID ORDER BY Charge.DateCharged DESC) AS [Row],
Customer.CustomerID, Customer.FirstName, Customer.LastName,
Customer.Status, Charge.ChargeID, Charge.Amount, Charge.DateCharged, Charge.ACK
FROM Customers
INNER JOIN Charge ON Customer.CustomerID=Charge.CustomerID
) a
INNER JOIN (
SELECT
Row_Number() OVER (PARTITION BY Customer.CustomerID ORDER BY Charge.DateCharged DESC) AS [Row],
Customer.CustomerID, Customer.FirstName, Customer.LastName,
Customer.Status, Charge.ChargeID, Charge.Amount, Charge.DateCharged, Charge.ACK
FROM Customers
INNER JOIN Charge ON Customer.CustomerID=Charge.CustomerID
) b ON a.CustomerID=b.CustomerID
WHERE a.[Row]=b.[Row]-1
AND a.ACK = 'Declined'
AND b.ACK = 'Declined'
AND a.[Row] IN (1,2)
AND b.[Row] IN (1,2)