I have a table, to which I need to add an increment column, however the increment should happen based on the existing values in the other columns.
select * from mytable;
first_col second_col
A B
A C
A D
A E
A B
A D
Now, I want to add another column, say new_column whose value increments uniquely on the basis of the first_col and second_col.
The column should be populated like these :
first_col second_col new_col
A B 1
A C 1
A D 1
A E 1
A B 2
A D 2
A B 3
Is it possible to do this using some sort of an MySQL in built auto increment strategy.
Using a temporary table with an auto_incremented id column you could do
create temporary table tt (
id int auto_increment primary key,
col1 varchar(32),col2 varchar(32));
insert into tt
select col1, col2 from origtable;
select col1, col2,
(select count(*)+1 from tt s
where s.col1=m.col1 and s.col2=m.col2
and s.id<m.id) n
frm tt m
There is no built in increment method in MySQL, but you can do this with either correlated subqueries or variables:
select t.*,
(#rn := if(#c = concat(first_col, ':', second_col), #rn + 1,
#c := concat(first_col, ':', second_col), 1, 1
)
) as new_col
from mytable t cross join
(select #rn := 0, #c := '') params
order by first_col, second_col;
Note: this re-orders the results. If you want the results in the original order, then you need a column that specifies that ordering.
Here's how you can do it, replace col1val and col2val with the values to be inserted.
INSERT INTO mytable (first_col, second_col, new_col)
VALUES (SELECT col1val, col2val SUM(COUNT(*), 1)
FROM mytable
GROUP BY first_col, second_col
HAVING first_col = col1val AND second_col = col2val)
Note that this is an insert query, and will affect only newly inserted values.
Related
I want to get a random row for each group when using GROUP BY in MySQL 5.7. The most clean way to do it from my research is doing something like this:
SELECT ANY_VALUE(column_1), ANY_VALUE(column_2), ..., ANY_VALUE(column_n)
FROM table
GROUP BY column
Since there is no syntax for something like ANY_VALUE(*) or ANY_VALUE(column_1, column2, ..., column_n) I am left confused if with the above query each value can come from a different row, or if all ANY_VALUE fields will come from the same row.
If you want a random row, use row_number():
select t.*
from (select t.*,
row_number() over (partition by column order by rand()) as seqnum
from t
) t
where seqnum = 1;
I am guessing that this is also faster than group by, but you can check if that is the case.
In MySQL 5.7, you can use variables:
select t.*
from (select t.*,
(#rn := if(#c = column, #rn + 1,
if(#c := column, 1, 1)
)
) as rn
from (select t.* from t order by column, rand) t cross join
(select #c := '', #rn := 0) params
) t
where rn = 1;
Assuming the following schema and sample data:
create table tbl(
id int auto_increment primary key,
grp int not null,
val int not null,
index (grp)
);
insert into tbl (grp, val) values (1, 1);
insert into tbl (grp, val) values (1, 2);
insert into tbl (grp, val) values (1, 3);
insert into tbl (grp, val) values (2, 1);
insert into tbl (grp, val) values (2, 2);
Get distinct groups in a derived table (or use the base table for groups, if you have). Get a random primary key in a subquery in SELECT clause with ORDER BY rand() LIMIT 1. Then join the result as a derived table with the base table.
select t.*
from (
select (
select id
from tbl t
where t.grp = g.grp
order by rand()
limit 1
) as id
from (select distinct grp from tbl) g
) r
join tbl t using (id);
Result would be something like
| id | grp | val |
| --- | --- | --- |
| 2 | 1 | 2 |
| 4 | 2 | 1 |
View on DB Fiddle
Is it possible to insert data at the start on a column? Lets say like this.
What i mean is,is it possible to insert a bunch of data from 1 column of a table into another table starting at the row position?
tysm
SQL tables represent unordered sets. So, you cannot do exactly what you want. You can put the data side-by-side using variables and aggregation:
select max(col1) as col1, max(col2) as col2
from ((select (#rn1 := #rn1 + 1) as rn, col1, NULL as col2
from table1 t1 cross join (select #rn1 := 0) params
order by ??
) union all
(select (#rn2 := #rn2 + 1) as rn, NULL, col1
from table2 t2 cross join (select #rn2 := 0) params
order by ??
)
) tt
group by rn;
The order by ?? is for the column that specifies the ordering. If you don't care about the ordering, then just remove the order by. The ordering within the two columns will be arbitrary.
I want to find all NULL values in column parameter_id and set them to lowest unused parameter_id.
I have query which will find lowest unused parameter_id, I also know how to get list of NULL values.
SELECT MIN(t1.parameter_id)+1 FROM table AS t1 WHERE NOT EXISTS (SELECT * FROM table AS t2 WHERE t2.parameter_id = t1.parameter_id+1)
I can get list of all rows with parameter_id=NULL, then make query to find current lowest unused parameter_id and then update parameter_id to that lowest unused number. Since table has 50.000 rows, this approach would create thousands of queries (50.000 * 2 per row).
Is there way to run "single query" which will find all parameter_id=NULL and update them all to current lowest unused parameter_id?
Here is table decrtiption (MySQL 5.5):
id (INT) primary key, auto_increment
parameter_id (INT) default NULL
Sample data:
# id, parameter_id
1, NULL
2, 1
3, NULL
4, 5
5, 3
Desired result:
# id, parameter_id
1, 2
2, 1
3, 4
4, 5
5, 3
EDIT:
I distilled what I want to single query. I simply need to run this query until there is 0 rows affected by UPDATE.
UPDATE `table`
SET parameter_id=
(SELECT *
FROM
(SELECT MIN(t1.parameter_id)+1
FROM `table` AS t1
WHERE NOT EXISTS
(SELECT *
FROM `table` AS t2
WHERE t2.parameter_id = t1.parameter_id+1)) AS t4)
WHERE parameter_id IS NULL LIMIT 1
The following enumerates the unused parameter ids:
select t.*, (#rn := #rn + 1) as seqnum
from table t cross join
(select #rn := 0) params
where not exists (select 1 from table t2 where t2.parameter_id = t.id)
order by t.id;
(You might want to put this in a temporary table with an index on seqnum for the subsequent query.)
The problem is getting a join key for the update. Here is a bit of a kludge: I'm going to add a column, enumerate it, and then drop it:
alter table `table` add column null_seqnum;
update `table` t cross join (select #rn1 := 0) params
set null_seqnum = (#rn1 := #rn1 + 1)
where parameter_id is null;
update `table` t join
(select t.*, (#rn := #rn + 1) as seqnum
from `table` t cross join
(select #rn := 0) params
where not exists (select 1 from `table` t2 where t2.parameter_id = t.id)
order by t.id
) tnull
on t.null_seqnum = tnull.seqnum
set t.parameter_id = tnull.id;
alter table `table` drop column null_seqnum;
current situation is to add below value of A01, B03, Z11 and X21 in repetitive way in field code for 400 hundreds row of data in table BabyCode.
Above is current table - without value in 'Code" column
Above is to be updated table - repetitive value is added in 'Code' column
You can do this:
INSERT INTO BabyCode
SELECT Codes.Code
FROM
(
SELECT id
FROM
(
SELECT t3.digit * 100 + t2.digit * 10 + t1.digit + 1 AS id
FROM TEMP AS t1
CROSS JOIN TEMP AS t2
CROSS JOIN TEMP AS t3
) t
WHERE id <= 400
) t,
(
SELECT 1 AS ID, 'A01' AS Code
UNION ALL
SELECT 2, 'B03'
UNION ALL
SELECT 3, 'Z11'
UNION ALL
SELECT 4, 'X21'
) codes;
But you will need to define a temp table, to use as an anchor table:
CREATE TABLE TEMP (Digit int);
INSERT INTO Temp VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
SQL Fiddle Demo
This will insert 400 hundred rows of the values A01, B03, Z11, and X21, into the code column in the table BabyCode.
You could put the four values into a virtual table identical to that used in #Mahmoud Gamal's answer, and, if the ID values in your table start at 1 and are sequential (have neither gaps nor duplicates), you could use the following method to join to the virtual table and update the target's Code column:
UPDATE YourTable t
INNER JOIN (
SELECT 1 AS ID, 'A01' AS Code
UNION ALL SELECT 2, 'B03'
UNION ALL SELECT 3, 'Z11'
UNION ALL SELECT 4, 'X21'
) x
ON (t.ID - 1) MOD 4 + 1 = x.ID
SET t.Code = x.Code
;
Otherwise you could use variables to assign 1, 2, 3, 4 sequentially to every row of your table, then you would be able join to the virtual table using those values:
UPDATE YourTable t
INNER JOIN (
SELECT ID, #rnk := CASE WHEN #rnk = 4 THEN 0 ELSE #rnk END + 1 AS rnk
FROM YourTable
CROSS JOIN (SELECT #rnk := 0) x
ORDER BY ID
) r ON t.ID = r.ID
INNER JOIN (
SELECT 1 AS ID, 'A01' AS Code
UNION ALL SELECT 2, 'B03'
UNION ALL SELECT 3, 'Z11'
UNION ALL SELECT 4, 'X21'
) x
ON r.rnk = x.ID
SET t.Code = x.Code
;
Both queries can be played with at SQL Fiddle:
Method 1
Method 2
I have a column I want to sort by, with periodical updates on the rank (daily). I currently use this in code
get all rows from table order by column
rank = 1
foreach row in table
update row's rank to rank
rank++
this takes an update for each row in MySQL. Are there more efficient ways to do this?
Use an update with a join:
set #rank := 0;
update tbl a join
(select id, #rank := #rank + 1 as new_rank from tbl order by col) b
on a.id = b.id set a.rank = b.new_rank;
If expecting to have a lot of rows, you'll get the best performance by doing the join against a table that is indexed, e.g.:
set #rank := 0;
create temporary table tmp (id int primary key, rank int)
select id, #rank := #rank + 1 as rank from tbl order by col;
update tbl join tmp on tbl.id = tmp.id set tbl.rank = tmp.rank;
Finally, you could potentially make it faster by skipping the update step entirely and swapping in a new table (not always feasible):
set #rank := 0;
create table new_tbl (id int primary key, rank int, col char(10),
col2 char(20)) select id, #rank := #rank + 1 as rank, col, col2
from tbl order by col;
drop table tbl;
rename table new_tbl to tbl;