Re-sequence column data to fill in gaps left after row deletion? - mysql

I am trying to find a way to re-sequence the content of a column after deletions leave a missing value.
The troubles..
1 - the values are not unique, but can repeat any number of times, so SORT and ROW_NUMBER won't work.
2 - the values are not sequential in presentation, they can appear in any order, but must always -as a whole- progress from 1 to the maximum value without any gaps.
A visual of what I am trying to accomplish..
ID, Case, Othercols
1, 1, data
2, 3, data
6, 5, data
8, 3, data
I need to create a procedure to find the missing values in Case and pull all higher values down to fill in the gaps.
ID, Case, OtherCols
1, 1, data
2, 2, data
6, 3, data
8, 2, data
I know there are better ways to resolve the issue than this, but the database is part of a server so I cannot change how it operates, only the content, and even that only at specific times.
I am only able to remove the 'failed' data and re-sequence (so far by hand) to get the software to take the result as valid.

If I understand correctly, you can resequence the values using variables:
select case, (#rn := #rn + 1) as newval
from (select distinct case from table t order by case) t cross join
(select #rn := 0) vars;
With this as a lookup table, you can then renumber the values:
update table t join
(select case, (#rn := #rn + 1) as newval
from (select distinct case from table t order by case) t cross join
(select #rn := 0) vars
) tt
on t.case = tt.case
set t.case = tt.newval;
Note: this uses case as a column name even though that is not allowed in MySQL.

Related

MySQL add column with numeric category taking an increment based on another column value and position

I have a table in MySQL as described below
rownum,value,status
1,16,1
2,32,1
3,16,1
4,23,0
5,33,0
6,16,0
7,22,0
8,13,1
9,43,1
10,32,1
11,45,0
12,28,0
13,23,0
14,28,0
15,31,1
16,13,1
17,44,1
Here the third column shows the status of the row basis which I want to add a new column. The logic required is for every set of repeated values of 1 in column "status" the new column will get a number assigned to all of those rows. And this number will increment with +1 when the next set of 1s are found. the output table should look as below.
rownum,value,status,category
1,16,1,1
2,32,1,1
3,16,1,1
4,23,0,null
5,33,0,null
6,16,0,null
7,22,0,null
8,13,1,2
9,43,1,2
10,32,1,2
11,45,0,null
12,28,0,null
13,23,0,null
14,28,0,null
15,31,1,3
16,13,1,3
17,44,1,3
I am a bit confused on how this logic can be created and any inputs will be really helpful. Thanks!
You can achieve this in MySQL using User-defined Session variables, and using conditional functions like If(). We store previous row's status value in #stat variable, and compare it with current row's status value, to get results as intended.
But if it is just for display purposes, you should seriously consider handing this in your application code.
Nevertheless, try the following query (DB Fiddle DEMO):
SELECT
IF( dt.status = 0,
NULL,
IF( #stat = 0, #cat := #cat + 1, #cat )
) AS category,
dt.rownum,
dt.value,
#stat := dt.status AS status
FROM
(
SELECT
rownum,
value,
status
FROM your_table
ORDER BY rownum ASC
) AS dt
CROSS JOIN (SELECT #cat := 0,
#stat := 0) AS init_user_vars

MySQL counter for specific situations

I need to generate unique "ids", the catch is, it can be only between 1 - 99999.
The "good" thing is, that it has to be unique only in group with another column.
We have groups, each group has its own "group_id" and each group need something like unique('group_id', 'increment_id')
The 99999 records is enough for several years for each group right now, but it is not enough for all groups together, therefore I cant just create table with AUTO_INCREMENT and inserting there records and taking its auto increment.
For example, if I need 5 records for Group one and three records for Group two, I suppose to get something like this:
group_id, increment_id
1, 1,
1, 2,
1, 3,
1, 4,
1, 5,
2, 1
2, 2,
2, 3
Also, the conflict is not an option, therefore using something like "length" can be tricky, if done programatically (there can be i.e. 10 requests at once, each of them first select length for given group_id and then tries to create 10 rows with same increment_id)
However I am thinking - if I set it up as the value of subselect of count, than it will always be "ok"?
You can create a auxiliar table named counters to manage that:
table: counters
columns: group_id, current_counter
OR
Each time you insert a row increment_id = select max(increment_id)+1 from table_xxx where group_id = group_xxxx
You can use user variables to get the incrementing number within each group_id:
select
t.*,
#rn := if(#group_id = group_id,
#rn + 1,
if(#group_id := group_id, 1, 1)
) increment_id
from (
select group_id
from your_table t
/* some where clauses */
order by group_id
) t
cross join (
select #rn := 0,
#group_id := - 1
) t2

Loop through results of sql query

I have a table with 50 records.
In that table, I just added a column name Number (int). For each of those 50 records the current value is (NULL).
How can I make a simple query ( LOOP ) which will go through all those records (rows) and for each set the row number example (1, 2, 3, 4, 5)
You can use variables with update. If you don't care about the ordering:
update t cross join
(select #rn := 0) params
t.number = (#rn := #tn + 1);
Alternatively, you could create a new table with an auto-increment column and load the data into that table. That way, new records will also be assigned new numbers.

How to add IDs to each unique value in a table

I have a table in mysql which has a column filled with text values, lets say pets. So I would have a column pets with values like: "cat", "dog", "turtle", "cat", "cat", "turtle". In reality there are hundreds of thousands of different pet types in that column, but many repeats.
What I would like to do is create a second column which has an index for each entry in the first column. So for instance, in the above case, we would assign 'cat' the id of 1, 'dog' an id of 2, 'turtle' of 3 etc. and end up with a column with the values: 1, 2, 3, 1, 1, 3
Is there a way to do this? (preferably a quick way as the table is over 7 million rows).
Create a new table with the lookup values. You can do this easily:
create table PetTypes as
select (#rn := #rn + 1) as pettypeid, pettype
from (select distinct pettype from pets) p cross join
(select #rn := 0) params;
Then you can readily add, update, or just use the column in the select:
alter table pets add petttypeid int;
update pets p join
pettypes pt
on p.pettype = pt.pettype
set p.pettypeid = pt.pettypeid;
I would then recommend that you remove the pettype column from the original table.
Note: you can do the above without making a new table. But I think creating a new lookup table for this column is the right way to go.

Can't understand a mysql query

I had a question yesterday about ordering a mysql query by rand(). And I got a good answer here:
https://stackoverflow.com/a/16597706/2333744
THe code for the answer is below.
create temporary table results as
(Select *, #rn := #rn + 1 as rn, rand() as therand
from table1 inner join
table2
on table1.in = table2.in cross join
(select #rn := 0) const
where table1.T = A
);
select *
from results
where therand < 1000/#rn
order by therand
limit 500;
I understand everything except for
cross join (select #rn : = 0) const
I'm not sure what this is doing and if its important. When I remove it I get no performance change. Can anyone understand this part?
The User-Defined Variable #rn used in this case just for making a serial number column as explained in the answer of the previous question where you get this from.
The const is not used as a keyword here ... so don't be 'const-fused' by that. It is just a given name to (select #rn := 0) ... It could have been any other name like A, B, oops, aah, etc ... (see the second link below)
See example use in the folowing links to better understand the User-Defined Variables:
Create a Cumulative Sum Column in MySQL
MySql: Select Query- Make A Cumulative Sum Column
frankly, all this does is to reset the #rn variable. Is is "packed" into a select to avoid running 2 queries. The const means that it is constant, hence only evaluated once.
You could run into trouble when you remove it and add further queries into a single transaction.
Best regards
Zsolt