I am trying to build a query to get the latest record which could be used in MySQL, Oracle 10 and 12.
Scenario:
I have 4 tables - customer, address, loan, application
I wanted to join these 4 tables to get the customer name and address for an application.
customer, loan and application have 1 to 1 relationship
while customer and address has 1 to many relationship.
select count(c.name)
from application
left join loan on (loan.id = application.id)
inner join customer on (loan.cust_num = customer.id);
This gives 100 rows;
select count(c.name)
from application
left join loan on (loan.id = application.id)
inner join customer on (loan.cust_num = customer.id)
inner join address a1 on (loan.cust_num = address.cust_num)
inner join (
select max(date) as max_date, cust_num
from address
where address_type = 'studio'
group by cust_num
) a2
on a1.cust_num = a2.cust_num
and a1.date = a2.max_date;
This gives 200 records since in address for a date there are more than 1 records whose address_type is 'studio' for a customer.
How to get the latest record.
At the end I wanted address columns from address table and name from customer table for a application.
Thanks.
For code that works in both Oracle and MySQL, I would recommend a correlated subquery:
select count(c.name)
from application a join
loan l
on o.id = a.id join
customer c
on l.cust_num = c.id left join
address ad
on ad.cust_num = l.cust_num and
ad.address_type = 'studio' and
ad.date = (select max(ad2.date)
from address ad2
where ad2.cust_num = ad.cust_num and
ad2.address_type = ad.address_type
);
If you are using MySQL 8+ or don't need MySQL, then you should go with row_number() as Tim suggests.
Note that I removed the left join. The join to customer is using a field from loan, which turns the outer join into an inner join. You might as well express the logic actually being implemented.
Related
I have 3 tables in my database
companies{
id,
name,
address
}
stores{
id,
name,
address,
company_id
}
invoices{
id,
total,
date_time,
store_id
}
As you can see, each store is connected to a company via foreign key, also each invoice is connected to a store.
My question is, how can i write a SQL query which will return all stores by a company and order them by their turnover?
If i use the query:
SELECT s.*,
sum(i.total) as turnover FROM store s
JOIN invoices i
ON i.store_id = s.id
WHERE YEAR(i.date_time) = 2019;
I can see the turnover for one store for a year 2019 for example, but i'm struggling to find a way to get a list of store ordered by their turnover for a certain period.
You're going to need to join all 3 tables:
SELECT *
FROM
companies c
INNER JOIN stores s on s.company_id = c.id
INNER JOIN invoices i ON i.store_id = s.id
That's your entire raw data in detailed list. Then you say you want it for a certain company only:
SELECT *
FROM
companies c
INNER JOIN stores s on s.company_id = c.id
INNER JOIN invoices i ON i.store_id = s.id
WHERE c.name = 'Acme Rubber Co'
Then you only want the stores and the invoices amounts:
SELECT s.name, i.total
FROM
companies c
INNER JOIN stores s on s.company_id = c.id
INNER JOIN invoices i ON i.store_id = s.id
WHERE c.name = 'Acme Rubber Co'
Then you want a row set where each line is a single store and the sum of all invoices for that store:
SELECT s.name, SUM(i.total)
FROM
companies c
INNER JOIN stores s on s.company_id = c.id
INNER JOIN invoices i ON i.store_id = s.id
WHERE c.name = 'Acme Rubber Co'
GROUP BY s.name
Lastly you want them in descending order, highest total first:
SELECT s.name as storename, SUM(i.total) as turnover
FROM
companies c
INNER JOIN stores s on s.company_id = c.id
INNER JOIN invoices i ON i.store_id = s.id
WHERE c.name = 'Acme Rubber Co'
GROUP BY s.name
ORDER BY turnover DESC
The order of evaluation in sql is FROM(with joins), WHERE, GROUP BY, SELECT, ORDER BY which is why I use different names in eg the order by than I do in the group by. Conceptually your db only sees the names of things as output by the immediately previous operation. Mysql isn't actually too picky but some db are - you couldn't say GROUP BY storename in sql server because the SELECT that creates the storename alias hasn't been run at the time the group by is done
Note: I wasn't really sure on what you were looking for in a WHERE - you started by saying "all stores turnover for a certain company" and finished saying you were "struggling to get turnover for a period"
If you want a period, use eg WHERE somedatecolumn BETWEEN '2000-01-01' AND '2000-12-31' (Between is inclusive) or WHERE somedatecolumn >= '2000-01-01' AND somedatecolumn < '2001-01-01' (A good pattern to use if the date includes a time too). It is almost never wise to call a function on a column you're searching with, ie do not do WHERE YEAR(somedatecolumn) = 2000 because it disables indexing on the column and makes the search very slow
I am trying to get records with a single SQL query. There are many records in the database but it returns only 1 row.
For example:
it returns record no. 1. If i delete this record then it returns record no. 2. As there are many records. I want it to display all the records.
Here is my Query.
SELECT camp.title, camp.status, c.name, b.base_id, count(s.base_id) as subscribers
FROM campaigns as camp
JOIN company as c ON c.id = camp.company_id
JOIN campaign_numbers as b ON b.campaign_id = camp.id
JOIN base_numbers as s ON s.base_id = b.base_id
WHERE camp.campaign_date = '2017-04-07' AND camp.approved = 1
What i actually want my query to return all the campaigns for current date which is 2017-04-07 and approved status should be 1 but my query is returning only single row, not all campaigns.
If i do this, it returns all..
SELECT title, status
From Campaigns
WHERE campaign_date = '2017-04-07' AND approved = 1
But i also want to show company name and also want to count all subscribers too. WHere am i doing it wrong?
You have an aggregation function in the SELECT with no GROUP BY. In most databases, this would return an error. In MySQL, this is an aggregation query that always returns one row.
Presumably you want the GROUP BY:
GROUP BY camp.title, camp.status, c.name, b.base_id
try as below
SELECT camp.title, camp.status, c.name, b.base_id, count(s.id) as subscribers
FROM campaigns as camp
Left JOIN company as c ON c.id = camp.company_id
Left JOIN campaign_numbers as b ON b.campaign_id = camp.id
Left JOIN base_numbers as s ON s.base_id = b.base_id
WHERE camp.campaign_date = '2017-04-07' AND camp.approved = 1
group by camp.title, camp.status, c.name, b.base_id
I have following query to solve:
"List the Member(s) who are born in and after 1990 and have organised the Hackathons that have received funding from the project(s) that have the highest number of labs co-working on them."
SELECT Member.email, Member.firstName, Member.lastName, Member.dateOfBirth,
Hubs.organiserMember, MAX(LabInProject.projectID)
FROM LabInProject
INNER JOIN Project ON LabInProject.projectID=Project.projectID
INNER JOIN Hackathon ON Project.projectID=Hackathon.fundingProject
INNER JOIN Hubs ON Hackathon.eventID=Hubs.eventID
INNER JOIN Member ON Member.email=Hubs.organiserMember
WHERE LabInProject.projectID = (SELECT MAX(LabInProject.projectID) FROM LabInProject)
GROUP BY Hubs.organiserMember
HAVING Member.dateOfBirth > '1990'
The SELECT MAX gives me the highest projectID (number) in the row, NOT the highest COUNT of projectID.
How do I get the "MAX COUNT" of projectID in table: LabInProject?
I have tried by making a subquery with a derived table: totalCount, but I don't know how to connect this with the joins, it's not working.
HAVING COUNT(*) =
(
SELECT COUNT(projectID) totalCount
FROM LabInProject
GROUP BY projectID
LIMIT 1
)
WHERE LabInProject.projectID = (SELECT MAX(LabInProject.projectID) FROM LabInProject)
You have a Syntax-Error here.
Try to post the closing bracket at the end of the statement.
Consider the below derived table in an inner join with its own derived tables to replace the earlier WHERE condition. This should return multiple projects that share same maximum counts:
...
INNER JOIN
-- OBTAIN PROJECT AND COUNTS CONDITIONED TO THE MAX
(SELECT sub.ProjectID, Count(*) As ProjectIDCount
FROM LabInProject sub
INNER JOIN Project ON LabInProject.projectID=Project.projectID
INNER JOIN Hackathon ON Project.projectID=Hackathon.fundingProject
INNER JOIN Hubs ON Hackathon.eventID=Hubs.eventID
INNER JOIN Member ON Member.email=Hubs.organiserMember
WHERE Member.dateOfBirth > '1990'
GROUP BY sub.ProjectID
HAVING Count(*) IN
-- OBTAIN SCALAR VALUE OF MAX PROJECT COUNT
(SELECT Max(dT.ProjectIDCount) As MaxOfProjectIDCount
FROM
-- OBTAIN PROJECT COUNTS
(SELECT subdT.ProjectID, Count(*) As ProjectIDCount
FROM LabInProject subdT
INNER JOIN Project ON LabInProject.projectID=Project.projectID
INNER JOIN Hackathon ON Project.projectID=Hackathon.fundingProject
INNER JOIN Hubs ON Hackathon.eventID=Hubs.eventID
INNER JOIN Member ON Member.email=Hubs.organiserMember
WHERE Member.dateOfBirth > '1990'
GROUP BY subdT.ProjectID) As dT)
) As temp
ON LabInProject.projectID = temp.projectID
...
This query is working fine. It gives a count of contest entrants for whom the contact id in contest_entries is their origin_contact in the person table.
SELECT c.handle, COUNT(*)
FROM `contest_entry` ce,
person p,
contest c
WHERE
p.origin_contact = ce.contact AND
c.id = ce.contest
GROUP BY c.id
I want to now query how many of those records also have at least one record where the contact id matches in email_list_subscription_log BUT that table may have many log records for any one contact id.
How do I write a join that gives me a count that is not inflated by the multiple records?
Should I use a version of my first query to get all of the contact ids into a tmp table and just use that?
Not sure which field is contact id, but you can do something like this:
select c.handle,
count(*) as count
from `contest_entry` ce
inner join person p on p.origin_contact = ce.contact
inner join contest c on c.id = ce.contest
where exists (
select 1
from email_list_subscription_log l
where l.contact_id = ce.contact
)
group by c.id
You ought to deflate the email_list_subscription_log with DISTINCT or GROUP:
SELECT c.handle, COUNT(*)
FROM `contest_entry` ce
JOIN person p ON (p.origin_contact = ce.contact)
JOIN contest c ON (c.id = ce.contest)
JOIN (SELECT DISTINCT contact, email FROM email_list_subscription_log ) AS elsuniq
ON (ce.contact = elsuniq.contact)
[ WHERE ]
GROUP BY c.id
Using GROUP in the subquery you might count the number of records while still returning one row per element:
JOIN (SELECT contact, count(*) AS elsrecords FROM email_list_subscription_log
GROUPY BY contact) AS elsuniq
With this JOIN syntax, the WHERE is not necessary, but I kept it there if you need additional filtering.
I have the following tables setup in a MySQL database:
Client:
id
name
Session:
id
service_id
date
Attendance:
id
client_id
session_id
Service:
id
duration
Clients have a many to many relationship with Sessions through the Attendance table.
I am trying to write a query which will return each clients' name and the total amount of time they have spent in sessions, like this:
Name Total Time (Mins)
Person A 200
Person B 500
Person C 100
So far my query looks like this:
SELECT
(SELECT SUM(services.duration)
FROM services
INNER JOIN sessions
ON sessions.service_id = services.id
INNER JOIN attendances
ON attendances.session_id = session.id
INNER JOIN clients
ON clients.id = attendances.client_id
GROUP BY clients.id) AS total_duration,
client.name
FROM clients
However, this returns no results. Whereas, if I run either part of the query in isolation I get the data I need, ie:
SELECT name FROM clients
OR
SELECT SUM(services.duration)
FROM services
INNER JOIN sessions
ON sessions.service_id = services.id
INNER JOIN attendances
ON attendances.session_id = session.id
INNER JOIN clients
ON clients.id = attendances.client_id
GROUP BY clients.id
Can anyone see a reason why it would not be working when I combine the two?
You are missing a WHERE condition in correlated query. I rewrote your query with only joins to make it more readable:
SELECT c.name, sum(srv.duration)
FROM clients c
JOIN attendances a ON a.client_id = c.id
JOIN sessions s ON s.id - a.session_id
JOIN services srv ON srv.id = s.service_id
GROUP BY c.name