MySQL finding gaps in column with multiple ID - mysql

I would like to find gaps in following table:
create table sequence
(
`Id` int,
`Value` int not null,
PRIMARY KEY (`Id`,`Value`)
);
insert into sequence
( `Id`, `Value` )
values
(10, 0 ),
(10, 1 ),
(10, 4 ),
(10, 5 ),
(10, 6 ),
(10, 7 ),
(11, 0 ),
(11, 1 ),
(11, 2 ),
(11, 5 ),
(11, 7 );
Expeced result is somthing like:
10 | 2-3
11 | 3-4
11 | 6
or
10 | 2
10 | 3
11 | 3
11 | 4
11 | 6
I know, that value of the colum 'Value' is between 0 and 7.
Is it possible to do it using MySQL?
EDIT 1
Based on answers I come with this:
SELECT Tbl1.Id,
startseqno,
Min(B.Value) - 1 AS END
FROM (SELECT Id,
Value + 1 AS StartSeqNo
FROM SEQUENCE AS A
WHERE NOT EXISTS (SELECT *
FROM SEQUENCE AS B
WHERE B.Id = A.id
AND B.Value = A.Value + 1)
AND Value < (SELECT Max(Value)
FROM SEQUENCE B
WHERE B.Id = A.Id)) AS Tbl1,
SEQUENCE AS B
WHERE B.Id = Tbl1.Id
AND B.Value > Tbl1.startseqno
But now I am getting just
10 | 2 | 3
Please, does somebody know, how to fix it?
sqlfiddle

You can do this with not exists:
select s.*
from sequence s
where not exists (select 1 from sequence s2 where s2.id = s.id and s2.value = s.value + 1) and
exists (select 1 from sequence s2 where s2.id = s.id and s2.value > s.value);
The exists clause is important so you don't report the final value for each id.
EDIT:
Here is a better approach:
select s.value + 1 as startgap,
(select min(s2.value) - 1 from sequence s2 where s2.id = s.id and s2.value > s.value) as endgap
from sequence s
where not exists (select 1 from sequence s2 where s2.id = s.id and s2.value = s.value + 1) and
exists (select 1 from sequence s2 where s2.id = s.id and s2.value > s.value);

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

MySQL Add missing Months in resultset

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:

Sql update where left join count equal to 3

Hi I'm trying to build a sql query where I update the value of a table where the left join of another table is equal to 3.
Example when a vehicle has 3 photos.
The query I've written thus far, it seems to fail with group by though.
UPDATE domain.vehicle_listing AS t0 LEFT OUTER JOIN photo AS t1 ON t0.id = t1.vehicle_listing_id
SET t0.active = 0
WHERE `create_date` >= '2015-5-2'
AND user_profile_id is not null
AND t0.active = 1
GROUP BY t1.vehicle_listing_id
HAVING COUNT(DISTINCT t1.id) = 3
ORDER BY create_date desc;
Vehicle_Listing
id
Photo
id, vehicle_listing_id, photo_url
OneToMany relationship with photo.
You can also use exists
UPDATE vehicle_listing AS t0
SET t0.active = 0
WHERE t0.`create_date` >= '2015-05-02'
AND t0.user_profile_id is not null
AND t0.active = 1
AND EXISTS (
SELECT 1
FROM photo
WHERE vehicle_listing_id=t0.id
GROUP BY vehicle_listing_id
HAVING COUNT(DISTINCT id) = 3
)
Sample data for vehicle_listing
INSERT INTO vehicle_listing
(`id`, `title`, `create_date`, `active`,user_profile_id)
VALUES
(1, 'test', '2015-05-02 00:00:00', 1,1),
(2, 'test1', '2015-05-02 00:00:00', 1,1)
;
Sample data for photo
INSERT INTO photo
(`id`, `vehicle_listing_id`, `photo_url`)
VALUES
(1, 1, 'image.jpg'),
(2, 1, 'image.jpg'),
(3, 1, 'image.jpg'),
(4, 2, 'image.jpg'),
(5, 2, 'image.jpg')
;
Sample Output
id title create_date active user_profile_id
1 test May, 02 2015 00:00:00 0 1
2 test1 May, 02 2015 00:00:00 1 1
DEMO
It is silly to use a left join for this. You want inner join:
UPDATE cardaddy.vehicle_listing vl INNER JOIN
(SELECT p.vehicle_listing_id, count(*) as cnt
FROM photo p
GROUP BY p.vehicle_listing_id
) p
ON vl.id = p.vehicle_listing_id AND
p.cnt = 3
SET vl.active = 0
WHERE vl.create_date >= '2015-05-02' AND
vl.user_profile_id IS NOT NULL AND
vl.active = 1;
(Assuming that user_profile_id is in vehicle_listing.)
UPDATE cardaddy.vehicle_listing AS t0
LEFT OUTER JOIN (
SELECT vehicle_listing_id, count(1) AS counter
FROM photo
GROUP BY vehicle_listing_id
) AS t1
ON t0.id = t1.vehicle_listing_id
AND t1.counter = 3
SET t0.active = 0
WHERE `create_date` >= '2015-5-2'
AND user_profile_id IS NOT NULL
AND t0.active = 1
AND t1.vehicle_list_is IS NOT NULL

MySQL matrix multiplication

I am trying to write matrix multiplication for MySQL and am kinda stuck:
basically, my matrices are stored in format
[row#, column#, matrixID, value], so for example matrix [3 x 2] would be something like:
[row#, column#, matrixID, value]
1 1 mat01 1
1 2 mat01 2
1 3 mat01 3
2 1 mat01 4
2 2 mat01 5
2 3 mat01 6
being equivalent to: [[1 2 3],[4 5 6]]
following does calculation of single element of matrix1 * matrix2 quite well:
SELECT SUM(row1.`val` * col2.`val`)
FROM matValues row1
INNER JOIN `matValues` col2
WHERE row1.`row` = 1 AND row1.`mID`='matrix1' AND
col2.`mID`='matrix2' AND col2.`col` = 1 AND row1.col = col2.row
wrapping this into function and then using another function to iterate over row and column numbers might work, but I have problems with generating this set of numbers and iterating over them using SQL.
Any advice / suggestions are welcome
Try:
select m1.`row#`, m2.`column#`, sum(m1.value*m2.value)
from matValues m1
join matValues m2 on m2.`row#` = m1.`column#`
where m1.matrixID = 'mat01' and m2.matrixID = 'mat02'
group by m1.`row#`, m2.`column#`
Example here.
(Replace 'mat01' and 'mat02' with suitable matrixID values.)
You can do the entire calculation in SQL. You only give an example with a single matrix, which because it is not square, cannot be multiplied by itself.
Here is the idea:
SELECT mout.row, mout.col, SUM(m1.value*m2.value)
FROM (select distinct row from matValues cross join
select distinct COL from matValues
) mout left outer join
matValues m1
on m1.row = mout.row left outer join
matValues m2
on m2.col = mout.col and
m2.row = m1.col
I know this is SQL-Server syntax, but it should give you a start on the corresponding MySql syntax. The sparse matrix nature seems to handle well.
with I as (
select * from ( values
(1,1, 1),
(2,2, 1),
(3,3, 1)
) data(row,col,value)
)
,z_90 as (
select * from ( values
(1,2, 1),
(2,1,-1),
(3,3, 1)
) data(row,col,value)
)
,xy as (
select * from ( values
(1,2, 1),
(2,1, 1),
(3,3, 1)
) data(row,col,value)
)
,x_90 as (
select * from ( values
(1,1, 1),
(2,3, 1),
(3,2,-1)
) data(row,col,value)
)
select
'I * z_90' as instance,
a.row,
b.col,
sum( case when a.value is null then 0 else a.value end
* case when b.value is null then 0 else b.value end ) as value
from I as a
join z_90 as b on a.col = b.row
group by a.row, b.col
union all
select
'z_90 * xy' as instance,
a.row,
b.col,
sum( case when a.value is null then 0 else a.value end
* case when b.value is null then 0 else b.value end ) as value
from z_90 as a
join xy as b on a.col = b.row
group by a.row, b.col
union all
select
'z_90 * x_90' as instance,
a.row,
b.col,
sum( case when a.value is null then 0 else a.value end
* case when b.value is null then 0 else b.value end ) as value
from z_90 as a
join x_90 as b on a.col = b.row
group by a.row, b.col
order by instance, a.row, b.col
yields:
instance row col value
----------- ----------- ----------- -----------
I * z_90 1 2 1
I * z_90 2 1 -1
I * z_90 3 3 1
z_90 * x_90 1 3 1
z_90 * x_90 2 1 -1
z_90 * x_90 3 2 -1
z_90 * xy 1 1 1
z_90 * xy 2 2 -1
z_90 * xy 3 3 1
However, I suggest you also check out performing this on your graphics card. NVIDIA has a good example of implementing matrix multiplication in theri C Programming Guide.