I am trying to get total amount of orders, and the amount of unique customers in a 30 day timeframe.
I have this table:
order_id, order_date, order_country, customer_id.
(1, '2021/03/04', 'SWE', 1),
(2, '2021/03/01', 'SWE', 1),
(3, '2021/03/01', 'DK', 3),
(4, '2021/03/01', 'DK', 3),
(5, '2021/03/03', 'NOR', 2),
(6, '2021/02/27', 'DK', 3),
(7, '2020/12/30', 'Ger', 4);
I Have tried something like this:
SELECT order_date
, COUNT(order_id) as orderAmount
, COUNT(distinct customer_id) as customerAmount
FROM orders
WHERE order_date BETWEEN NOW() - INTERVAL 30 DAY AND NOW()
GROUP
BY order_date
This gets the total amount of orders per date which is what i want. But it only counts unique customers per date and not monthly, so the same customer comes up multiple times. I have tried small variations of this sql script but can't quite get it to work.
Please check whether it's your desired answer or not.
Schema (MySQL v5.5)
create table orders (order_id int, order_date date, order_country varchar(10), customer_id int);
insert into orders values(1, '2021/03/04', 'SWE', 1);
insert into orders values(2, '2021/03/01', 'SWE', 1);
insert into orders values(3, '2021/03/01', 'DK', 3);
insert into orders values(4, '2021/03/01', 'DK', 3);
insert into orders values(5, '2021/03/03', 'NOR', 2);
insert into orders values(6, '2021/02/27', 'DK', 3);
insert into orders values(7, '2020/12/30', 'Ger', 4);
Query #1
SELECT
o.order_date,COUNT(order_id) as orderAmount
, coalesce(max(Customer_Count),0) as customerAmount
FROM orders o left join (select order_date,count(distinct customer_id)Customer_Count from orders o where
not exists (select customer_id from orders oa where o.customer_id=oa.customer_id and oa.order_date<o.order_date
and oa.order_date > now() - INTERVAL 30 DAY )
group by order_date) oc on o.order_date=oc.order_date
WHERE o.order_date BETWEEN NOW() - INTERVAL 30 DAY AND NOW()
group by o.order_date
order by o.order_date;
order_date
orderAmount
customerAmount
2021-02-27
1
1
2021-03-01
3
1
2021-03-03
1
1
2021-03-04
1
0
View on DB Fiddle
Just count the first time a customer appears:
SELECT order_date, COUNT(*) as num_orders,
COUNT(DISTINCT customer_id) as distinct_customers_on_day,
SUM(SUM(seqnum = 1)) OVER (ORDER BY order_date) as running_distinct_customers
FROM (SELECT o.*,
ROW_NUMBER() OVER (PARTITION BY customer_id ORDER BY order_date) as seqnum
FROM orders o
WHERE o.order_date BETWEEN NOW() - INTERVAL 30 DAY AND NOW()
) o
GROUP BY order_date;
Here is a db<>fiddle.
Related
I have a table like this:
CREATE TABLE test (
ID SERIAL PRIMARY KEY,
user_id INT,
createdAt DATE,
status_id INT
);
INSERT INTO test VALUES
(1, 12, '2020-01-01', 4),
(2, 12, '2020-01-03', 7),
(3, 12, '2020-01-06', 7),
(4, 13, '2020-01-02', 5),
(5, 13, '2020-01-03', 6),
(6, 14, '2020-03-03', 8),
(7, 13, '2020-03-04', 4),
(8, 15, '2020-04-04', 7),
(9, 14, '2020-03-02', 6),
(10, 14, '2020-03-10', 5),
(11, 13, '2020-04-10', 8);
this is my fiddle
In that table there's id for the id of each transaction, user_id was the user, createdAt was the date of transaction happen, and status_id was the status for every transaction (in this case status_id 4, 5, 6, 8 are approved transaction)
I want to find out max, min, avg different day for every each transaction on every users who doing transaction between '2020-02-01' and '2020-04-01' with >1 transaction approved on that period
This is my query:
SELECT MIN(diff) AS `MIN`, MAX(diff) AS `MAX`, SUM(diff) / COUNT(DISTINCT user_id) AS `AVG`
FROM (
SELECT ID, user_id, DATEDIFF((SELECT t2.createdAt FROM test t2 WHERE t2.user_id = t1.user_id AND t1.createdAt <= t2.createdAt AND t2.id <> t1.id LIMIT 1), t1.createdAt) AS diff
FROM test t1
where
status_id in (4, 5, 6, 8)
HAVING SUM(t1.user_id BETWEEN '2020-02-01' AND '2020-04-01')
AND SUM(t1.user_id >= '2020-02-01') > 1
) DiffTable
WHERE diff IS NOT NULL
but it's said:
In aggregated query without GROUP BY, expression #1 of SELECT list contains nonaggregated column 'fiddle_KDQIQDMUZEIOVXFHRZPY.t1.ID'; this is incompatible with sql_mode=only_full_group_by
what should i do?
this is my fiddle
expected result
+-----+-----+---------+
| MAX | MIN | AVERAGE |
+-----+-----+---------+
| 36 | 1 | 22 |
+-----+-----+---------+
explanation :
- the user_id who have approval transaction on 2020-02-01 until 2020-04-01 and user_id who have transaction more than 1 are user_id 13 & 14
- the maximum of different day on 2020-02-01 until 2020-04-01 are user_id 13 which the different day for each transaction happen in 2020-03-04 and doing next transaction again in 2020-04-10
- the minimum day of different day of each transaction are user_id 14 who doing transaction on 2020-03-02 and next transaction 2020-03-03
- average are 22 days (sum of different day on user_Id 13 & 14 / amount of user_id who fit on this condition)
You need to do the GROUPing outside of your subquery; the subquery should just be used to limit the selected transactions to those which have the desired status_id value and dates within the required range. Then you can select users with more than one transaction in the period in the outer query:
SELECT user_id,
COUNT(*) AS transactions,
MIN(diff) AS `MIN`,
MAX(diff) AS `MAX`,
SUM(diff) / COUNT(diff) AS `AVG`
FROM (
SELECT user_id, DATEDIFF((SELECT MIN(t2.createdAt)
FROM test t2
WHERE t2.user_id = t1.user_id
AND t1.createdAt < t2.createdAt
AND t2.status_id in (4, 5, 6, 8)
), t1.createdAt) AS diff
FROM test t1
WHERE status_id in (4, 5, 6, 8)
AND createdAt BETWEEN '2020-02-01' AND '2020-04-01'
) DiffTable
WHERE diff IS NOT NULL
GROUP BY user_id
HAVING COUNT(*) > 1
Output (for your fiddle):
user_id transactions MIN MAX AVG
14 2 1 7 4.0000
Demo on dbfiddle
If you want the values based on all transactions that occurred over that period, rather than by user_id, you can simply remove the GROUP BY and HAVING clauses:
SELECT COUNT(*) AS transactions,
MIN(diff) AS `MIN`,
MAX(diff) AS `MAX`,
SUM(diff) / COUNT(diff) AS `AVG`
FROM (
SELECT user_id, DATEDIFF((SELECT MIN(t2.createdAt)
FROM test t2
WHERE t2.user_id = t1.user_id
AND t1.createdAt < t2.createdAt
AND t2.status_id in (4, 5, 6, 8)
), t1.createdAt) AS diff
FROM test t1
WHERE status_id in (4, 5, 6, 8)
AND createdAt BETWEEN '2020-02-01' AND '2020-04-01'
) DiffTable
WHERE diff IS NOT NULL
Output:
transactions MIN MAX AVG
3 1 37 15.0000
Demo on dbfiddle
Note that there a couple of issues with the existing subquery in the DATEDIFF computation: a LIMIT without ORDER BY is not guaranteed to give the expected results, and there is no conditioning on status_id. I've fixed both those issues in my updated queries.
I am trying to make a graph of the sum of one MySQL column over the past few months. Right now I have the SQL statement:
SELECT SUM(Distance) AS `Data`, MONTH(`Date`) AS `DateVal`
FROM `Activities`
WHERE Date > ADDDATE(NOW(), INTERVAL -10 MONTH) GROUP BY YEAR(`Date`),
MONTH(`Date`) ORDER BY Date DESC LIMIT 10
This SQL statement does not work because it only pulls out the months that have data. Is there a way for the SQL server to return 0 for months that have no data?
The only I could find to do this would be to use 10 different queries, is there a better way?
Since it is possible that some months have no dates, you have to get the list of months from somewhere: To do that, you need a table with the list of months, and then join that table in your query. This way, all months with no data will have 0 as value.
CREATE TABLE months (
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
`name` VARCHAR(10) NOT NULL
)
INSERT INTO months VALUES
(1, 'january'), (2, 'february'), (3, 'march'),
(4, 'april'), (5, 'may'), (6, 'june'),
(7, 'july'), (8, 'august'), (9, 'september'),
(10, 'october'), (11, 'november'), (12, 'december');
SELECT SUM(Distance) AS `Data`, MONTH(`Date`) AS `DateVal`
FROM `Activities` JOIN months on MONTH(`Date`) = months.`id`
WHERE Date > ADDDATE(NOW(), INTERVAL -10 MONTH) GROUP BY YEAR(`Date`),
MONTH(`Date`) ORDER BY Date DESC LIMIT 10
I have table like events(id, date, event_name, user_id) and records like
(1, '2013-12-01 08:30:00', 'login', 9),
(2, '2013-12-01 08:33:00', 'login', 4),
(3, '2013-12-01 10:22:00', 'logout', 9),
(4, '2013-12-02 08:34:00', 'login', 9),
...
I need to fetch every first event for each (or specified) user in a day for every days in a month.
for ex:
2013-12-01 08:30:00, login, 9
2013-12-02 08:34:00, login, 0
...
Help me to construct properly SQL query
SELECT events.*
FROM
events INNER JOIN (SELECT user_id,
DATE(`date`) as day_event,
MIN(`date`) AS first_event
FROM events
GROUP BY
user_id,
DATE(`date`)) fe
ON events.user_id = fe.user_id
AND events.`date` = fe.first_event
Please see fiddle here.
If you only want to return a month, you can put your condition in your subquery:
(SELECT user_id,
DATE(`date`) as day_event,
MIN(`date`) AS first_event
FROM events
WHERE `date` >= '2013-12-01' AND `date` < '2014-01-01'
GROUP BY
user_id,
DATE(`date`)) fe
Try this---
SELECT *
FROM events
WHERE id IN (SELECT min(id)
FROM events
GROUP BY user_id, date)
I have a table like
CREATE TABLE sales
(`id` int, `date` date, `amount` int(4))
;
insert into sales values (1, '2012-09-01', 200),
(2, '2012-09-01', 300),
(3, '2012-09-02', 400),
(4, '2012-09-02', 500),
(5, '2012-09-02', 600)
I wish to retrieve a row showing the sales for today, and the sales for yesterday
like
Date Today Total sales Yesterday Sales
2012-09-02 1500 500
Tried using something like
SELECT id, date, sum(amount) FROM sales
GROUP BY date;
But it returns the sales day wise. I understand that can be done programmatically, but is there a better way to directly retrieve it from the DB?
sqlfiddle
SELECT id, date(now()) as `date`,
SUM(IF(date(`date`) = date(now()), `amount`, 0)) as TodayTotalSales,
SUM(IF(date(`date`) < date(now()), `amount`, 0)) as OtherDaySales
FROM sales;
http://sqlfiddle.com/#!2/0ef6a/18
You are getting that because Id is different for each record. You have two option now:
Don't retrieve Id and write query like:
SELECT date, sum(amount) FROM sales
GROUP BY date;
Use a join with subquery
SELECT a.ID, a.date, b.amount
FROM sales a, (SELECT date, sum(amount) amount FROM sales
GROUP BY date) b
WHERE a.date = b.date;
Please Note: In option 2, second and third columns will be repeating with same value for each id within a day.
SELECT date, sum(amount), yestersales
FROM sales AS s1,
(SELECT sum(amount) as yestersales, ADDDATE(date, 1) AS yesterdate
FROM sales GROUP BY date) AS s2
WHERE s1.date = s2.yesterdate
GROUP BY date;
Will do what you want, but it's not really very efficient, I don't think. I would personally do it in code.
Selecting the ID doesn't really make much sense here since you're grouping by date.
I want to select all my order values per month. I know this works fine with GROUP BY month but only with months with orders in it. Now I want also the months with no orders so I get all months.
This is my query:
SELECT SUM(VerkoopfactBedrag) AS bedrag, DATE_FORMAT(VerkoopfactDatum,'%M') AS date
FROM verkoopfacturen
WHERE Verkoopfact_UserId = 12
AND VerkoopfactDatum BETWEEN '2011-01-01' AND '2011-12-30'
GROUP BY MONTH(VerkoopfactDatum)
So when the result of a month is 0 I want to see the month with value 0 but now the month don't show up.
Is this possible?
One way to do this is to create and populate a table full of consecutive months.
You can then OUTER JOIN using that table.
So something like:
drop table if exists all_months;
create table all_months (a_month_id int unsigned PRIMARY KEY,a_month varchar(20) NOT NULL, UNIQUE KEY `all_months_uidx1` (a_month));
insert into all_months values (1,'January');
insert into all_months values (2,'February');
insert into all_months values (3,'March');
insert into all_months values (4,'April');
insert into all_months values (5,'May');
insert into all_months values (6,'June');
insert into all_months values (7,'July');
insert into all_months values (8,'August');
insert into all_months values (9,'September');
insert into all_months values (10,'October');
insert into all_months values (11,'November');
insert into all_months values (12,'December');
SELECT SUM(IFNULL(t1.VerkoopfactBedrag,0)) AS bedrag,
am.a_month AS date
from
(
select
ifnull(vn.VerkoopfactBedrag,0) as VerkoopfactBedrag,
cast(DATE_FORMAT(VerkoopfactDatum, '%M') as char) as mdate
FROM verkoopfacturen vn
WHERE Verkoopfact_UserId = 12
AND VerkoopfactDatum BETWEEN '2011-01-01' AND '2011-12-31'
GROUP BY DATE_FORMAT(VerkoopfactDatum, '%M')
) t1 RIGHT OUTER JOIN all_months am on t1.mdate = am.a_month
group by am.a_month
order by a_month_id asc;
PS Not sure if you have anything against Oudejaarsavond but there are 31 days in December ;-)
After searching for a simple solution i finally found this which I think is SIMPLE. This will show last year and this years sales side by side.
select
date_format(current_date - INTERVAL 1 YEAR,'%Y') as LAST_YR,
date_format(NOW(),'%Y') as THIS_YR,
monthname(date) as month,
sum(case when year(date) = date_format(current_date - INTERVAL 1 YEAR,'%Y') then amount else 0 end) as sales_ly,
sum(case when year(date) = DATE_FORMAT(NOW(),'%Y') then amount else 0 end) as sales_ty
from tablename
where date between date_format(current_date - INTERVAL 1 YEAR,'%Y-%01-%01')
and date_format(current_date, '%Y-%12-%31')
group by monthname(date)
order by max(month(date));