How to add IDs to each unique value in a table - mysql

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.

Related

Sql select where array in column

In my query I use join table category_attributes. Let's assume we have such rows:
category_id|attribute_id
1|1
1|2
1|3
I want to have the query which suites the two following needs. I have a variable (php) of allowed attribute_id's. If the array is subset of attribute_id then category_id should be selected, if not - no results.
First case:
select * from category_attributes where (1,2,3,4) in category_attributes.attribute_id
should give no results.
Second case
select * from category_attributes where (1,2,3) in category_attributes.attribute_id
should give all three rows (see dummy rows at the beginning).
So I would like to have reverse side of what standard SQL in does.
Solution
Step 1: Group the data by the field you want to check.
Step 2: Left join the list of required values with the records obtained in the previous step.
Step 3: Now we have a list with required values and corresponding values from the table. The second column will be equal to required value if it exist in the table and NULL otherwise.
Count null values in the right column. If it is equal to 0, then it means table contains all the required values. In that case return all records from the table. Otherwise there must be at least one required value is missing in the table. So, return no records.
Sample
Table "Data":
Required values:
10, 20, 50
Query:
SELECT *
FROM Data
WHERE (SELECT Count(*)
FROM (SELECT D.value
FROM (SELECT 10 AS value
UNION
SELECT 20 AS value
UNION
SELECT 50 AS value) T
LEFT JOIN (SELECT value
FROM Data
GROUP BY value) D
ON ( T.value = D.value )) J
WHERE value IS NULL) = 0;
You can use group by and having:
select ca.category_id
from category_attributes ca
where ca.attribute_id in (1, 2, 3, 4)
group by ca.category_id
having count(*) = 4; -- "4" is the size of the list
This assumes that the table has no duplicates (which is typical for attribute mapping tables). If that is a possibility, use:
having count(distinct ca.attribute_id) = 4
You can aggregate attribute_id into array and compare two array from php.
SELECT category_id FROM
(select category_id, group_concat(attribute_id) as attributes from category_attributes
order by attribute_id) t WHERE t.attributes = (1, 2, 3);
But you need to find another way to compare arrays or make sure that array is always sorted.

How do i make duplicates in a existing column unique?

I am using MySQL 5.6 Server. I use Navicat to work in the DB.
I have searched and found alot of possible solutions to my problem but none of them have worked for me.
I have a table with around 36000 rows. I have a column wich allowes duplicate entrys but i would like to make the duplicates in the row unique. Column with duplicates
I can find my duplicates using this query.
But there is to many to manually edit them all. Search Result
SELECT name, COUNT(ItemTemplate_ID)
FROM itemtemplate
GROUP BY ItemTemplate_ID
HAVING ( COUNT(ItemTemplate_ID) > 1 )
What i am looking for is a way to do one of these things.
Update the duplicates with new unique entries.
Add a text entry in another column for every duplicates. (I have a couple of empty columns i can use to add some text too.
Update the entire column with unique entries. ( Doesnt matter what its calle just has to be unique.)
Thanks in advance.
Edit - There allready is a unique column called Id_nb.
The column i want to change entries in should not be unique.
Let me assume that you do not have a unique column in the table. If so, you can do this with variables:
set #rn := 0;
set #it := '';
update itemtemplate it
set it.ItemTemplate_ID = (case when #it = it.ItemTemplate_ID
then concat_ws('_', it.name, (#rn := #rn + 1))
when #it := it.ItemTemplate_ID
then if(#rn := 0, it.ItemTemplate_ID, it.ItemTemplate_ID)
else if(#rn := 0, it.ItemTemplate_ID, it.ItemTemplate_ID)
end)
order by it.ItemTemplate_ID;
This method does assume that there are not already columns with names like "ABC" and "ABC_1".
Update the duplicates with new unique entries.
Let's assume that you have a unique column named id.
update itemtemplate set ItemTemplate_ID = (select md5(uuid())) where
FIND_IN_SET(id,(SELECT GROUP_CONCAT(id) FROM itemtemplate GROUP BY ItemTemplate_ID HAVING ( COUNT(ItemTemplate_ID) > 1 ) ));
Add a text entry in another column for every duplicates. (I have a couple of empty columns i can use to add some text too.
update itemtemplate set ItemTemplate_ID = (select md5(uuid())), emptyColumn='text' where
FIND_IN_SET(id,(SELECT GROUP_CONCAT(id) FROM itemtemplate GROUP BY ItemTemplate_ID HAVING ( COUNT(ItemTemplate_ID) > 1 ) ));
Sorry, I don't quite understand the third question.
By the way,if you have executed the first sql, the second sql will not work anymore.

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.

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

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.