MySQL missing values from column - mysql

Radio_ID | Log_ID
-----------------
1 | 1
1 | 2
1 | 4
1 | 7
1 | 10
2 | 1
2 | 2
2 | 3
2 | 5
Is it possible to get the following output in a single sql statement?
Output:
Radio_ID | Log_ID
-----------------
1 | 3
1 | 5
1 | 6
1 | 8
1 | 9
2 | 4
Logic: returns missing values for each id < max value for id (ex 10 is max value for radio id 1 and missing values are 3,5,6,8,9).
I have a data parser for a radio device and when the radio is outside the coverage area it doesn't send data, so I have to send a new request for missing Log_IDs.
Thank you!

Let me assume that you have a table called numbers with integers of a sufficient range.
Then you can do:
select r.radio_id, n.n as MissingLogId
from (select radio_id, min(log_id) as minli, max(log_id) as maxli
from table t
group by radio_id
) r join
numbers n
on n.n between r.minli and r.maxli left join
table t
on t.radio_id = r.radio_id and t.log_id = n.n
where t.radio_id is null;
The idea is to generate all the numbers for each radio id (between the minimum and maximum id) using a join. Then existing values are filtered out.
If you don't have a numbers table handy, you need to create one large enough. This would probably work:
create table numbers (n int primary key);
insert into numbers(n)
select (#rn := #rn + 1) as n
from table cross join (#rn := 0);

Related

MySQL - select distinct value from two column

I have a table with the following structure:
IdM|IdS
-------
1 | 2
1 | 3
1 | 4
2 | 1
2 | 3
2 | 4
3 | 1
3 | 2
3 | 3
3 | 4
How could I make a select statement on this table, which will return some rows of this table, where in each row, a specific id appears only one, indifferent on which column it is specified?
For the above result set, I would like a query that would return:
-------
1 | 2
3 | 4
-------
To give another example, if you would omit the first row in the original dataset:
IdM|IdS
-------
1 | 3
1 | 4
2 | 1
2 | 3
2 | 4
3 | 1
3 | 2
3 | 3
3 | 4
the result set should be:
-------
1 | 3
2 | 4
-------
That's actually an interesting problem. If I follow you correctly, you want to iterate through the dataset and only retain rows where both values were never seen before. You could use a recursive query:
with recursive
data as (
select idm, ids, row_number() over(order by idm, ids) rn
from mytable
where idm <> ids
),
cte as (
select idm, ids, rn, 1 as to_keep , concat(idm, ',', ids) visited from data where rn = 1
union all
select d.idm, d.ids, d.rn,
(not find_in_set(d.idm, c.visited) and not find_in_set(d.ids, c.visited)),
case when (not find_in_set(d.idm, c.visited) and not find_in_set(d.ids, c.visited))
then concat_ws(',', c.visited, d.idm, d.ids)
else c.visited
end
from cte c
inner join data d on d.rn = c.rn + 1
)
select idm, ids from cte where to_keep
The first CTE enumerates the rows ordered by both columns. Then the recursive query walks the resultset, checks if both values are new, and sets a flag accordingly of the columns. Flagged numbers are retained to be used for filtering in the following iteration.
Demo on DB Fiddle
Note that, given your requirement, not all values may appear in the resultset. Consider the following dataset:
idm ids
+-----+---
1 2
1 3
1 4
Your logic will only return the first row.

Select data from single column as multiple record

I have the following table structure in a mysql database.
id | files | status
1 a.pdf,b.pdf,c.pdx 1
2 d.pdf,e.pdf.g.pdf 2
3 x.pdf,k.pdf,y.pdf 1
As you can see, the attachments are all stored on a single line.
My query is supposed to select all rows where status = 1 so i'm expecting the data in the following format.
1 1 a.pdf 1
2 1 b.pdf 1
3 1 c.pdf 1
4 3 x.pdf 1
5 3 k.pdf 1
6 3 y.pdf 1
Unfortunately, I am unsure which operator I can use to accomplish this. I'm aware SQL has pivot but i doubt even that can address my issue.
As a result, I would appreciate if I could get any help in the condition of the select query.
Regards
Try this:
SET #row_num = 0;
SELECT
(#row_num := #row_num + 1) ROW_NUM,
id,
SUBSTRING_INDEX(SUBSTRING_INDEX(files, ',', idx), ',', -1) FileName,
status
FROM
test
JOIN
(SELECT 1 idx UNION ALL SELECT 2 idx UNION ALL SELECT 3 idx UNION ALL SELECT 4 idx UNION ALL SELECT 5 idx) idxs
on idxs.idx - 2 < LENGTH(files) - LENGTH(REPLACE(files, ",", ""))
WHERE status = 1
;
Just add as many indexes as your max count of files in a string.
Note that there should be no comma fater last filename. This is magic -2 is responsible for.
Consider the following...
I have a table (ints) of integers (0-9)...
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(id SERIAL PRIMARY KEY
,files VARCHAR(100) NOT NULL
,status INT NOT NULL
);
INSERT INTO my_table VALUES
(1,'a.pdf,b.pdf,c.pdx',1),
(2,'d.pdf,e.pdf,g.pdf',2),
(3,'x.pdf,k.pdf,y.pdf',1);
SELECT DISTINCT id
, SUBSTRING_INDEX(SUBSTRING_INDEX(files,',',i2.i*10+i1.i+1),',',-1) file
, status
FROM my_table
, ints i1
, ints i2
WHERE status = 1
ORDER
BY id, i2.i*10+i1.i;
+----+-------+--------+
| id | file | status |
+----+-------+--------+
| 1 | a.pdf | 1 |
| 1 | b.pdf | 1 |
| 1 | c.pdx | 1 |
| 3 | x.pdf | 1 |
| 3 | k.pdf | 1 |
| 3 | y.pdf | 1 |
+----+-------+--------+

Loop unique insert into temporary table MySql stored procedure

I have two tables that look like this:
Table 1
Type 1 | Type 2 | Type 3 | ...
1 | 3 | 0 | ...
Table 2
Type 1 | Type 2 | Type 3 | ...
3 | 2 | 1 | ...
I would like to combine them into a temporary table like this:
Temporary Table
UID | Type | Table
1 | Type 1 | 1
2 | Type 2 | 1
3 | Type 2 | 1
4 | Type 2 | 1
7 | Type 1 | 2
8 | Type 1 | 2
9 | Type 1 | 2
10 | Type 2 | 2
11 | Type 2 | 2
Essentially, the numbers in tables 1 and 2 are totals and I want to break them out into individual rows in this temporary table.
I started going down the path of selecting from both tables and storing the values into temporary variables. I was then going to loop through every single variable and insert into the temporary table. But I have about 15 columns per table and there has got to be an easier way of doing this. I just don't know what it is.
Does anyone have any insight on this? My knowledge is incredibly limited on MySql stored procedures.
Not sure of an easy way to do this. One option would be to have a numbers table. Heres a quick approach to getting 1-10 in a common-table-expression (change as needed).
Then you could join to each table and each type, using union all for each subset. Here is a condensed version:
with numbers as (select 1 n union all select 2 union all
select 3 union all select 4 union all select 5 union all
select 6 union all select 7 union all select 8 union all
select 9 union all select 10)
select 'type1' as type, '1' as tab
from numbers n join table1 t on n.n <= t.type1
union all
select 'type2' as type, '1' as tab
from numbers n join table1 t on n.n <= t.type2
union all
select 'type1' as type, '2' as tab
from numbers n join table2 t on n.n <= t.type1
union all
select 'type2' as type, '2' as tab
from numbers n join table2 t on n.n <= t.type2
Demo Fiddle

PHPMyAdmin - Update 30k rows with looped number sequence

I hope i'm explaining this properly... but i'm trying to update a column in a table with 30k rows with a repeated sequence.
I've populated entire columns before with random numbers using:
UPDATE locations SET template = CAST((RAND() * 4)+1 AS UNSIGNED);
Which gave:
2
4
5
1
3
etc. in a random fashion throughout the 30k rows...
I would like to enter a query that can produce a repeated sequence like:
1
2
3
4
5
1
2
3
4
5
across all 30k rows.
I've been looking into loops and auto increments but can't get it to work.
Any help much appreciated :)
Perhaps using a variable will do for example
DROP TABLE IF EXISTS T;
CREATE TABLE T(ID INT, SEQNO INT);
INSERT INTO T VALUES (1,NULL),(2,NULL),(3,NULL),(4,NULL),(5,NULL),(6,NULL),(7,NULL);
UPDATE T
SET SEQNO = (SELECT IF(#RN = 2 ,#RN:=1,#RN:=#RN + 1) FROM (SELECT #RN:=0) R)
WHERE 1 = 1
+------+-------+
| ID | SEQNO |
+------+-------+
| 1 | 1 |
| 2 | 2 |
| 3 | 1 |
| 4 | 2 |
| 5 | 1 |
| 6 | 2 |
| 7 | 1 |
+------+-------+
Thanks for the suggestions... I had a hard time finding an answer but eventually found something that would do exactly what I was after. I must admit it is far beyond my capabilities, but here it is:
SET #row_number = 0;
SET #max_num = 75;
update locations loc1
join (
select
if ((num % #max_num) = 0, #max_num, (num % #max_num)) as num2,
a.*
from (
select
(#row_number:=#row_number + 1) AS num,
loc.*
from locations loc
ORDER BY num
) a
order by num, num2
) loc2 on (loc2.id = loc1.id)
set loc1.colname = loc2.num2;

Mysql Select records from a table loop group by field

I have a table with products.
The table has a companyId field.
Let's describe it like this:
id --- companyId
1 | 2
2 | 3
3 | 4
4 | 2
5 | 3
6 | 1
7 | 4
I want to select all the records ordered by companyId but with the company id looping, as so:
id --- companyId
6 | 1
1 | 2
2 | 3
3 | 4
4 | 2
5 | 3
7 | 4
How can I achieve it?
You can achieve this using MySQL user defined variables
SELECT
t.id,
t.companyId
FROM
(
SELECT
*,
IF(#sameCompany = companyId , #rn := #rn + 1,
IF(#sameCompany := companyId, #rn := 1,#rn := 1)
) AS rn
FROM companytable
CROSS JOIN (SELECT #sameCompany := -1, #rn := 1) AS var
ORDER BY companyId
) AS t
ORDER BY t.rn , t.companyId
See Demo
Explanation:
First sort the data according to companyId so that the same company ids stick together.
Now take a walk along this sorted result and assign a sequentially increasing row number every time you see the same companyId otherwise assign 1 as row number.
Now name this sorted result (with row number) t.
Finally sort these data (t) according to ascending row number and ascending companyId.