SQL reuse a subquery 'AS' as a parameter for another subquery - mysql

I am new to SQL and try to re-use an alias/subquery I made as a parameter for another subquery.
For a certain period of time I want all the customers who have made a purchase, I get the date of the last purchase but now I am trying to pass this date to the invoices in order to get the name of the salesperson associated to this invoice.
So far I have this:
SELECT c.id,
c.firstname,
c.lastname,
c.language,
c.sex,
c.company,
c.city,
c.postal_code,
c.email,
c.created_at,
(SELECT max(`created_at`) FROM invoices WHERE client_id=c.id) AS last_purchase_date,
[...]
FROM
clients c
JOIN
boutiques b ON b.id = c.boutique_id
JOIN
brands br ON br.id = b.brand_id
[...]
and would like something like:
SELECT c.id,
c.firstname,
c.lastname,
c.language,
c.sex,
c.company,
c.city,
c.postal_code,
c.email,
c.created_at,
u.name
(SELECT max(`created_at`) FROM invoices WHERE client_id=c.id) AS last_purchase_date,
(SELECT id FROM invoices WHERE created_at = last_purchase_date) AS last_invoice_id
(SELECT name FROM users u WHERE id=last_invoice.user_id) AS sales_advisor
[...]
FROM
clients c
JOIN
boutiques b ON b.id = c.boutique_id
JOIN
users u ON u.boutique_id = b.id
JOIN
brands br ON br.id = b.brand_id
[...]
Thanks in advance!

Consider migrating those subqueries into derived tables (i.e., queries in FROM or JOIN clauses instead of SELECT clause). In fact, two of those subqueries can become whole tables: invoices and second users.
SELECT c.id,
c.firstname,
c.lastname,
c.language,
c.sex,
c.company,
c.city,
c.postal_code,
c.email,
c.created_at,
u.name,
agg.last_purchase_date,
i.id AS last_invoice_id,
u2.name AS sales_advisor
[...]
FROM
clients c
JOIN
boutiques b ON b.id = c.boutique_id
JOIN
users u ON u.boutique_id = b.id
JOIN
brands br ON br.id = b.brand_id
JOIN
(
SELECT client_id, max(`created_at`) as last_purchase_date
FROM invoices
GROUP BY client_id
) agg
ON c.id = agg.client_id
JOIN
invoices i ON i.client_id = agg.client_id
AND i.created_at = agg.last_purchase_date
JOIN
users u2 ON u2.id = i.user_id
[...]

Related

MySQL Group Join Table

I have below three SQL statement and I want to select out like below, I tried but not success.
Need some
help.
Output:
member_id, balance, firstname, lastname, LastPurchase, LastOrde
SELECT c.member_id
, c.firstname
, c.lastname
, m.balance
FROM member m
, customer c
where m.member_id = c.member_id
order
by m.member_id
SELECT member_id, max(date) as LastPurchase
FROM purchase
GROUP
BY member_id
SELECT member_id, max(date) as LastOrder
FROM ordert
GROUP
BY member_id
You can join these statements -
SELECT c.member_id, c.firstname, c.lastname, m.balance, p.LastPurchase, o.LastOrder
FROM member m
join customer c on m.member_id = c.member_id
left join (SELECT member_id, max(date) as LastPurchase
FROM purchase
GROUP BY member_id) p on p.member_id = m.member_id
left join (SELECT member_id, max(date) as LastOrder
FROM ordert
GROUP BY member_id) o on o.member_id = m.member_id
order by m.member_id
You can join the aggregate queries. The JOIN ... USING syntax comes handy here, since all join column names are the same:
SELECT c.member_id, c.firstname, c.lastname, m.balance, p.last_purchase, o.last_purchase
FROM member m
INNER JOIN customer c USING(member_id)
INNER JOIN (
SELECT member_id, max(date) last_purchase FROM purchase GROUP BY member_id
) p USING(member_id)
INNER JOIN (
SELECT member_id, max(date) last_order FROM order GROUP BY member_id
) o USING(member_id)
ORDER BY c.member_id
Important: your original query uses implicit, old-shool joins (with a comma in the from clause) - this syntax fell out of favor more than 20 years ago and its use is discourage, since it is harder to write, read, and understand.
One of the many benefits of using explicit joins here is that you can easily change the INNER JOINs to LEFT JOINs if there is a possibility that a member has no purchase or no order at all.

Display results which have no count/zero as well

I am trying to get a count of the number of logins during a given timeframe, currently my SQL query displays only results that had at least one login, I'd like it to display even those which have zero logins.
Query i'm using:
SELECT c.FullName, COUNT(l.Id)
FROM LoginsTable l JOIN UsersTable u ON u.Email = l.Email JOIN Organisations c ON c.Id = u.OrganisationId
WHERE l.AttemptTime > "2019-10-01" AND l.AttemptTime < "2019-11-01" AND l.Success = 1
GROUP BY c.Name
ORDER BY c.Name ASC;
You have a few issues. Firstly, you either need to use a RIGHT JOIN from LoginsTable or reorder the JOINs to put the JOIN to LoginsTable last and use a LEFT JOIN. Given the nature of your query the latter probably makes more sense.
Secondly, you need to put any conditions on fields from a table which has been LEFT JOINed into the join condition, otherwise MySQL converts the LEFT JOIN into an INNER JOIN (see the manual). Finally, you should GROUP BY the same fields as specified in your SELECT. This should work:
SELECT c.FullName, COUNT(l.Id)
FROM Organisations c
JOIN UsersTable u ON u.OrganisationId = c.Id
LEFT JOIN LoginsTable l ON u.Email = l.Email AND l.AttemptTime > "2019-10-01" AND l.AttemptTime < "2019-11-01" AND l.Success = 1
GROUP BY c.FullName
ORDER BY c.FullName
I found 2 issues here:
your group by column is not listed on your column
date condition is using double quotes.
try below query.
SELECT c.FullName, COUNT(l.Id)
FROM LoginsTable l
LEFT JOIN UsersTable u ON u.Email = l.Email
LEFT JOIN Organisations c ON c.Id = u.OrganisationId
WHERE l.AttemptTime between '2019-10-01' AND '2019-11-01' AND l.Success = 1
GROUP BY c.FullName
ORDER BY c.FullName ASC;
As Roman Hocke said you need to use left join as below :
SELECT c.FullName, COUNT(l.Id)
FROM UsersTable u
JOIN Organisations c ON c.Id = u.OrganisationId
LEFT JOIN LoginsTable l ON u.Email = l.Email
WHERE l.AttemptTime > "2019-10-01" AND l.AttemptTime < "2019-11-01" AND l.Success = 1
GROUP BY c.Name
ORDER BY c.Name ASC;
Moreover, you should fix your group by or select using the same field : SELECT c.Name or GROUP BY c.FullName ORDER BY c.FullName
EDIT : Nick's answer is the one. As he said perfectly well, you need to put your conditions in the on clause of your left join.
SELECT c.FullName, COUNT(l.Id)
FROM UsersTable u
JOIN Organisations c ON c.Id = u.OrganisationId
LEFT JOIN LoginsTable l ON (u.Email = l.Email AND l.AttemptTime > "2019-10-01" AND l.AttemptTime < "2019-11-01" AND l.Success = 1)
GROUP BY c.FullName
ORDER BY c.FullName ASC;

use 2 left join can't work but separately can get results

I have three tables, company, user and share. I want to count one company's user and share, they are not relevant.
There may be a row that has share value but not user value. so I used left join, I can get results separately, but it doesn't work together.
Here is my query:
SELECT c.name, count(u.company_id), count(s.company_id)
FROM company c
LEFT JOIN user u
ON c.id=u.company_id and u.company_id=337
WHERE u.company_id is NOT NULL
LEFT JOIN share s
ON c.id=s.id AND s.company_id=337
WHERE s.company_id is NOT NULL
You need to do at least one of the counts in a subquery. Otherwise, both counts will be the same, since you're just counting the rows in the resulting cross product.
SELECT c.name, user_count, share_count
FROM company AS c
JOIN (SELECT company_id, COUNT(*) AS user_count
FROM users
GROUP BY company_id) AS u
ON u.company_id = c.id
JOIN (SELECT company_id, COUNT(*) AS share_count
FROM share
GROUP BY company_id) AS s
ON s.company_id = c.id
WHERE c.company_id = 337
Another option is to count the distinct primary keys of the tables you're joining with:
SELECT c.name, COUNT(DISTINCT u.id) AS user_count, COUNT(DISTINCT s.id) AS share_count
FROM company AS c
JOIN users AS u on u.company_id = c.id
JOIN share AS s ON s.company_id = c.id
WHERE c.company_id = 337
Your code looks okay, except for the extra WHERE clause. However, you probably want COUNT(DISTINCT), because the two counts will return the same value:
SELECT c.name, count(distinct u.company_id), count(distinct s.company_id)
FROM company c LEFT JOIN
user u
ON c.id = u.company_id and u.company_id=337 LEFT JOIN
share s
ON c.id = s.id AND s.company_id=337
WHERE s.company_id is NOT NULL AND u.company_id IS NOT NULL;

MySQL LEFT JOIN that won't work

I have a mysql join and for someone reason its returning duplicate fields for image and firstname and lastname. Like it's not joining right.
Here is the SQL
SELECT a.follow_id, a.user_id, a.following, b.firstname, b.lastname, c.firstname, c.lastname, b.image, c.image
FROM followers a
LEFT JOIN candidates b ON a.following = b.user_id
LEFT JOIN donors c On a.following = c.user_id
WHERE a.user_id = 222
LIMIT 9
Both candidates and donors have a firstname and lastname and image, so I need to get those fields, but not duplicate the fields.
My Results
Can someone please tell me what I'm doing wrong?
Thanks in advance.
SELECT a.follow_id, a.user_id, a.following, b.firstname, b.lastname, b.image
FROM followers a
LEFT JOIN candidates b ON a.following = b.user_id
WHERE a.user_id = 222
UNION DISTINCT
SELECT a.follow_id, a.user_id, a.following, c.firstname, c.lastname, c.image
FROM followers a
LEFT JOIN donors c On a.following = c.user_id
WHERE a.user_id = 222
I'm not sure i'm understand your problem correctly.
Your follow_id and following fields are unique. Since you are joining to the candidates and donors tables using the unique following field, you will need to create 2 subqueries that pull the pertinent information from each table as well as the user_id from the followers table.
You can then do your joins from your followers table to each subquery:
SELECT f.user_id,
can.firstname,
can.lastname,
don.firstname,
don.lastname,
can.image,
don.image
FROM followers f
LEFT JOIN (SELECT a.user_id,
a.following,
b.firstname,
b.lastname,
b.image
FROM followers a
INNER JOIN candidates b
ON a.following = b.user_id) can
ON f.user_id = can.user_id
LEFT JOIN (SELECT a.user_id,
a.following,
c.firstname,
c.lastname,
c.image
FROM followers a
INNER JOIN donors c
ON a.following = c.user_id) don
ON f.user_id = don.user_id
WHERE f.user_id = 222
LIMIT 9;

MySQL - Query to return customer's first order

Basic schema (only relevant fields shown):
customers
id | fname | sname | email
orders
order_id | customer | level | timestamp
I need to select every customer, but along with their personal details I want to join the level of their first order.
SELECT c.fname, c.sname, c.email, o.level
FROM customers c
LEFT JOIN orders o ON c.id = o.customer
..?
Edit
I have tried two solutions so far and they completely kill my entire database. No database requests will complete after running the queries, so I'm wondering if maybe the queries are too large. There are about 10,000 orders and 15,000 customers. Is this expected? I've never had these problems before on these tables.
Well, this will work, but is not optimal:
SELECT c.fname, c.sname, c.email, o.level
FROM customers c
LEFT JOIN orders o ON c.id = o.customer
WHERE o.`timestamp` = ( SELECT MIN( o1.`timestamp` ) FROM orders o1 WHERE o1.customer = c.id )
GROUP BY c.id
Try this query -
SELECT c.fname, c.sname, c.email, o.level FROM customers c
LEFT JOIN (
SELECT o1.* FROM orders o1
JOIN (SELECT customer, MIN(timestamp) timestamp FROM orders GROUP BY customer) o2
ON o1.customer = o2.customer AND o1.timestamp = o2.timestamp) o
ON c.id = o.customer;
SELECT c.fname, c.sname, c.email, o.level
FROM customers c
LEFT JOIN orders o ON c.id = o.customer
ORDER BY o.timestamp ASC LIMIT 1
SELECT c.fname, c.sname, c.email, o.level
FROM customers c
LEFT JOIN orders o ON c.id = o.customer
ORDER BY o.timestamp
GROUP BY c.id
Subselects are definitely not the most efficient but here's a query
SELECT c.fname, c.sname, c.email, o.level,
(select level from orders
where c.id = o.customer order by timestamp asc limit 0,1) as level
FROM customers c
This will give you ALL customers (orders or not) and their corresponding level. If you performance is slow, make sure you have proper indexes too