MySQL Add missing Months in resultset - mysql

I'm trying to add missing months in this result set.
Where month is missing, add it with the value 0 for Quantita.
SELECT MONTH(Data) AS Mese,Count(*) AS Quantita
FROM prenotazioni
WHERE Cancellata IS NULL
AND FKCampo = 1
AND YEAR(Data) = YEAR(CURDATE()) -1
GROUP BY Mese
ORDER BY Mese ASC
+------+----------+
| Mese | Quantita |
+------+----------+
| 4 | 123 |
+------+----------+
| 5 | 100 |
+------+----------+
| 7 | 377 |
+------+----------+
| 9 | 54 |
+------+----------+

The following is messy and I am sure there has to be a cleaner way...
So, first I create a table with all the months:
CREATE TABLE all_months (
month_num INT
);
INSERT INTO all_months VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12)
Then I join your query with this table (in my case on the right) and use a case-when to decide if there is a count or not:
SELECT
all_months.month_num,
CASE
WHEN tmp.Quantita is NULL THEN 0
ELSE tmp.Quantita
END as Quantita
FROM (
SELECT MONTH(Data) AS Mese,Count(*) AS Quantita
FROM prenotazioni
WHERE Cancellata IS NULL
AND FKCampo = 1
AND YEAR(Data) = YEAR(CURDATE()) -1
GROUP BY Mese
) as tmp
RIGHT JOIN all_months on all_months.month_num=tmp.Mese
ORDER BY all_months.month_num ASC
Your query is now in tmp and ordering is happening after that. The result is:
month_num Quantita
1 1
2 2
3 0
4 0
5 0
6 0
7 0
8 3
9 1
10 0
11 1
12 1
Demo: here

A solution that does not require creating a new table.
SELECT
`months`.`number`,
CASE
WHEN tmp.Quantita IS NULL THEN 0
ELSE tmp.Quantita
END AS Quantita
FROM (
SELECT MONTH(Data) AS Mese, COUNT(*) AS Quantita
FROM prenotazioni
WHERE Cancellata IS NULL
AND FKCampo = 1
AND YEAR(Data) = YEAR(CURDATE()) -1
GROUP BY Mese
) AS tmp RIGHT JOIN (
SELECT 1 AS `number`
UNION SELECT 2
UNION SELECT 3
UNION SELECT 4
UNION SELECT 5
UNION SELECT 6
UNION SELECT 7
UNION SELECT 8
UNION SELECT 9
UNION SELECT 10
UNION SELECT 11
UNION SELECT 12
) AS `months` ON `months`.`number` = tmp.Mese
ORDER BY `months`.`number` ASC;

One method is to construct or find a table that has all the months you want and use a LEFT JOIN.
In your case, though, conditional aggregation might do this with a simpler query. This assumes that you have at least one row for each month, even if that is filtered out by the existing where clause.
So, this might work:
SELECT MONTH(Data) AS Mese,
SUM(Cancellata IS NULL AND FKCampo = 1 ) AS Quantita
FROM prenotazioni
WHERE YEAR(Data) = YEAR(CURDATE()) - 1
GROUP BY Mese
ORDER BY Mese ASC ;
Of course, if the original table does not have all the months, then they still won't be in the result set and you are back to having to use an outer join.

Same idea of #urban but simplifying the query using left join:
create table prenotazioni
(
id int auto_increment,
Cancellata int default null null,
FKCampo int default 1 null,
Data date default null null,
constraint table_nametes_pk
primary key (id)
);
insert into prenotazioni (Cancellata, FKCampo, Data)
values (null, 1, "2019-04-01"),
(null, 1, "2019-04-01"),
(null, 1, "2019-05-01"),
(null, 1, "2019-05-01"),
(null, 1, "2019-05-01"),
(null, 1, "2019-01-01"),
(null, 1, "2019-01-01"),
(null, 1, "2020-01-01"),
(null, 1, "2018-01-01");
CREATE TABLE all_months (
month_num INT
);
INSERT INTO all_months VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12);
select month_num, count(p.Data)
from
all_months am left join prenotazioni p on (am.month_num = month(p.Data))
where
p.id is null
or
(Cancellata IS NULL AND FKCampo = 1 AND YEAR(Data) = YEAR(CURDATE()) - 1)
GROUP BY am.month_num
ORDER BY am.month_num;
result:

Related

How to return the number of sets for each date

I am running a query that returns:
Date Set Data
2021-07-02 1 A
2021-07-02 2 B
2021-07-02 3 C
2021-08-15 1 D
2021-10-27 1 E
2021-10-27 2 F
I need to also return the number of Sets for each date:
Date Set Data NoSets
2021-07-02 1 A 3
2021-07-02 2 B 3
2021-07-02 3 C 3
2021-08-15 1 D 1
2021-10-27 1 E 2
2021-10-27 2 F 2
SELECT csm_pat_exe_date.pedate_id, csm_patient_exercise.pat_exe_id,
csm_exercise_details.ede_id, csm_exercise.exe_id,
ses_pat_id, ses_pat_note, ses_pat_body_weight, ses_pat_blood_pressure, ses_pat_blood_glucose_level,
exe_name, ede_order, ede_type,
ede_unit, weekofyear(csm_pat_exe_date.pedate_date) AS weekNumber, csm_pat_exe_date.pedate_date, peds_id, peds_set, peds_result, pedate_note, t2.noSets
FROM csm_session_patient, csm_session, csm_exercise, csm_patient_exercise, csm_exercise_details,
csm_pat_exe_date_set, csm_pat_exe_date
INNER JOIN (
SELECT pedate_date, COUNT(*) as noSets
FROM csm_patient_exercise, csm_pat_exe_date
WHERE csm_patient_exercise.pat_id = '1'
AND csm_patient_exercise.pat_exe_id = csm_pat_exe_date.pat_exe_id
GROUP BY pedate_date
) t2 ON csm_pat_exe_date.pedate_date = t2.pedate_date
WHERE csm_session_patient.pat_id= '1'
AND csm_session_patient.ses_id = csm_session.ses_id
AND csm_session.ses_date = csm_pat_exe_date.pedate_date
AND exe_archived IS NULL
AND csm_exercise.exe_id = csm_patient_exercise.exe_id
AND csm_patient_exercise.pat_exe_id = '1'
AND csm_patient_exercise.exe_id = csm_patient_exercise.pat_exe_id
AND csm_patient_exercise.pat_exe_id = csm_pat_exe_date.pat_exe_id
AND csm_pat_exe_date.pedate_date >= '2021-06-01'
AND csm_pat_exe_date.pedate_date <= '2021-09-24'
AND csm_pat_exe_date.pedate_id = csm_pat_exe_date_set.pedate_id
AND csm_pat_exe_date_set.ede_id = csm_exercise_details.ede_id
ORDER BY csm_pat_exe_date.pedate_date, peds_set, ede_order;
You may add the count window function to your select clause eg
Mysql 8+
SELECT
`Date`,
`Set`,
`Data`,
`NoSets`,
COUNT(*) OVER (PARTITION BY Date) as NoSets
FROM
...include the rest of your query here
Older Mysql Versions
You may use variables or aggregates to achieve your count
Schema (MySQL v5.5)
CREATE TABLE my_table (
`Date` DATETIME,
`Set` INTEGER,
`Data` VARCHAR(1)
);
INSERT INTO my_table
(`Date`, `Set`, `Data`)
VALUES
('2021-07-02', '1', 'A'),
('2021-07-02', '2', 'B'),
('2021-07-02', '3', 'C'),
('2021-08-15', '1', 'D'),
('2021-10-27', '1', 'E'),
('2021-10-27', '2', 'F');
Query #1
SELECT
`Date`,
`Set`,
`Data`,
`NoSets`
FROM (
SELECT
t.*,
#maxcnt:=IF(#prevdate2=`DATE`,IF(#maxcnt>cnt,#maxcnt,cnt),cnt) as NoSets,
#prevdate2:=`Date`
FROM (
SELECT
#cnt:=IF(#prevdate1=`DATE`,#cnt+1,1) as cnt,
#prevdate1:=`Date`,
m.*
FROM
my_table m
CROSS JOIN (SELECT #cnt:=0,#prevdate1:=NULL) vars
ORDER BY `Date`
) t
CROSS JOIN (SELECT #maxcnt:=0,#prevdate2:=NULL) vars
ORDER BY `Date`,cnt DESC
) t2;
Date
Set
Data
NoSets
2021-07-02 00:00:00
3
C
3
2021-07-02 00:00:00
2
B
3
2021-07-02 00:00:00
1
A
3
2021-08-15 00:00:00
1
D
1
2021-10-27 00:00:00
2
F
2
2021-10-27 00:00:00
1
E
2
Query #2
SELECT
t1.`Date`,
t1.`Set`,
t1.`Data`,
t2.`NoSets`
FROM
my_table t1
INNER JOIN (
SELECT `Date`, COUNT(*) as NoSets
FROM my_table
GROUP BY `Date`
) t2 ON t1.`Date`=t2.`Date`;
Date
Set
Data
NoSets
2021-07-02 00:00:00
1
A
3
2021-07-02 00:00:00
2
B
3
2021-07-02 00:00:00
3
C
3
2021-08-15 00:00:00
1
D
1
2021-10-27 00:00:00
1
E
2
2021-10-27 00:00:00
2
F
2
or
SELECT
t1.`Date`,
t1.`Set`,
t1.`Data`,
(
SELECT COUNT(*) FROM my_table t2 WHERE t2.Date=t1.Date
) as `NoSets`
FROM
my_table t1
View on DB Fiddle
Let me know if this works for you.

Need some help to clean duplicates in MySQL table which didn't have constraint

I've inherited some MySQL table that was designed without correct constraint so it gets filled with some duplicate rows which I need to remove. The problem that across duplicate rows data isn't usually consistent, see example below:
id
request_id
guid_id
details
flag
1
10
fh82EN
help me
1
2
11
fh82EN
3
12
fh82EN
assistance required
1
4
12
fh82EN
assistance required
1
5
13
fh82EN
6
13
fh82EN
assist me.
1
7
13
fh82EN
8
14
fh82EN
Records with id: 1,2,8 perfectly fine. For duplicate records with id 3, 4 I have designed the query below which works fine and removes all duplicates without an issue:
DELETE IR.*
FROM platform.temp IR
WHERE id IN (
SELECT maxId AS id FROM (
SELECT MAX(id) as maxId, request_id, guid_id
FROM platform.temp
GROUP BY request_id, guid_id
HAVING COUNT(*) > 1
) AS T
);
The problem is records with id 5,6,7. You can see that the same record by (guid_id and request_id) is not consistent. So, my previous query will delete records with content too because of MAX(id). I have designed a query that fixes these records, but we are talking about a huge database and this query is painfully slow:
UPDATE platform.temp AS DEST_T
INNER JOIN (
SELECT request_id, guid_id, details, flag FROM platform.temp WHERE details IS NOT NULL AND details != ''
) AS SOURCE_T
SET DEST_T.details = SOURCE_T.details, DEST_T.flag = SOURCE_T.flag
WHERE DEST_T.guid_id = SOURCE_T.guid_id AND DEST_T.request_id = SOURCE_T.request_id;
How can I change my delete query that it will order my subgroup by field details and will select not MAX(id) but first id, so I will be sure that last row in subgroup will always be populated with value and will left?
MySQL version: 5.6.40-log
UPDATE1:
The desired outcome after cleaning the table should be as follow:
id
request_id
guid_id
details
flag
1
10
fh82EN
help me
1
2
11
fh82EN
3
12
fh82EN
assistance required
1
6
13
fh82EN
assist me.
1
8
14
fh82EN
Use a self join of the table:
DELETE t1
FROM tablename t1 INNER JOIN tablename t2
ON t2.request_id = t1.request_id AND t2.guid_id = t1.guid_id
WHERE (t2.id < t1.id AND COALESCE(t1.details, '') = '')
OR
(t2.id > t1.id AND COALESCE(t2.details, '') <> '');
This will keep 1 row for each request_id and guid_id combination, not necessarily the one with the min id.
See the demo.
Another way to do it, with conditional aggregation:
DELETE t1
FROM tablename t1 INNER JOIN (
SELECT request_id, guid_id,
MIN(id) min_id,
MIN(CASE WHEN COALESCE(details, '') <> '' THEN id END) min_id_not_null
FROM tablename
GROUP BY request_id, guid_id
) t2 ON t2.request_id = t1.request_id AND t2.guid_id = t1.guid_id
WHERE t1.id <> COALESCE(t2.min_id_not_null, t2.min_id);
This will keep the row with the min id under your conditions, but maybe its performance would not be that good compared to the 1st query.
See the demo.
Another way is to emulate the ROW_NUMBER ad then perform the delete operation.
DELETE FROM test
WHERE id NOT IN (select id
from (SELECT id,
#row_number := CASE WHEN #last_request_id <> x.request_id + x.guid_id
THEN 1 ELSE #row_number + 1 END AS row_num,
#last_request_id := x.request_id + x.guid_id
FROM test x
CROSS JOIN (SELECT #row_number := 0, #last_request_id := null, #last_guid_id := null) y
ORDER BY request_id, guid_id, details DESC) temp
where row_num = 1);
Demo.
As i said in the comments i would use it with row_numbers, which in mysql 8 would look much more nicer
CREATE TABLE temp
(`id` varchar(4), `request_id` varchar(12), `guid_id` varchar(9), `details` varchar(21), `flag` varchar(6))
;
INSERT INTO temp
(`id`, `request_id`, `guid_id`, `details`, `flag`)
VALUES
('1', '10', 'fh82EN', 'help me', '1'),
('2', '11', 'fh82EN', NULL, NULL),
('3', '12', 'fh82EN', 'assistance required', '1'),
('4', '12', 'fh82EN', 'assistance required', '1'),
('5', '13', 'fh82EN', NULL, NULL),
('6', '13', 'fh82EN', 'assistance required', '1'),
('7', '13', 'fh82EN', NULL, NULL),
('8', '14', 'fh82EN', NULL, NULL)
;
DELETE t1
FROM temp t1 INNER JOIN
(SELECT `id`
, IF(#request = `request_id` AND #guid = guid_id, #rn:= #rn+1,#rn := 1) rn
,#request := `request_id` as request_id
,#guid := guid_id as guid_id
fROM temp,(SELECT #request := 0, #guid := '',#rn := 0) t1
ORDER BY `guid_id`,`request_id`,`details` DESC, id) t2 ON
t1.`id` = t2.`id` AND rn > 1
SELECT * FROM temp
id | request_id | guid_id | details | flag
:- | :--------- | :------ | :------------------ | :---
1 | 10 | fh82EN | help me | 1
2 | 11 | fh82EN | null | null
3 | 12 | fh82EN | assistance required | 1
6 | 13 | fh82EN | assistance required | 1
8 | 14 | fh82EN | null | null
db<>fiddle here

Calculate difference between min and max for each column only if higher then 0

I need to calculate the difference between odds based on the value in the 'updated' column at the moment I take odds where the updated value is a min and minus it from odds where the updated value is max. It works perfect but I've just realized that in some columns happens to be 0 sometimes and I was wondering if it's possible to select the minimum still based on the updated column and only values where higher than 0.
That's how the table looks like
fixture_id
H_odds
D_odds
A_odds
ev_tstamp
updated
120000
1.40
1.50
1.30
132000
12
120000
1.10
1.10
1.10
132000
11
120000
1.20
0
1.60
132000
10
And that's what I would like to get back
fixture_id
H_odds
D_odds
A_odds
ev_tstamp
updated
dif_h
dif_d
dif_a
120000
1.40
1.50
1.30
132000
12
0.2
0.4
-0.3
That's what I'm getting back at the moment
fixture_id
H_odds
D_odds
A_odds
ev_tstamp
updated
dif_h
dif_d
dif_a
120000
1.40
1.50
1.30
132000
12
0.2
1.5
-0.3
The code I'm using
select
t_max.*,
(t_max.H_odds - t_min.H_odds) as dif_h,
(t_max.D_odds - t_min.D_odds) as dif_d,
(t_max.A_odds - t_min.A_odds) as dif_a
from
(
select
fixture_id,
min(updated) min_updated,
max(updated) max_updated
from
test
group by
fixture_id
) as t1
join test as t_min on (t_min.fixture_id = t1.fixture_id and t_min.updated = t1.min_updated)
join test as t_max on (t_max.fixture_id = t1.fixture_id and t_max.updated = t1.max_updated)
Consider the following:
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(fixture_id INT NOT NULL
,updated INT NOT NULL
,outcome ENUM('Home win','Draw','Away win') NOT NULL
,odds DECIMAL(5,2) NOT NULL
,PRIMARY KEY(fixture_id,outcome,updated)
);
INSERT INTO my_table VALUES
(120,12,'Home win',1.40),
(120,11,'Home win',1.10),
(120,10,'Home win',1.20),
(120,12,'Draw',1.50),
(120,11,'Draw',1.10),
(120,12,'Away win',1.30),
(120,11,'Away win',1.10),
(120,10,'Away win',1.60);
Latest odds:
SELECT x.*
FROM my_table x
JOIN
( SELECT fixture_id
, outcome
, MAX(updated) min_updated
FROM my_table x
GROUP
BY fixture_id
, outcome
) y
ON y.fixture_id = x.fixture_id
AND y.outcome = x.outcome
AND y.min_updated = x.updated;
Earliest odds:
Earliest odds:
SELECT x.*
FROM my_table x
JOIN
( SELECT fixture_id
, outcome
, MIN(updated) min_updated
FROM my_table x
GROUP
BY fixture_id
, outcome
) y
ON y.fixture_id = x.fixture_id
AND y.outcome = x.outcome
AND y.min_updated = x.updated;
Delta:
SELECT a.*
, a.odds - b.odds delta
FROM
( SELECT x.*
FROM my_table x
JOIN
( SELECT fixture_id
, outcome
, MAX(updated) min_updated
FROM my_table x
GROUP
BY fixture_id
, outcome
) y
ON y.fixture_id = x.fixture_id
AND y.outcome = x.outcome
AND y.min_updated = x.updated
) a
JOIN
( SELECT x.*
FROM my_table x
JOIN
( SELECT fixture_id
, outcome
, MIN(updated) min_updated
FROM my_table x
GROUP
BY fixture_id
, outcome
) y
ON y.fixture_id = x.fixture_id
AND y.outcome = x.outcome
AND y.min_updated = x.updated
) b
ON b.fixture_id = a.fixture_id
AND b.outcome = a.outcome;
Result:
+------------+---------+----------+------+-------+
| fixture_id | updated | outcome | odds | delta |
+------------+---------+----------+------+-------+
| 120 | 12 | Home win | 1.40 | 0.20 |
| 120 | 12 | Draw | 1.50 | 0.40 |
| 120 | 12 | Away win | 1.30 | -0.30 |
+------------+---------+----------+------+-------+
This solution only works on MySQL 8+.
I would suggest window functions. The following treats each odds column separately . . . and it does not make any assumptions about the odds increasing or decreasing with each update:
select fixture_id, ev_tstamp, max(updated),
max(case when update = max_h_update then h_odds end) as max_h,
max(case when update = max_d_update then h_odds end) as max_d,
max(case when update = max_a_update then h_odds end) as max_a,
(max(case when update = max_h_update then h_odds end) -
max(case when update = min_h_update then h_odds end)
) as h_diff,
(max(case when update = max_d_update then d_odds end) -
max(case when update = min_d_update then d_odds end)
) as d_diff,
(max(case when update = max_a_update then a_odds end) -
max(case when update = min_a_update then a_odds end)
) as a_diff
from (select t.*,
max(case when h_odds <> 0 then update end) over (partition by fixture_id) as max_h_update,
min(case when h_odds <> 0 then update end) over (partition by fixture_id) as min_h_update,
max(case when d_odds <> 0 then update end) over (partition by fixture_id) as max_h_update,
min(case when d_odds <> 0 then update end) over (partition by fixture_id) as min_h_update,
max(case when a_odds <> 0 then update end) over (partition by fixture_id) as max_a_update,
min(case when a_odds <> 0 then update end) over (partition by fixture_id) as min_a_update
from test t
) t
group by fixture_id, ev_tstamp;
I just modify the code little bit to calculate the difference only for specific group of odds (avg) so it look like bellow. It worked just once though, it took over 15 seconds to process and the other times I tried it didn't work due to time out error. Just to clarify in my structure the market column is the 'outcome' column from your example.
explain SELECT a.*
, a.odds - b.odds delta
FROM
( SELECT x.*
FROM average_odds x
JOIN
( SELECT fix_id
, market
, MAX(updated) min_updated
FROM average_odds x where odds_type=avg
GROUP BY fix_id
, market
) y
ON y.fix_id = x.fix_id
AND y.market = x.market
AND y.min_updated = x.updated
) a
JOIN
( SELECT x.*
FROM average_odds x
JOIN
( SELECT fix_id
, market
, MIN(updated) min_updated
FROM average_odds x where odds_type=avg
GROUP BY fix_id
, market
) y
ON y.fix_id = x.fix_id
AND y.market = x.market
AND y.min_updated = x.updated
) b
ON b.fix_id = a.fix_id
AND b.market = a.market
ORDER BY `delta` ASC
That's the explain table
ID
S TYPE
table..
parti
type
pos_keys
KEY
key len
ref
rows
filtered
extra
1
PRIMARY
derived3>
null
all
null
null
null
null
17466
100.00
Using temporary; Using filesort
1
PRIMARY
x
null
ref
fix,fixi,market,updat
fix
4
y.fix_id
596
0.11
Using where
1
PRIMARY
x
null
ref
fix,fixi,market,updat
fix
4
y.fix_id
596
2.27
Using where
1
PRIMARY
derived5>
null
ref
auto_key0>
auto_key0>
31
y.fix_id,y.market,bobi.x.updated
10
100.00
using index
5
DERIVED
x
null
ref
boki
boki
4
const
17466
100.00
Using index condition; Using temporary; Using file...
3
DERIVED
x
null
ref
boki
boki
4
const
17466
100.00
Using index condition; Using temporary; Using file...

How will I fix this table using MySQL?

I am new to MySQL. I am using MySQL 8.0.
My schema and sample data is as follows:
CREATE TABLE AA (
A int
);
insert into AA (A) values (1);
insert into AA (A) values (1);
insert into AA (A) values (2);
insert into AA (A) values (2);
insert into AA (A) values (1);
insert into AA (A) values (3);
CREATE TABLE BB (
code int,
description varchar(30)
);
insert into BB (code, description) values (1, 'Male');
insert into BB (code, description) values (2, 'Female');
Here's my code
with totalcount as (
select code as 'CODE',
description as 'SEX',
count(A) AS 'TOTAL',
ROUND((COUNT(A) * 100.0) / (SELECT COUNT(A) FROM AA),2) AS 'PERCENT',
sum(count(A)) over (order by CODE asc) AS 'CUMULATIVE',
ROUND((SUM(COUNT(A)) OVER (ORDER BY CODE ) / (SELECT COUNT(A) FROM AA) *100 ),2) AS 'CUMPERCENT'
from AA, BB
where AA.A= BB.code
group by AA.A
)
select *
from totalcount
UNION ALL
SELECT '0' CODE, 'TOTAL' SEX, SUM(TOTAL), SUM(PERCENT), '0' CUMULATIVE, '0.00' CUMPERCENT
from totalcount
ORDER BY CODE;
The output was this
CODE | SEX | TOTAL | PERCENT | CUMULATIVE | CUMPERCENT
0 TOTAL 5 100.00 0 0.00
1 MALE 3 60.00 3 60.00
2 FEMALE 2 40.00 5 100.00
I used UNION since I want the total of these items. In this case, I inserted 0 in TOTAL of SEX column to put it in the first row. Is there another way aside from using UNION and inserting code 0?
As you can see from my table BB, there are only code 1 and 2 for male and female, respectively. I want to show that the value not in this table should be in NOT VALID.
CODE | SEX | TOTAL | PERCENT | CUMULATIVE | CUMPERCENT
0 TOTAL 6 100.00 0 0.00
1 MALE 3 50.00 3 50.00
2 FEMALE 2 33.33 5 83.33
3 NOT VALID 1 16.67 6 100.00
Can you help me with this? Thank you.
This is a very tricky reporting question. One approach uses GROUP BY ROLLUP to generate the total row, along with judicious use of COALESCE to fill in the missing values per your requirements.
SELECT
COALESCE(t1.A, 0) AS CODE,
CASE WHEN t1.A IS NOT NULL THEN COALESCE(t2.DESCRIPTION, 'NOT VALID') ELSE 'TOTAL' END AS SEX,
t1.TOTAL,
ROUND(100.0 * t1.TOTAL / SUM(CASE WHEN t1.A IS NOT NULL THEN t1.TOTAL ELSE 0 END) OVER (), 2) AS PERCENT,
SUM(CASE WHEN t1.A IS NOT NULL THEN t1.TOTAL ELSE 0 END) OVER (ORDER BY t1.A) AS CUMULATIVE,
ROUND(100.0 * SUM(CASE WHEN t1.A IS NOT NULL THEN t1.TOTAL ELSE 0 END) OVER (ORDER BY t1.A) /
SUM(CASE WHEN t1.A IS NOT NULL THEN t1.TOTAL ELSE 0 END) OVER (), 2) AS CUMPERCENT
FROM
(
SELECT
A,
COUNT(*) AS TOTAL
FROM AA
GROUP BY A WITH ROLLUP
) t1
LEFT JOIN BB t2
ON t2.CODE = t1.A
ORDER BY
CODE;
Demo

Why MySQL full outer join returns nulls?

Why MySQL full outer join returns nulls?
Hi
I have the following data:
s_id,date,p_id,amount_sold
1, '2015-10-01', 1, 10
2, '2015-10-01', 2, 12
7, '2015-10-01', 1, 11
3, '2015-10-02', 1, 11
4, '2015-10-02', 2, 10
5, '2015-10-15', 1, 22
6, '2015-10-16', 2, 20
8, '2015-10-22', 3, 444
and i want my query to output something like this: (A = sum of amount_sold for p_id=1 for that date,B = sum of amount_sold for p_id=2 for that date)
date,A,B,Difference
'2015-10-01',21,12,9
'2015-10-02',11,10,1
'2015-10-15',22,0,22
'2015-10-01',0,20,-20
I tried with this query, but the order its returning is having NULLS and the output is wrong:
SELECT A.p_id,A.date,sum(A.amount_sold) A,B.Bs, (sum(A.amount_sold) - B.Bs) as difference FROM sales as A
LEFT JOIN (
SELECT SUM( amount_sold ) Bs,p_id,s_id, DATE
FROM sales
WHERE p_id =2
group by date
) as B ON A.s_id = B.s_id
where A.p_id=1 or B.p_id=2
group by A.date, A.p_id
UNION
SELECT A.p_id,A.date,sum(A.amount_sold) A,B.Bs, (sum(A.amount_sold) - B.Bs) as difference FROM sales as A
RIGHT JOIN (
SELECT SUM( amount_sold ) Bs,p_id,s_id, DATE
FROM sales
WHERE p_id =2
group by date
) as B ON A.s_id = B.s_id
where B.p_id=2
group by A.date, A.p_id
It returned:
p_id date A Bs difference
1 2015-10-01 21 NULL NULL
2 2015-10-01 12 12 0
1 2015-10-02 11 NULL NULL
2 2015-10-02 10 10 0
1 2015-10-15 22 NULL NULL
2 2015-10-16 20 20 0
What am i doing wrong here? and what is the correct way of doing it? any help would be appreciated.
A full join isn't needed. You can use conditional aggregation instead:
select
date,
sum(case when p_id = 1 then amount_sold else 0 end) a,
sum(case when p_id = 2 then amount_sold else 0 end) b,
sum(case when p_id = 1 then amount_sold else 0 end)
- sum(case when p_id = 2 then amount_sold else 0 end) difference
from sales
where p_id in (1,2)
group by date