JOIN and SUM different statement results (Wordpress-Mailster Database) - mysql

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 :'(

Related

SQL Query Joins (Create view of combined sum from different table)

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 |

sql subquery joins

I am trying to allow for the custom creation of tables and columns using a few tables in the database.
The Data is in a table called CustomData,
ID | CustomEntries_ID | CustomColumn_ID | Value
-------------------------------------------------
1 | 1 | 1 | Pheven
2 | 1 | 2 | Routine
3 | 1 | 3 | Planned
4 | 1 | 4 | 2014
5 | 2 | 1 | John
6 | 2 | 2 | Routine
7 | 2 | 3 | Planned
8 | 2 | 4 | 2017
SELECT * FROM (
(SELECT CustomEntries_ID AS ID, `Value` AS `Name` FROM `CustomData` WHERE CustomColumn_ID = 1) AS T1 NATURAL JOIN
(SELECT CustomEntries_ID AS ID, `Value` AS `Servive` FROM `CustomData` WHERE CustomColumn_ID = 2) AS T2 NATURAL JOIN
(SELECT CustomEntries_ID AS ID, `Value` AS `Type` FROM `CustomData` WHERE CustomColumn_ID = 3) AS T3 NATURAL JOIN
(SELECT CustomEntries_ID AS ID, `Value` AS `Model` FROM `CustomData` WHERE CustomColumn_ID = 4) AS T4 NATURAL JOIN
)
The first problem seems to be if there is an entry missing the CustomData table all data relating to that customEntries_ID are excluded.
The second issue is this seems very inefficient, I'm sure someone has a better suggestion for this query or to allow for Custom table creation without actually creating new tables on the database.
Just use conditional aggregation:
SELECT customernetyid
max(case when CustomColumn_ID = 1 then value end) as name,
max(case when CustomColumn_ID = 2 then value end) as service,
max(case when CustomColumn_ID = 3 then value end) as type,
max(case when CustomColumn_ID = 4 then value end) as model
FROM CustomData
GROUP BY customentryid;

select two tables mysql without join

There are two tables, recharge and purchase.
select * from recharge;
+-----+------+--------+---------------------+
| idx | user | amount | created |
+-----+------+--------+---------------------+
| 1 | 3 | 10 | 2016-01-09 20:16:18 |
| 2 | 3 | 5 | 2016-01-09 20:16:45 |
+-----+------+--------+---------------------+
select * from purchase;
+-----+------+----------+---------------------+
| idx | user | resource | created |
+-----+------+----------+---------------------+
| 1 | 3 | 2 | 2016-01-09 20:55:30 |
| 2 | 3 | 1 | 2016-01-09 20:55:30 |
+-----+------+----------+---------------------+
I want to figure out balance of users which is SUM(amount) - COUNT(purchase.idx). (in this case, 13)
So I had tried
SELECT (SUM(`amount`)-COUNT(purchase.idx)) AS balance
FROM `recharge`, `purchase`
WHERE purchase.user = 3 AND recharge.user = 3
but, it returned error.
If you want an accurate count, then aggregate before doing arithmetic. For your particular case:
select ((select sum(r.amount) from recharge where r.user = 3) -
(select count(*) from purchase p where p.user = 3)
)
To do this for multiple users, move the subqueries to the from clause or use union all and aggregation. The second is safer if a user might only be in one table:
select user, coalesce(sum(suma), 0) - coalesce(sum(countp), 0)
from ((select user, sum(amount) as suma, null as countp
from recharge
group by user
) union all
(select user, null, count(*)
from purchase
group by user
)
) rp
group by user
It is possible to using union like this
SELECT SUM(`amount`-aidx) AS balance
FROM(
SELECT SUM(`amount`) as amount, 0 as aidx
from `recharge` where recharge.user = 3
union
select 0 as amount, COUNT(purchase.idx) as aidx
from `purchase`
WHERE purchase.user = 3 )a

Selecting unique rows on basis of certain criteria

I have an SQL table called "trainings" that looks like this:
+-----+-----------+--------+------------+------------+------------+-------+
| Id | Booked |Coach_No| Student_No | StartDate | EndDate | Color |
+-----+-----------+--------+------------+------------+------------+-------+
| 1 | 1 | 20 | NULL | 2011-03-18 |2011-03-19 | 3 |
| 2 | 1 | 20 | 45 | 2011-03-18 |2011-03-19 | 1 |
| 3 | 1 | 15 | 41 | 2011-03-20 |2011-03-21 | 18 |
| 4 | 0 | 21 | NULL | 2011-03-22 |2011-03-23 | 3 |
| 5 | 0 | 33 | NULL | 2011-03-20 |2011-03-21 | 3 |
| 6 | 0 | 34 | NULL | 2011-03-20 |2011-03-21 | 3 |
+-----+-----------+--------+------------+------------+------------+-------+
I'm looking to frame an SQL query that will fetch all the rows with unique start and end dates. For rows with duplicate start and end dates, I need to select those with a color of 1 or 18 in preference over those with a color of 3.
I've attempted to use the query below, but the distinct row that is selected is the one with the lowest Id
SELECT * FROM trainings GROUP BY StartDate,EndDate
What is the right approach?
You could group by on StartDate, EndDate, and select two ID's for the different color priorities. Then join back to the original table, preferring the high priority:
select b1.*
from Trainings b1
join (
select max(case when Color in (1,18) then Id end) as HighPrioID
, max(case when Color not in (1,18) then Id end) as LowPrioID
from Trainings
group by
StartDate
, EndDate
) b2
on b1.Id = COALESCE(b2.HighPrioID, b2.LowPrioID);
Test data:
drop table if exists Trainings;
create table Trainings (id int, StartDate datetime, EndDate datetime, Color int);
insert Trainings values
(1,'2011-03-18','2011-03-19', 3),
(2,'2011-03-18','2011-03-19', 1),
(3,'2011-03-20','2011-03-21',18),
(4,'2011-03-22','2011-03-23', 3),
(5,'2011-03-20','2011-03-21', 3);
SELECT DISTINCT CONCAT(StartDate, EndDate) FROM trainings
If I understood it right.
Is it your mean
SELECT * FROM trainings WHERE color IN (1,18) GROUP BY StartDate,EndDate
Assuming StartDate, EndDate and Colors results in unique records ....
SELECT * FROM
From Trainings T
(
SELECT
StartDate,
EndDate,
MAX(CASE WHEN Color = 3 THEN 0 ELSE Color END) Color
From Trainings
GROUP By StartDate, EndDate
) T1 on T.StartDate = T1.StartDate AND T.EndDate = T1.EndDate AND T.Color = T1.Color
You can do something like this :
select
t1.*,
case
when t2.Id is null then 1
when t1.color in (1,18) then 2
else 3
end as priority
from trainings as t1
left join trainings as t2 on
t1.StartDate = t2.StartDate and
t1.EndDate = t2.EndDate and
t1.Id != t2.Id
order by priority
The value of priority will help you find what you want :
rows with priority 1 have unique start and end date
rows with priority 2 have 1 or 18 has color
all other rows have priority 3

Exclude duplicate rows in query result

I want to count the number of records in database from more than two tables that are joined.
For example I have a table like this.
table
jobd + name
1 | jobA
2 | jobB
tableA
imgeid + orderid + jobid
1 | 1 | 1
2 | 2 | 1
3 | 3 | 1
4 | 4 | 1 (this order is not yet started)
tableB
taskid + orderid + task + status
1 | 1 | 1 | UPDATED
2 | 1 | 1 | UPDATED
3 | 1 | 1 | COMPLETED
4 | 2 | 2 | SAVED
5 | 3 | 3 | COMPLETED
My problem here is that when I count base on status (# tableB) my query results both the UPDATED which has the same orderid.
This is my sample query that same with the one I'm working.
SELECT t.name
COUNT(CASE WHEN tb.task = 1 AND tb.status <> 'COMPLETED' THEN tb.status ELSE NULL END) inprogress,
COUNT(CASE WHEN tb.task = 1 AND tb.status = 'COMPLETED' THEN tb.status ELSE NULL END) completed
FROM tableA ta
LEFT JOIN tableB tb
ON tb.orderid = ta.orderid
LEFT JOIN table t
ON t.jobid = ta.jobid
GROUP BY t.jobid;
My results something like
name + inprogress + completed
jobA | 2 | 1
The inprogress results must only be 1 because it has the same orderid. The reason why it has two UPDATED because this table is HISTORICAL. I don't know how can get the distinct orderid in tableB so it will only results to 1.
The main point here is that I can count the total orders which status is in progress, completed and not started per job.
I hope my question is clear. If you have other way, please let me know. Thanks
Can't you use a Count distinct? Here's a link, see nearer the bottom of the page, it will only the unique field you specify: w3schools.com/sql/sql_func_count.asp
SELECT t.name
COUNT(DISTINCT tb.orderid CASE WHEN tb.task = 1 AND tb.status 'COMPLETED' THEN tb.status
ELSE NULL END) inprogress,
COUNT(DISTINCT tb.orderid CASE WHEN tb.task = 1 AND tb.status = 'COMPLETED' THEN tb.status
ELSE NULL END) completed