Exclude duplicate rows in query result - mysql

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

Related

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

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

MySQL Compare between 2 Tables then add results

I have a table with the next structure
Table A:
| Id | 1 | 2 | 3 |
|-----|---------|----------|-----------|
| 1 | 00:05:00| 00:10:00 | (null) |
| 2 | 00:10:00| (null) | (null) |
Table B
| Id | col |Expected |
|-----|---------|----------|
| 1 | 1 | 00:06:00 |
| 2 | 2 | 00:12:00 |
| 3 | 3 | 00:22:00 |
I am trying to make a sum depending on the actual value on the rows of table A in comparison to the expected on table B
Select
Id, (Select ??????? From (Select TiempoStd from B)as Stime) as Time
From
A
Basically i want to make a comparison between the 2 tables to see which one is greater and add that to the next one.
I cant manage to understand how to call a specific value under my temp table Stime.
i am not that familiar with SQL so thats why i cant get this, the logic is something like this. in where the question marks are on the Query
ADDTIME(IF(A.1>(Stime.Expected where col = 1),A.1,(Stime.Expected where col = 1)),
ADDTIME(IF(A.2>(Stime.Expected where col = 2),A.2,(Stime.Expected where col = 2)),
IF(A.3>(Stime.Expected where col = 3),A.3,(Stime.Expected where col = 3))
Stime.Expected where col = 3 is a bad syntax right? but i hope you get the point of the logic im trying to make here.
so the output would be like this
| Id | Time |
|-----|---------|
| 1 | 00:40:00|
| 2 | 00:44:00|
Use a UNION to convert table A to a table with separate rows for each column, then join this with table B.
select *
FROM TableB AS b
JOIN (SELECT Id, 1 AS col, a.1 AS Time
FROM TableA as a
UNION
SELECT Id, 2 AS col, a.2 AS Time
FROM TableA AS a
UNION
SELECT Id, 3 AS col, a.3 AS Time
FROM TableA AS a) AS a
ON a.Id = b.Id AND a.col = b.col
This made the trick, i got help from some friends and got to it, gives the desired result.
SELECT
A.Id,
addtime(
addtime(
ifnull(Case when A.1 > E.Expected1 Then A.1 else E.expected1 end,'00:00:00') ,
ifnull(Case when A.2 > E.Expected2 Then A.2 else E.expected2 end,'00:00:00')
), ifnull(Case when A.3 > E.Expected3 Then A.3 else E.expected3 end,'00:00:00')
) as Time
From TableA A, (
SELECT
sum(case when col=1 Then Expected else 0 end) as Expected1,
sum(case when col=2 Then Expected else 0 end) as Expected2,
sum(case when col=3 Then Expected else 0 end) as Expected3
From TableB ) as E

Advanced SQL Select and update items from the same table

I have a table that has the rows item_id (unique key), base_id, step_id, and active.
What i need to do is update everything in that table, that matches base_id where step_id = 1 and active = 0 to active = 1 that doesn't have an entry in the same table with the same base_id and step_id = 2 and active = 1.
----------------------------------------
| item_id | base_id | step_id | active |
| 1 | 1 | 1 | 0 |
| 2 | 1 | 2 | 1 |
| 3 | 2 | 1 | 0 |
| 4 | 3 | 1 | 0 |
| 5 | 3 | 1 | 0 |
----------------------------------------
This would return make item 3, 4, and 5 update to have active = 1
If that makes sense. Any help is greatly appreciated. Thanks in advance.
select item_id
from your_table
where base_id in
(
select base_id
from your_table
group by base_id
having sum(step_id = 1 and active = 0) > 0
and sum(step_id = 2 and active = 1) = 0
)
What does the inner select do?
It groups the records by base_id and takes only those having at least 1 record with step_id = 1 and active and zero records with step_id = 2 and active = 1.
sum() counts how many times the inner condition is true.
To update the table so that the matching values have active set to 1 we can take the solution by juergen d and turn it into an update statement.
As MySQL has some issues with updating a table that it is referencing in a subquery we insert an extra level of nesting which forces the creation of a temporary result and allows the update:
update table1 t
set t.active = 1
where base_id in (
select base_id from (
select base_id
from table1
group by base_id
having sum(step_id = 1 and active = 0) > 0
and sum(step_id = 2 and active = 1) = 0
) a
);
This would set active = 1 for item_id 3, 4 and 5
Sample SQL Fiddle
I would approach this with a simple in or exists:
select t.*
from table t
where step_id = 1 and active = 0 and
not exists (select 1
from table t2
where t2.base_id = t.base_id and t2.step_id = 2 and t2.active = 1
);
This seems like a direct translation of your description.

Rows to columns in mysql

I'm doing the select:
select id,
status,
count(status) as qtd
from user
group by id, status;
The return is:
id | status | qtd
1 YES 5
1 NO 3
2 YES 3
2 NO 1
I want this:
id | YES | NO
1 5 3
2 3 1
Thanks.
NOTE:
you can use case logic to do what you want.. basically you want to pivot the results and to pivot them you have to use aggregates with conditionals to fake a pivot table since mysql doesn't have a way to accomplish that
QUERY:
SELECT
id,
SUM(CASE status WHEN 'Yes' THEN 1 ELSE 0 END) as 'YES',
SUM(CASE status WHEN 'No' THEN 1 ELSE 0 END) as 'NO'
FROM user
GROUP BY id;
DEMO
OUTPUT:
+----+-----+----+
| id | YES | NO |
+----+-----+----+
| 1 | 5 | 3 |
| 2 | 3 | 1 |
+----+-----+----+
You can do so,using expression in sum() like sum(status ='Yes') will result as boolean (0/1) and thus you can have your count based on your criteria you provide in sum function
select id,
sum(status ='Yes') as `YES`,
sum(status ='No') as `NO`
from user
group by id;

combining multiple different sql into one

cusID | Name | status | Date
---------------------------------
1 | AA | 0 | 2013-01-25
2 | BB | 1 | 2013-01-23
3 | CC | 1 | 2013-01-20
SELECT COUNT(cusID) FROM customer WHERE STATUS=0;
SELECT COUNT(cusID) FROM customer WHERE STATUS=1;
Is there a way of combing such two sql and return the results as one. Because want to avoid calling to DB everytime. I tried UNION of two statments, but only showing one result.
This is the shortest possible solution in MySQL.
SELECT SUM(status = 1) totalActive,
SUM(status = 0) totalInactive
FROM tableName
SQLFiddle Demo
and this is the CASE version
SELECT SUM(CASE WHEN status = 1 THEN 1 ELSE 0 END) totalActive,
SUM(CASE WHEN status = 0 THEN 1 ELSE 0 END) totalInactive
FROM tableName
SQLFiddle Demo