Select count date from multiple where conditions for each day - mysql

I have lot of coupons. I would like to learn for each day how many coupons from each campaign have been received by users. But I cannot write something like assigned_date for each select row
SELECT count(id) as number_of_coupons,
DATE_FORMAT(assigned_date, '%d-%m-%Y') as date,
(SELECT COUNT(id) FROM coupon WHERE campaign_id = 1 AND assigned_date=THIS MUST BE SOMETHING) as campaign_1,
(SELECT COUNT(id) FROM coupon WHERE campaign_id = 2 AND assigned_date=THIS MUST BE SOMETHING) as campaign_2
FROM coupon
GROUP BY DATE_FORMAT(assigned_date, '%d-%m-%Y')
order by STR_TO_DATE(date, '%d-%m-%Y') DESC
So the result will be something like. How can I achieve this result?
+-------------------+------------+-------------+-----------+
| number of coupons | date | campaign_1 | campaign2 |
+-------------------+------------+-------------+-----------+
| 156 | 12-10-2019 | 6980 | 100 |
| 177 | 11-10-2019 | 6980 | 100 |
| 44 | 10-10-2019 | 6980 | 100 |
| 94 | 09-10-2019 | 6980 | 100 |
| 93 | 08-10-2019 | 6980 | 100 |
+-------------------+------------+-------------+-----------+

Not knowing what your data structure looks like, I can only speculate on what the solution should be. However, my guess is that a query such as the following is what you want:
SELECT COUNT(DISTINCT cv.id) as number_of_coupons,
DATE_FORMAT(cv.assigned_date, '%d-%m-%Y') as date,
SUM(CASE WHEN c.campaign_id = 1 THEN 1 ELSE 0 END) as campaign_1,
SUM(CASE WHEN c.campaign_id = 2 THEN 1 ELSE 0 END) as campaign_2
FROM coupon_vault cv LEFT JOIN
coupons c
ON cv.coupon_id = c.coupon_id
GROUP BY DATE_FORMAT(cv.assigned_date, '%d-%m-%Y')
ORDER BY MIN(cv.assigned_date);
It is quite possible that the COUNT(DISTINCT) is unnecessary and COUNT() would suffice.
Both Postgres and MySQL (your original tag) have reasonable alternatives for the SUM(CASE . . .). However, you have not specified your database, so I'm sticking with the code that works in both databases.

Related

Joining 2 SQL SELECT result into one query

I wanted to know if there's a way to join two or more result sets into one.
i have the following two queries
First query:
SELECT
CONCAT(day(db.prod_id.created_on),"-",month(db.prod_id.created_on),"-",year(db.prod_id.created_on)) as day_month_year,
db.country.country ,
count(concat(day(db.prod_id.created_on),"-",month(db.prod_id.created_on),"-",year(db.prod_id.created_on))) as count ,
COUNT(DISTINCT db.prod_id.email) AS MAIL
from db.prod_id
left join db.country on db.prod_id.branch_id = db.country.id
where db.prod_id.created_on > '2020-11-17' and (db.country.type = 1 or db.country.type = 2)
group by
concat(day(db.prod_id.created_on),"-",month(db.prod_id.created_on),"-",year(db.prod_id.created_on)),
db.country.country
order by db.prod_id.created_on
The second query:
select
CONCAT(day(db.prod_id.created_on),"-",month(db.prod_id.created_on),"-",year(db.prod_id.created_on)) as day_month_year,
db.country.country,
count(CONCAT(day(db.prod_id.created_on),"-",month(db.prod_id.created_on),"-",year(db.prod_id.created_on))) as count_BUY
from db.prod_id
left join db.prod_evaluations on db.prod_id.id = db.prod_evaluations.id
left join db.country on db.prod_id.branch_id = db.country.id
left join (Select prod_properties.prod_id, prod_properties.value From prod_properties Where prod_properties.property_id = 5) as db3 on db3.prod_id = db.prod_id.id
where db.prod_id.created_on > '2020-11-17'
and db3.value = 'online-buy' and db.prod_id.status_id <> 25
group by
concat(day(db.prod_id.created_on),"-",month(db.prod_id.created_on),"-",year(db.prod_id.created_on)),
db.country.country
order by db.prod_id.created_on
The first query give the following result:
+------------+---------+-------+------+
| day | Country | Count | Mail |
+------------+---------+-------+------+
| 17-11-2020 | IT | 200 | 100 |
| 17-11-2020 | US | 250 | 100 |
| 18-11-2020 | IT | 350 | 300 |
| 18-11-2020 | US | 200 | 100 |
+------------+---------+-------+------+
The second query give:
+------------+---------+-----------+
| day | Country | Count_BUY |
+------------+---------+-----------+
| 17-11-2020 | IT | 50 |
| 17-11-2020 | US | 70 |
| 18-11-2020 | IT | 200 |
| 18-11-2020 | US | 50 |
+------------+---------+-----------+
Now i want to merge these two result in one:
+------------+---------+-------+------+-----------+
| day | Country | Count | Mail | Count_BUY |
+------------+---------+-------+------+-----------+
| 17-11-2020 | IT | 200 | 100 | 50 |
| 17-11-2020 | US | 250 | 100 | 70 |
| 18-11-2020 | IT | 350 | 300 | 200 |
| 18-11-2020 | US | 200 | 100 | 50 |
+------------+---------+-------+------+-----------+
How can i perform this query?
I'm using mysql
Thanks
The simple way: You can join queries.
select *
from ( <your first query here> ) first_query
join ( <your second query here> ) second_query using (day_month_year, country)
order by day_month_year, country;
This is an inner join. You can also outer join of course. MySQL doesn't support full outer joins, though. If you want that, you'll have to look up how to emulate a full outer join in MySQL.
The hard way ;-) Merge the queries.
If I am not mistaken, your two queries can be reduced to
select
date(created_on),
branch_id as country,
count(*) as count_products,
count(distinct p.email) as count_emails
from db.prod_id
where created_on >= date '2020-11-17'
and branch_id in (select country from db.country where type in (1, 2))
group by date(created_on), branch_id
order by date(created_on), branch_id;
and
select
date(created_on),
branch_id as country,
count(*) as count_buy
from db.prod_id
where created_on >= date '2020-11-17'
and status_id <> 25
and prod_id in (select prod_id from prod_properties where property_id = 5 and status_id <> 25)
group by date(created_on), branch_id
order by date(created_on), branch_id;
The two combined should be
select
date(created_on),
branch_id as country,
sum(branch_id in (select country from db.country where type in (1, 2)) as count_products,
count(distinct case when branch_id in (select country from db.country where type in (1, 2) then p.email end) as count_emails,
sum(status_id <> 25 and prod_id in (select prod_id from prod_properties where property_id = 5 and status_id <> 25)) as count_buy
from db.prod_id
where created_on >= date '2020-11-17'
group by date(created_on), branch_id
order by date(created_on), branch_id;
You see, the conditions the queries have in common remain in the where clause and the other conditions go inside the aggregation functions.
sum(boolean) is short for sum(case when boolean then 1 else 0 end), i.e. this counts the rows where the condition is met in MySQL.

select query using nested queries in 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;

MySQL Count if the value has not appeared before

here is a table of users, carts, and time.
A user can have multiple carts. All anonymous users have the userId = 1000; any identified user has an ID different from 1000.
All cartIds are unique.
+------------+-------------+----------------------+
| userId | cartId | time |
+------------+-------------+----------------------+
| 7650 | 231 | 2014-08-27 13:41:02 |
+------------+-------------+----------------------+
| 7632 | 221 | 2014-08-27 13:42:02 |
+------------+-------------+----------------------+
| 7650 | 289 | 2014-08-27 14:13:02 |
+------------+-------------+----------------------+
| 1000 | 321 | 2014-08-27 14:41:02 |
+------------+-------------+----------------------+
| 7650 | 500 | 2014-08-27 17:41:02 |
I am interested in calculating the number of distinct identified users by the hour of the day.
I tried the following, but it cannot keep a record of all the IDs entered before when I group by them up by Hour(Date).
COUNT( distinct (case when userId <> 1000 then userId end)) as numSELFIDUsers
For the output, I want something like:
+------------+-------------+----------------------+
| Date | HourOfDay | numSELFIDUsers |
+------------+-------------+----------------------+
| 2014-08-27 | 13 | 2 |
+------------+-------------+----------------------+
| 2014-08-27 | 14 | 0 |
+------------+-------------+----------------------+
| 2014-08-27 | 17 | 0 |
+------------+-------------+----------------------+
Please let me know if there are any questions.
Thanks in advance for the help.
I think you want something like this:
select date(time), hour(time),
COUNT(distinct case when userId <> 1000 then userId end) as numSELFIDUsers
from usercarts
where date(time) = '2014-08-27'
group by date(time), hour(time)
order by 1, 2;
This looks similar to what you have in the query. I'm not sure why your version wouldn't work.
EDIT:
You also seem to want times with 0 counts. This is a bit more challenging, but you can do it like this:
select d.dt, h.hr, COUNT(distinct case when userId <> 1000 then userId end)
from (select distinct date(time) dt from usercarts where dt IN YOUR RANGE) d cross join
(select 0 as hr union all select 1 union all select 2 union all select 3 union all . . .
select 23
) h left join
usercart uc
on date(uc.time) = d.dt and hour(uc.time) = h.hr;
The . . . is where you put in the rest of the numbers from 3 to 23.
EDIT II:
I suspect that you are actually looking for the first time a user appears. If so, try this:
select date(firsttime), hour(firsttime), count(*) as NumFirstUsers
from (select userId, min(time) as firsttime
from usercarts
where userid <> 1000
group by userId
) u
group by date(firsttime), hour(firsttime)
order by 1, 2;

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.

Mysql join query

I'm using two tables in the database. These tables look like this:
Table A:
id | date
----------------------
12001 | 2011-01-01
13567 | 2011-01-04
13567 | 2011-01-04
11546 | 2011-01-07
13567 | 2011-01-07
18000 | 2011-01-08
Table B:
user | date | amount
----------------------------------
15467 | 2011-01-04 | 140
14568 | 2011-01-04 | 120
14563 | 2011-01-05 | 140
12341 | 2011-01-07 | 140
18000 | 2011-01-08 | 120
I need a query that will join these the two tables.
The first query should result in a total number of users from table A group by date and the number of unique users from table A grouped by date. That query looks like:
SELECT COUNT(DISTINCT id) AS uniq, COUNT(*) AS total, format_date(date, '%Y-%m-%d') as date FROM A GROUP BY date
From the second table I need the sum of the amounts grouped by dates.
That query looks like:
SELECT SUM(amount) AS total_amount FROM B GROUP BY DATE_FORMAT( date, '%Y-%m-%d' )
What I want to do is to merge these two queries into one on column "date", and that as a result I get the following list:
date | unique | total | amount
-----------------------------------------------
2011-01-01 | 1 | 1 | 0
2011-01-04 | 1 | 2 | 260
2011-01-05 | 0 | 0 | 140
2011-01-07 | 2 | 2 | 140
2011-01-08 | 1 | 1 | 120
How can I do that using one query?
Thanks for all suggestions.
select date_format(a.date, '%Y-%m-%d') as date, a.uniq, a.total, ifnull(b.amount, 0) as amount
from (
select count(distinct id) as uniq, count(*) as total, date
from tablea
group by date
) a
left join (
select sum(amount) as amount, date
from tableb
group by date
) b on a.date = b.date
order by a.date
I assume that field date is a datetime type. It's better to format output fields in final result set (date field in this case).
Your queries are fine everything they need is a join.