I have two tables
Account table
id | account_no
-----------------------
1 | 111
2 | 222
Account details
id | act_id (fk) | amount | created_dt_ | created_by
------------------------------------------------
1 | 1 | 10 | 2022-10-30 | SYSTEM
2 | 1 | 100 | 2022-11-05 | user1
3 | 1 | 144 | 2022-11-10 | user2
4 | 1 | 156 | 2022-11-16 | user3
5 | 2 | 50 | 2022-11-05 | SYSTEM
6 | 2 | 51 | 2022-11-10 | user2
7 | 3 | 156 | 2022-11-16 | SYSTEM
I need a query to fetch only rows from account details which has at least 2 records for an account id, and merge those rows to a single row showcasing the initial amount and user who created it and the last amount and who created it, something like this
act_id | ini_amt | ini_dt | ini_usr | fnl_amt | fnl_dt | fnl_usr
-------------------------------------------------------------------------------------
1 | 10 | 2022-10-30 | SYSTEM | 156 | 2022-11-16 | user3
2 | 50 | 2022-11-05 | SYSTEM | 51 | 2022-11-10 | user2
we need only the rows with more than one records. How do i fetch that?
In MySQL 8 you could do it like this.
If you need also information fom account, you simle can join it
CREATE TABLE Account
(`id` int, `account_no` int)
;
INSERT INTO Account
(`id`, `account_no`)
VALUES
(1, 111),
(2, 222)
;
Records: 2 Duplicates: 0 Warnings: 0
CREATE TABLE Account_details
(`id` int, `act_id` int, `amount` int, `created_dt_` varchar(10), `created_by` varchar(6))
;
INSERT INTO Account_details
(`id`, `act_id`, `amount`, `created_dt_`, `created_by`)
VALUES
(1, 1, 10, '2022-10-30', 'SYSTEM'),
(2, 1, 100, '2022-11-05', 'user1'),
(3, 1, 144, '2022-11-10', 'user2'),
(4, 1, 156, '2022-11-16', 'user3'),
(5, 2, 50, '2022-11-05', 'SYSTEM'),
(6, 2, 51, '2022-11-10', 'user2'),
(7, 3, 156, '2022-11-16', 'SYSTEM')
;
Records: 7 Duplicates: 0 Warnings: 0
WITH CTE_MIN as(
SELECT
`act_id`, `amount`, `created_dt_`, `created_by`,
ROW_NUMBER() OVER(PARTITION BY `act_id` ORDER BY `created_dt_` ASC,`id` ASC) rn
FROM Account_details),
CTE_MAX as(
SELECT
`act_id`, `amount`, `created_dt_`, `created_by`,
ROW_NUMBER() OVER(PARTITION BY `act_id` ORDER BY `created_dt_` DESC,`id` DESC) rn
FROM Account_details)
SELECT
mi.`act_id`, mi.`amount`, mi.`created_dt_`, mi.`created_by`, ma.`amount`, ma.`created_dt_`, ma.`created_by`
FROM
CTE_MIN mi JOIN CTE_MAX ma
ON mi.`act_id` = ma.`act_id`
AND mi.rn = ma.rn
AND mi.created_dt_!=ma.created_dt_
AND ma.rn = 1 ANd mi.rn = 1
act_id
amount
created_dt_
created_by
amount
created_dt_
created_by
1
10
2022-10-30
SYSTEM
156
2022-11-16
user3
2
50
2022-11-05
SYSTEM
51
2022-11-10
user2
fiddle
We can do this without CTEs, using window functions and conditional aggregation:
select act_id,
max(case when rn_asc = 1 then amount end) ini_amount,
max(case when rn_asc = 1 then created_dt end) ini_created_dt,
max(case when rn_asc = 1 then created_by end) ini_created_by,
max(case when rn_desc = 1 then amount end) fnl_amount,
max(case when rn_desc = 1 then created_dt end) fnl_created_dt,
max(case when rn_desc = 1 then created_by end) fnl_created_by
from(
select ad.*,
row_number() over(partition by act_id order by created_dt ) rn_asc,
row_number() over(partition by act_id order by created_dt desc) rn_desc,
count(*) over(partition by act_id) cnt
from account_details ad
) ad
where 1 in (rn_asc, rn_desc) and cnt > 1
group by act_id
In the subquery, row_number ranks records of the same account by ascending and descending date, while count checks how many records the account has.
Then, the outer query filters on accounts that have more than one record, and on the top/bottom record. We can then pivot the dataset with group by and conditional expressions to produce the expected result.
On older MySQL version which doesn't support windows functions:
select act_id,
max(case when new_col='min_value' then amount end) as ini_amt,
max(case when new_col='min_value' then created_dt end) as ini_dt,
max(case when new_col='min_value' then created_by end) as ini_usr,
max(case when new_col='max_value' then amount end) as fnl_amt,
max(case when new_col='max_value' then created_dt end) as fnl_dt,
max(case when new_col='max_value' then created_by end) as fnl_usr
from (
select ad.id,ad.act_id,ad.amount,ad.created_dt,ad.created_by,'max_value' as new_col
from AccountDetails ad
inner join (select act_id,max(created_dt) as max_created_dt
from AccountDetails
group by act_id
having count(*) >=2
) as max_val on max_val.act_id =ad.act_id and max_val.max_created_dt=ad.created_dt
union
select ad1.id,ad1.act_id,ad1.amount,ad1.created_dt,ad1.created_by,'min_value'
from AccountDetails ad1
inner join (select act_id,min(created_dt) as min_created_dt
from AccountDetails
group by act_id
having count(*) >=2
) as min_val on min_val.act_id =ad1.act_id and min_val.min_created_dt=ad1.created_dt
) as tbl
group by act_id;
https://dbfiddle.uk/q2Oxq0Ay
Related
After the last update of Mailster (email marketing plugin for wordpress), they have changed the way they store the information about opens, clicks, unsubscribes...
Until now, everything was stored in two databases:
bao_posts: Like any other wordpress post, the information of the
email that is sent was there. (When the post_type = 'newsletter')
bao_mailster_actions: This is where the user's actions with the
email were stored. 1 when it was sent to a person, 2 when they
opened it, 3 when they clicked on it and 4 when they unsubscribed.
And with this query, I could get a table with all the emails and the information of their openings, clicks, unsubscribed...
SELECT bao_posts.post_modified,
bao_posts.ID,
bao_posts.post_title,
COUNT(CASE WHEN bao_mailster_actions.type = 1 then 1 ELSE NULL END) AS Number_People_Reached,
COUNT(CASE WHEN bao_mailster_actions.type = 2 then 1 ELSE NULL END) AS Opens,
COUNT(CASE WHEN bao_mailster_actions.type = 3 then 1 ELSE NULL END) AS Clicks,
COUNT(CASE WHEN bao_mailster_actions.type = 4 then 1 ELSE NULL END) AS Unsubs
FROM bao_posts
LEFT JOIN bao_mailster_actions ON bao_mailster_actions.campaign_id = bao_posts.ID
WHERE bao_posts.post_type = 'newsletter'
GROUP BY bao_posts.ID ;
*Expected result of this query at the end of the post.
Now the problem is that this setting is kept for emails before the update, but it has changed for new ones and now bao_mailster_actions is separated into:
bao_mailster_action_sent
bao_mailster_action_opens
bao_mailster_action_clicks
bao_mailster_action_unsubscribes
I know how to get the count of each of these tables like this:
SELECT bao_mailster_action_sent.campaign_id,
COUNT(bao_mailster_action_sent.count) AS Number_People_Reached
FROM bao_mailster_action_sent
GROUP BY bao_mailster_action_sent.campaign_id;
To get:
campaign_id
Number_People_Reached
9785
300
9786
305
(And so on with each of these 4 new tables).
So what I would like to do would be to join these 4 new queries to the original one. I've been trying to combine different JOINs, but I don't quite understand how to do it.
*Bearing in mind that if an email ID matches in both, I would need it to add up their clicks, opens (or whatever).
The expected outcome would be something like this (the same as the first query but with the aggregate data):
post_modified
ID
post_title
Number_People_Reached
Opens
Clicks
Unsubs
2021-04-29 13:13:03
9785
Prueba email
300
102
30
1
2021-04-30 15:12:01
9786
Segundo email
305
97
56
0
Thanks in advance!
I suggest that you use UNION ALL to join all the tables in a CTE.You can then use this in your query. I have modified the name because we cannot have to records with the same name.
> create table if not exists bao_mailster_action_sent
( campaign_id int,count int);
create table if not exists bao_mailster_action_opens
( campaign_id int,count int);
create table if not exists bao_mailster_action_clicks
( campaign_id int,count int);
create table if not exists bao_mailster_action_unsubscribes
( campaign_id int,count int);
CREATE TABLE if not exists bao_posts(
post_modified date,
ID int,
post_title varchar(50) );
insert into bao_mailster_action_sent values
(1,88),(2,4),(4,6);
insert into bao_mailster_action_opens values
(2,4),(3,5),(4,10);
insert into bao_mailster_action_clicks values
(1,3),(2,3),(4,6);
insert into bao_mailster_action_unsubscribes values
(1,4),(3,5),(4,5);
INSERT INTO bao_posts values
( '2021-03-01',1,'first post'),
( '2021-06-01',2,'second opion'),
( '2021-09-01',3,'third way'),
( '2021-12-01',4,'last post');
WITH bao_mailster_actionsent AS
( SELECT campaign_id,count, 1 type FROM
bao_mailster_action_sent
UNION ALL
SELECT campaign_id,count,2 FROM
bao_mailster_action_opens
UNION ALL
SELECT campaign_id,count,3 FROM
bao_mailster_action_clicks
UNION ALL
SELECT campaign_id,count,4 FROM
bao_mailster_action_unsubscribes)
SELECT bao_mailster_actionsent.campaign_id,
COUNT(bao_mailster_actionsent.count) AS TotalCount,
SUM(bao_mailster_actionsent.count) AS TotalNumber,
'type'
FROM bao_mailster_actionsent
GROUP BY bao_mailster_actionsent.campaign_id,'type' ;
WITH baoMailsterAction AS
( SELECT campaign_id,count, 1 type FROM
bao_mailster_action_sent
UNION ALL
SELECT campaign_id,count,2 FROM
bao_mailster_action_opens
UNION ALL
SELECT campaign_id,count,3 FROM
bao_mailster_action_clicks
UNION ALL
SELECT campaign_id,count,4 FROM
bao_mailster_action_unsubscribes)
SELECT bao_posts.post_modified,
bao_posts.ID,
bao_posts.post_title,
COUNT(CASE WHEN bao_mailster_actions.type = 1 then 1 ELSE NULL END) AS Number_People_Reached,
COUNT(CASE WHEN bao_mailster_actions.type = 2 then 1 ELSE NULL END) AS Opens,
COUNT(CASE WHEN bao_mailster_actions.type = 3 then 1 ELSE NULL END) AS Clicks,
COUNT(CASE WHEN bao_mailster_actions.type = 4 then 1 ELSE NULL END) AS Unsubs
FROM bao_posts
campaign_id | TotalCount | TotalNumber | type
----------: | ---------: | ----------: | ---:
1 | 1 | 88 | 1
2 | 1 | 4 | 1
4 | 1 | 6 | 1
2 | 1 | 4 | 2
3 | 1 | 5 | 2
4 | 1 | 10 | 2
1 | 1 | 3 | 3
2 | 1 | 3 | 3
4 | 1 | 6 | 3
1 | 1 | 4 | 4
3 | 1 | 5 | 4
4 | 1 | 5 | 4
post_modified | ID | post_title | Number_People_Reached | Opens | Clicks | Unsubs
:------------ | -: | :----------- | --------------------: | ----: | -----: | -----:
2021-03-01 | 1 | first post | 1 | 0 | 1 | 1
2021-06-01 | 2 | second opion | 1 | 1 | 1 | 0
2021-09-01 | 3 | third way | 0 | 1 | 0 | 1
2021-12-01 | 4 | last post | 1 | 1 | 1 | 1
db<>fiddle here
I finally got it to work using only the new tables that Mailster created (it seems that finally they did move all the info to the new tables with the update) and with 4 LEFT JOINS.
I leave the code in case someone else finds it useful:
SELECT P.post_modified,
P.ID,
P.post_title,
IFNULL(S.count,0) as 'Total',
IFNULL(O.count,0) as 'Aperturas',
IFNULL(C.count,0) as 'Clicks',
IFNULL(U.count,0) as 'Bajas' from bao_posts as P
LEFT JOIN (select campaign_id, count(DISTINCT subscriber_id) as count from bao_mailster_action_clicks group by campaign_id) as C ON C.campaign_id = P.ID
LEFT JOIN (select campaign_id, count(DISTINCT subscriber_id) as count from bao_mailster_action_opens group by campaign_id) as O ON O.campaign_id = P.ID
LEFT JOIN (select campaign_id, count(DISTINCT subscriber_id) as count from bao_mailster_action_sent group by campaign_id) as S ON S.campaign_id = P.ID
LEFT JOIN (select campaign_id, count(DISTINCT subscriber_id) as count from bao_mailster_action_unsubs group by campaign_id) as U ON U.campaign_id = P.ID
WHERE P.post_type = 'newsletter'
ORDER BY P.post_modified ASC ;
P.S: As I expected, Mailster's support has not helped at all :'(
Hi I need a help regarding this problem. I want to create a view where it displays the summary of purchased and sold items.
Theses are my tables:
**tblfruit**
ID Name
1 Apple
2 Orange
3 mango
**tblpurchaseditems**
ID fruit_id qty amount
1 1 3 75
2 1 2 50
3 2 1 10
4 3 3 30
**tblsolditems**
ID fruit_id qty amount
1 1 2 150
2 1 2 350
3 2 1 50
4 3 2 230
5 3 1 120
I want the output :
**tblsummary**
ID fruit_id totalqtypurchased totalamountpurchased totalqtysold totalamountsold
1 1 5 125 4 500
2 2 1 10 1 50
3 3 3 30 3 350
So there are a couple of ways of doing this, either online or using a join.
Inline:
Select name, (select sum(qty) from tableb) as totalqty
From tablea
Join:
Select a.name, sum(b.qty) as totalqty
From tablea a
Join tableb b
On b.itemid = a.itemid
Group by a.name
For multiple tables things can get trickier using joins due to duplication so inline queries can work better here, for smaller queries.
select a.fruit_id,sum(b.qty),sum(b.amount),sum(c.qty),sum(c.amount)
from tblfruit a,tblpurchaseditems b,tblsolditems c
where a.fruit_id=b.fruit_id and a.fruit_id=c.fruit_id
group by a.fruit_id
From your sample data and expected result
you can try to UNION ALL to combine tblpurchaseditems and tblpurchaseditems table and make grp to spite two result set. then use condition aggregate function to get your expect result.
CREATE TABLE tblpurchaseditems(
ID INT,
fruit_id INT,
qty INT,
amount INT
);
INSERT INTO tblpurchaseditems VALUES (1,1,3,75);
INSERT INTO tblpurchaseditems VALUES (2,1,2,50);
INSERT INTO tblpurchaseditems VALUES (3,2,1,10);
INSERT INTO tblpurchaseditems VALUES (4,3,3,30);
CREATE TABLE tblsolditems(
ID INT,
fruit_id INT,
qty INT,
amount INT
);
INSERT INTO tblsolditems VALUES (1,1,2,150);
INSERT INTO tblsolditems VALUES (2,1,2,350);
INSERT INTO tblsolditems VALUES (3,2,1,50);
INSERT INTO tblsolditems VALUES (4,3,2,230);
INSERT INTO tblsolditems VALUES (5,3,1,120);
Query 1:
SELECT (#RN:=#RN+1) ID,
fruit_id,
SUM(CASE WHEN grp = 1 THEN qty END) totalqtypurchased ,
SUM(CASE WHEN grp = 1 THEN amount END) totalamountpurchased ,
SUM(CASE WHEN grp = 2 THEN qty END) totalqtysold,
SUM(CASE WHEN grp = 2 THEN amount END) totalamountsold
FROM (
select 1 grp,fruit_id,qty,amount from tblpurchaseditems
UNION ALL
SELECT 2,fruit_id,qty,amount FROM tblpurchaseditems
)t1 CROSS JOIN (SELECT #RN:=0) v
GROUP BY fruit_id
Results:
| ID | fruit_id | totalqtypurchased | totalamountpurchased | totalqtysold | totalamountsold |
|----|----------|-------------------|----------------------|--------------|-----------------|
| 1 | 1 | 5 | 125 | 5 | 125 |
| 2 | 2 | 1 | 10 | 1 | 10 |
| 3 | 3 | 3 | 30 | 3 | 30 |
NOTE
I would use a column itemGroup in table, which can represent which type.
1 mean purchaseditems
2 mean solditems
That would not need to use UNION ALL to combine two table, more make sense.
So in the table schema will look like.
CREATE TABLE tblitems(
ID INT,
fruit_id INT,
qty INT,
amount INT,
itemGroup INT
);
INSERT INTO tblitems VALUES (1,1,3,75,1);
INSERT INTO tblitems VALUES (2,1,2,50,1);
INSERT INTO tblitems VALUES (3,2,1,10,1);
INSERT INTO tblitems VALUES (4,3,3,30,1);
INSERT INTO tblitems VALUES (1,1,2,150,2);
INSERT INTO tblitems VALUES (2,1,2,350,2);
INSERT INTO tblitems VALUES (3,2,1,50 ,2);
INSERT INTO tblitems VALUES (4,3,2,230,2);
INSERT INTO tblitems VALUES (5,3,1,120,2);
Query 1:
SELECT (#RN:=#RN+1) ID,
fruit_id,
SUM(CASE WHEN itemGroup = 1 THEN qty END) totalqtypurchased ,
SUM(CASE WHEN itemGroup = 1 THEN amount END) totalamountpurchased ,
SUM(CASE WHEN itemGroup = 2 THEN qty END) totalqtysold,
SUM(CASE WHEN itemGroup = 2 THEN amount END) totalamountsold
FROM tblitems t1 CROSS JOIN (SELECT #RN:=0) v
GROUP BY fruit_id
Results:
| ID | fruit_id | totalqtypurchased | totalamountpurchased | totalqtysold | totalamountsold |
|----|----------|-------------------|----------------------|--------------|-----------------|
| 1 | 1 | 5 | 125 | 4 | 500 |
| 2 | 2 | 1 | 10 | 1 | 50 |
| 3 | 3 | 3 | 30 | 3 | 350 |
Following query...
SELECT event_id, user_id FROM EventUser WHERE user_id IN (1, 2)
...gives me the following result:
+----------+---------+
| event_id | user_id |
+----------+---------+
| 3 | 1 |
| 2 | 1 |
| 1 | 1 |
| 5 | 1 |
| 4 | 1 |
| 6 | 1 |
| 4 | 2 |
| 2 | 2 |
| 1 | 2 |
| 5 | 2 |
+----------+---------+
Now, I want to modify the above query so that I only get for example two rows for each user_id, eg:
+----------+---------+
| event_id | user_id |
+----------+---------+
| 3 | 1 |
| 2 | 1 |
| 4 | 2 |
| 5 | 2 |
+----------+---------+
I am thinking about something like this, which of course does not work:
SELECT event_id, user_id FROM EventUser WHERE user_id IN (1, 2) LIMIT 2 by user_id
Ideally, this should work with offsets as well because I want to use it for paginations.
For performance reasons it is essential to use the WHERE user_id IN (1, 2) part of the query.
One method -- assuming you have at least two rows for each user -- would be:
(select min(event_id) as event_id, user_id
from t
where user in (1, 2)
group by user_id
) union all
(select max(event_id) as event_id, user_id
from t
where user in (1, 2)
group by user_id
);
Admittedly, this is not a "general" solution, but it might be the simplest solution for what you want.
If you want the two biggest or smallest, then an alternative also works:
select t.*
from t
where t.user_id in (1, 2) and
t.event_id >= (select t2.event_id
from t t2
where t2.user_id = t.user_id
order by t2.event_id desc
limit 1, 1
);
Here is a dynamic example for such problems, Please note that this example is working in SQL Server, could not try on mysql for now. Please let me know how it works.
CREATE TABLE mytable
(
number INT,
score INT
)
INSERT INTO mytable VALUES ( 1, 100)
INSERT INTO mytable VALUES ( 2, 100)
INSERT INTO mytable VALUES ( 2, 120)
INSERT INTO mytable VALUES ( 2, 110)
INSERT INTO mytable VALUES ( 3, 120)
INSERT INTO mytable VALUES ( 3, 150)
SELECT *
FROM mytable m
WHERE
(
SELECT COUNT(*)
FROM mytable m2
WHERE m2.number = m.number AND
m2.score >= m.score
) <= 2
How about this?
SELECT event_id, user_id
FROM (
SELECT event_id, user_id, row_number() OVER (PARTITION BY user_id) AS row_num
FROM EventUser WHERE user_id in (1,2)) WHERE row_num <= n;
And n can be whatever
Later but help uses a derived table and the cross join.
For the example in this post the query will be this:
SELECT
#row_number:=CASE
WHEN #user_no = user_id
THEN
#row_number + 1
ELSE
1
END AS num,
#user_no:=user_id userid, event_id
FROM
EventUser,
(SELECT #user_no:=0,#row_number:=0) as t
group by user_id,event_id
having num < 3;
More information in this link.
Could somebody help with my SQL?
I have a table with records such as:
ID | Car_num | Service | Price
---+---------+---------+------
1 | 001 | shower | 10
2 | 002 | TV | 5
3 | 001 | TV | 5
How to write an SQL query to get the following output?
ID |Car_num | shower | TV
---+--------+--------+---
1 | 001 | 10 | 5
2 | 002 | 0 | 5
Use a pivot query:
SELECT MIN(ID) AS ID,
Car_num,
MAX(CASE WHEN Service = 'shower' THEN Price ELSE 0 END) AS shower,
MAX(CASE WHEN Service = 'TV' THEN Price ELSE 0 END) AS TV
FROM yourTable
GROUP BY Car_num
SELECT ROW_NUMBER() OVER(ORDER BY Car_num) Id,Car_num ,ISNULL([TV],0) [TV],ISNULL([shower],0) [shower]
FROM
(
SELECT Car_num , _Service , ISNULL(Price,0) Price
FROM #Table
)Data
PIVOT
(
MAX(Price) FOR _Service IN ([TV],[shower])
)AS PVT
Try this:
SELECT ID, Car_num
, SUM(IF(Service = 'shower', Price, 0)) AS Shower
, SUM(IF(Service = 'TV', Price, 0)) AS TV
FROM your_table
GROUP BY Car_num;
I have a database that looks like this SQL Fiddle: http://sqlfiddle.com/#!9/aa02e/1
CREATE TABLE Table1
(`Store` varchar(1), `Date` date, `Product` varchar(2), `Weekday` int, `Month` int, `Revenue` float)
;
INSERT INTO Table1
(`Store`, `Date`, `Product`, `Weekday`, `Month`, `Revenue`)
VALUES
('a', '20160101', 'aa', 5, 1, 1.5),
('a', '20160101', 'bb', 5, 1, 4),
('a', '20160101', 'cc', 5, 1, 3.5),
('a', '20160108', 'dd', 5, 1, 2.5),
('a', '20160108', 'ee', 5, 1, 5),
('b', '20160204', 'aa', 4, 2, 9.5),
('b', '20160204', 'bb', 4, 2, 4),
('b', '20160204', 'cc', 4, 2, 3),
('b', '20160211', 'dd', 4, 2, 1.5),
('b', '20160211', 'ee', 4, 2, 2.5)
;
SELECT * FROM table1;
+-------+------------+---------+---------+-------+---------+
| Store | Date | Product | Weekday | Month | Revenue |
+-------+------------+---------+---------+-------+---------+
| a | 2016-01-01 | aa | 5 | 1 | 1.5 |
| a | 2016-01-01 | bb | 5 | 1 | 4 |
| a | 2016-01-01 | cc | 5 | 1 | 3.5 |
| a | 2016-01-08 | dd | 5 | 1 | 2.5 |
| a | 2016-01-08 | ee | 5 | 1 | 5 |
| b | 2016-02-04 | aa | 4 | 2 | 9.5 |
| b | 2016-02-04 | bb | 4 | 2 | 4 |
| b | 2016-02-04 | cc | 4 | 2 | 3 |
| b | 2016-02-11 | dd | 4 | 2 | 1.5 |
| b | 2016-02-11 | ee | 4 | 2 | 2.5 |
+-------+------------+---------+---------+-------+---------+
It shows revenue data for stores incl. products, date and the respective day/month.
I want to select the following:
Store
Monthly revenue totals (i.e. what is the total revenue for store a in Jan?)
Weekday revenue averages (i.e. what is the avg revenue for store a on Thu?)
The first and second bullet are straightforward, but I'm having problems with the last one.
Currently, it takes the average over all products and all dates (assuming the weekday matches). What I need are the following steps:
Sum up all revenues for a store and a particular date (e.g. for store b: 9.5+4+3=16.5 for Feb 4th, and 1.5+2.5=4 for Feb 11th) if that date has the same weekday (here Thursday)
Take the average of the two values (e.g. avg(16.5,4)=10.25)
How can I accomplish that?
Thank you
Here is the query:
SELECT
Store,
SUM(CASE WHEN Month = 1 THEN Revenue ELSE NULL END) AS REVENUE_JAN,
SUM(CASE WHEN Month = 2 THEN Revenue ELSE NULL END) AS REVENUE_FEB,
AVG(CASE WHEN Weekday = 4 THEN Revenue ELSE NULL END) AS REVENUE_THU,
AVG(CASE WHEN Weekday = 5 THEN Revenue ELSE NULL END) AS REVENUE_FRI
FROM Table1
GROUP BY
Store
;
The weekday average is tricky. Your query is getting the average "order size" per weekday. But you want the total revenue.
One method is to first aggregate by weekday, but that is a bit of a mess. Instead, you can use this trick of calculating the average by dividing the total revenue by the number of days:
SELECT Store,
SUM(CASE WHEN Month = 1 THEN Revenue ELSE NULL END) AS REVENUE_JAN,
SUM(CASE WHEN Month = 2 THEN Revenue ELSE NULL END) AS REVENUE_FEB,
(SUM(CASE WHEN Weekday = 4 THEN Revenue END) /
COUNT(DISTINCT CASE WHEN Weekday = 4 THEN Date END)
) AS REVENUE_THU,
(SUM(CASE WHEN Weekday = 5 THEN Revenue END) /
COUNT(DISTINCT CASE WHEN Weekday = 5 THEN Date END)
) AS REVENUE_FRI
FROM Table1
GROUP BY Store;
SELECT
t1.store,
SUM(CASE WHEN Month = 1 THEN Revenue ELSE NULL END) AS REVENUE_JAN,
SUM(CASE WHEN Month = 2 THEN Revenue ELSE NULL END) AS REVENUE_FEB,
daily.REVENUE_THU,
daily.REVENUE_FRI
FROM Table1 t1
JOIN (
SELECT
Store,
weekday,
avg(CASE WHEN weekday = 4 THEN sum_rev END) as REVENUE_THU,
avg(CASE WHEN weekday = 5 THEN sum_rev END) as REVENUE_FRI
FROM (
SELECT
Store, date, weekday,
SUM(revenue) AS sum_rev
FROM Table1
GROUP BY
Store, date, weekday
) AS foo
GROUP BY Store, weekday
) AS daily ON daily.store = t1.store
GROUP BY
t1.store
How about this solution it return average for chosen day of chosen store
CREATE PROCEDURE sumForDayStore(IN vday INTEGER, IN vStore VARCHAR(50))
BEGIN
DECLARE totalDays INTEGER;
DECLARE totalRevenu INTEGER;
SET totalDays = (SELECT count(*) FROM Table1 WHERE WeekDay = vDay AND store = vStore);
SET totalRevenu = (SELECT sum(Revenue) FROM Table1 WHERE WeekDay = vDay AND store = vStore);
SELECT totalRevenu/totalDays;
END;
CALL sumForDayStore(5,'a');
How about this one:
SELECT mnth.Store, REVENUE_JAN, REVENUE_FEB, avg(rthu) REVENUE_THU, avg(rfri) REVENUE_FRI
FROM
(Select Store, sum(case when Month = 1 then Revenue else NULL END) REVENUE_JAN,
sum(case when Month = 2 then Revenue else NULL END) REVENUE_FEB
From Table1 group by Store) as mnth
join
(Select Store, sum(case when Weekday = 4 then Revenue end) rThu,
sum(case when Weekday = 5 then Revenue end) rFri from Table1 group by Store, Date) as dys
on mnth.Store = dys.Store
group by mnth.Store, REVENUE_JAN, REVENUE_FEB
I compared the performance of this with the query in the first answer and it shows better performance according to SQL server execution plan (1.6 times faster). Maybe this would be helpful on a larger data set.