select query using nested queries in mysql - mysql

i have 2 tables(say customer and details) containing following data. I need to print the details of the people who have invested maximum amount.
Customer:
+------+------------+--------+
| name | visited | amount |
+------+------------+--------+
| xyz | 2018-04-11 | 100 |
| xyz | 2018-04-11 | 1000 |
| abc | 2018-02-21 | 500 |
| xyz | 2018-03-11 | 700 |
| abc | 2018-01-24 | 50 |
+------+------------+--------+
Details:
+------+------------+
| name | detail |
+------+------------+
| abc | california |
| xyz | hongkong |
+------+------------+
I have found the customer who have invested the maximum amount using the query
select name,sum(amount)
from (
select name,amount
from customer
where visited >= DATE_SUB(CURDATE(),INTERVAL 8 MONTH)
) as subtable
group by name
order by amount
limit 1;
i have the following output
+------+-------------+
| name | sum(amount) |
+------+-------------+
| xyz | 1800 |
+------+-------------+
now how do i find the details of xyz from details table? I need to do this all in a single query. My output must be like.
+------+------------+
| name | detail |
+------+------------+
| xyz | hongkong |
+------+------------+
My stuck, how to proceed this?

select d.name, d.details, sum(c.amount)
from details d
join customer c on c.name = d.name
where c.visited >= DATE_SUB(CURDATE(),INTERVAL 8 MONTH)
group by d.name
order by sum(c.amount) desc
limit 1

You need to JOIN to details table. I've decided to use subquery to limit the amount of rows being looked up for the join operation.
select c.name, d.detail
from (
select
name,
sum(amount) as sum_amount
from customer
where visited >= DATE_SUB(CURDATE(),INTERVAL 8 MONTH)
group by name
order by sum_amount
limit 1
) c
left join details d on c.name = d.name
Note that I've removed your subquery as it was superfluous.

To get the following outputs you can write query with join like below :
Output:-
+------+------------+
| name | detail |
+------+------------+
| xyz | hongkong |
+------+------------+
Query:-
select d.name, d.details
from details d
join customer cust on cust.name = d.name and
cust.visited >= DATE_SUB(CURDATE(),INTERVAL 8 MONTH)
group by d.name
order by sum(cust.amount) desc
limit 1;
If you want to print sum Amount along with that then you have to just add one more column in your select statement :
Output:-
+------+------------+--------------+
| name | detail | sum(amount) |
+------+------------+--------------+
| xyz | hongkong | 1800 |
+------+------------+--------------+
Query:-
select d.name, d.details, sum(cust.amount)
from details d
join customer cust on cust.name = d.name and
cust.visited >= DATE_SUB(CURDATE(),INTERVAL 8 MONTH)
group by d.name
order by sum(cust.amount) desc
limit 1;

Related

Group the records and sort groups in itself

I have 2 columns in my pricing table: car_id and price
I want to
Group the records with same car_ids and sort these groups by price in itself. (namely, all records with the same car_id must be listed consecutively.)
I want to sort these groups by minimum price they include.
For example,
car_id | price
---------------
1 | 2
1 | 5
2 | 3
2 | 1
2 | 10
3 | 7
3 | 3
3 | 8
should be sorted like this:
car_id | price
---------------
2 | 1
2 | 3
2 | 10
1 | 2
1 | 5
3 | 3
3 | 7
3 | 8
How can I write a query that performs this operation? I am using MySQL V5.6.
You may use a subquery since the 5.6 does not support the window functions.
select *,
(
select min(price)
from datatab d2
where d1.car_id = d2.car_id
) min_price
from datatab d1
order by min_price, car_id, price
DBFIDDLE DEMO
You can use a subquery that returns the min price for each car_id in the ORDER BY clause:
select t.*
from tablename t
order by
(select min(price) from tablename where car_id = t.car_id),
car_id,
price
See the demo.
Or join the table to the subquery that returns all the min prices:
select t.*
from tablename t inner join (
select car_id, min(price) minprice
from tablename
group by car_id
) m on m.car_id = t.car_id
order by
m.minprice,
t.car_id,
t.price
See the demo.
Results:
| car_id | price |
| ------ | ----- |
| 2 | 1 |
| 2 | 3 |
| 2 | 10 |
| 1 | 2 |
| 1 | 5 |
| 3 | 3 |
| 3 | 7 |
| 3 | 8 |
You can use window functions in the order by:
select p.*
from pricing p
order by min(price) over (partition by car_id),
car_id;
The car_id handles the case when multiple car_ids have the same minimum price.
In older versions of MySQL, you can do something similar with a subquery:
select p.*
from pricing p
order by (select min(p2.price) from pricing p2 where p2.car_id = p.car_id),
car_id;

How to calculate max values of groups?

I have a table like so (I'm not sure how to format tables)
Category / Products / Purchases
1 | A | 12
1 | B | 13
1 | C | 11
2 | A | 1
2 | B | 2
2 | C | 3
Expected output:
1 | B | 13
2 | C | 3
However I keep on getting
1 | A | 13
2 | A | 3
ie. It just selects the first occurrence of the second column.
Here is my code:
SELECT Category, Products, MAX(Purchases) FROM myTable GROUP BY Category;
Use filtering in the where clause:
select t.*
from t
where t.purchases = (select max(t2.purchases) from t t2 where t2.category = t.category);
With NOT EXISTS:
select m.* from myTable m
where not exists (
select 1 from myTable
where category = m.category and purchases > m.purchases
)
See the demo.
Results:
| Category | Products | Purchases |
| -------- | -------- | --------- |
| 1 | B | 13 |
| 2 | C | 3 |
You can use row_number() to identify max purchase for each group or replace rownumber() to rank() if there are ties of max purchases for each group
Select Category, Products,
Purchases from (Select Category,
Products,
Purchases,
row_number() over (partition by
category, products order by
purchases desc) rn from table) t
where t.rn=1
)

Oldest date from most recent set of records

How do I find the oldest date from the most recent set of records in MySQL?
Consider the below
+--------+-----------+------------+
| ID | PRODUCTID | DATEACTIVE |
+--------+-----------+------------+
| 546502 | 23405 | 2017-07-20 |
| 545075 | 23405 | 2017-07-19 |
| 543651 | 23405 | 2017-07-18 |
| 456783 | 23405 | 2017-01-04 |
| 456782 | 23405 | 2017-01-03 |
| 456781 | 23405 | 2017-01-02 |
| 456780 | 23405 | 2017-01-01 |
| 65453 | 23405 | 2016-07-19 |
| 65452 | 23405 | 2016-07-18 |
+--------+-----------+------------+
I want to be able to find the most recent time a product was activated (18th July 2017)
Pls next time follow what Sloan said in the comments.
If I understood your question, you could use something like this.
The query, in the inner part, "groups" the consecutive dates, giving a consecutive number for each "group".
Then select only the "first" group (the more recent) and numbers the rows in ascending order by date.
Finally, the row with the first "row number" is selected.
SELECT *
FROM (
SELECT #rn:=#rn+1 AS RN,
A.*
FROM (
SELECT #gr:= IF(#prev_date=DATE_ADD(dateactive, INTERVAL +1 DAY), #gr, #gr+1) AS GR
,TN.*
,#prev_date:=dateactive AS PD
FROM TN
CROSS JOIN (SELECT #gr:=0, #prev_date:=(SELECT MAX(dateactive) FROM TN)) R
ORDER BY DATEACTIVE DESC
) A
CROSS JOIN (SELECT #rn:=0) R2
WHERE GR=1
ORDER BY DATEACTIVE
)B
WHERE RN=1;
Output:
RN GR id productid dateactive PD
1 1 543651 23405 18.07.2017 00:00:00 2017-07-18
SELECT MAX(DATEACTIVE) as most_recent_time
FROM `YOUR_TABLE_NAME`
WHERE PRODUCTID = 'YOUR_ID'
You could use ORDER BY to sort the data by date, See code below:
SELECT *
FROM `YOUR_TABLE_NAME`
WHERE `PRODUCTID` = 'YOUR_ID'
ORDER BY `DATEACTIVE` DESC;

MySQL Joining Tables While Counting & Grouping

I want to join 2 tables:
source_table
----------------------------------
| source_id label |
|----------------------------------|
| 1 Contact Form |
| 2 E-Mail |
| 3 Inbound Call |
| 4 Referral |
----------------------------------
related_table
---------------------------------------
| id created_at source |
|---------------------------------------|
| 1 2013-12-26 2 |
| 2 2013-12-26 2 |
| 3 2013-12-26 4 |
| 4 2013-12-25 1 |
| 5 2013-12-18 2 |
| 6 2013-12-16 4 |
| 7 2013-11-30 2 |
---------------------------------------
So that it looks like this:
---------------------------------------
| created_at source amount |
|---------------------------------------|
| 2013-12-26 E-Mail 2 |
| 2013-12-26 Referral 1 |
| 2013-12-25 Contact Form 1 |
| 2013-12-18 E-Mail 1 |
| 2013-12-16 Referral 1 |
---------------------------------------
I want to count the occurrences of each source in related_table grouped by the source for each date in the range.
But I'm not sure how to write the query.
Here's what I have so far:
SELECT DISTINCT
source_table.source_id,
source_table.label AS source,
related_table.created_at,
COUNT(*) AS amount
FROM source_table
INNER JOIN related_table
ON related_table.source=source_table.source_id AND
related_table.created_at>='2013-12-01' AND
related_table.created_at<='2013-12-31'
GROUP BY `source`
ORDER BY `created_at` ASC
I'm not very good with SQL, so the above query might be far off from what I need to have. All I know is that it doesn't work as expected.
My implementation:
select created_at, s.label, amount
from
(
select count(r.Source) as amount, r.source, r.created_at
from related_table r
group by r.source, r.created_at) a inner join source_table s
on a.source = s.source_id
where created_at between '2013-12-01' and '2013-12-31'
order by amount desc, created_at desc
http://sqlfiddle.com/#!2/841bd/2
adjusted demo to your example...
SELECT
created_at
,label as source
,COUNT(*) AS amount
FROM source_table
INNER JOIN related_table
ON source_table.source_id = related_table.source
GROUP BY label, created_at
ORDER BY created_at DESC

Get the balance of my users in the same table

Help please, I have a table like this:
| ID | userId | amount | type |
-------------------------------------
| 1 | 10 | 10 | expense |
| 2 | 10 | 22 | income |
| 3 | 3 | 25 | expense |
| 4 | 3 | 40 | expense |
| 5 | 3 | 63 | income |
I'm looking for a way to use one query and retrive the balance of each user.
The hard part comes when the amounts has to be added on expenses and substracted on incomes.
This would be the result table:
| userId | balance |
--------------------
| 10 | 12 |
| 3 | -2 |
You need to get each totals of income and expense using subquery then later on join them so you can subtract expense from income
SELECT a.UserID,
(b.totalIncome - a.totalExpense) `balance`
FROM
(
SELECT userID, SUM(amount) totalExpense
FROM myTable
WHERE type = 'expense'
GROUP BY userID
) a INNER JOIN
(
SELECT userID, SUM(amount) totalIncome
FROM myTable
WHERE type = 'income'
GROUP BY userID
) b on a.userID = b.userid
SQLFiddle Demo
This is easiest to do with a single group by:
select user_id,
sum(case when type = 'income' then amount else - amount end) as balance
from t
group by user_id
You could have 2 sub-queries, each grouped by id: one sums the incomes, the other the expenses. Then you could join these together, so that each row had an id, the sum of the expenses and the sum of the income(s), from which you can easily compute the balance.