mysql question... So I would like to auto populate a number, but I would like it to be based on another field. I'm not sure how to explain it other than using this example:
Field 1 | Field 2 (auto populate)
1 | 1
2 | 1
2 | 2
2 | 3
3 | 1
3 | 2
2 | 4
2 | 5
Is there a mysql function expression that can be added to the column?
I know I could do this in my Python script that is feeding the values, but I have to assume it will be faster if the function is baked into mysql.
UPDATE:
Let me add some clarification...
Field 1 will get filled with a value from a script.
There will be multiple entries of Field 1 that are the same number.
I would like Field 2 to auto populate to have a unique number WITHIN that Field 1 number.
You can just store the field1 and generate field2 as increasing integer value within each field1 using user variables like this:
select
t.field1,
#rn := if(field1 = #f1, #rn + 1, if(#fi := field1, 1, 1)) field2
from (
select
t.*
from your_table t
order by field1 --very important
) t cross join (select #rn := 0, #f1 := -1) t2
You could use a trigger which is a function which automatically runs on certain conditions.
Here is the manual http://dev.mysql.com/doc/refman/5.7/en/trigger-syntax.html.
You could for example use a AFTER UPDATE or AFTER INSERT trigger for this purpose.
Setting aside the usual caveats relating to MyISAM tables...
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(field1 INT NOT NULL
,field2 INT NOT NULL AUTO_INCREMENT
,PRIMARY KEY(field1,field2)
) ENGINE = MyISAM;
INSERT INTO my_table (field1) VALUES
(1),(2),(2),(2),(3),(3),(2),(2);
SELECT * FROM my_table;
+--------+--------+
| field1 | field2 |
+--------+--------+
| 1 | 1 |
| 2 | 1 |
| 2 | 2 |
| 2 | 3 |
| 2 | 4 |
| 2 | 5 |
| 3 | 1 |
| 3 | 2 |
+--------+--------+
..but I would question whether there's really any sense storing this information at all...
...hence, a more robust approach...
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(field1 INT NOT NULL
,field2 INT NOT NULL AUTO_INCREMENT PRIMARY KEY
) ENGINE = InnoDB;
INSERT INTO my_table (field1) VALUES
(1),(2),(2),(2),(3),(3),(2),(2);
SELECT x.field1
, CASE WHEN #prev = field1 THEN #i:=#i+1 ELSE #i:=1 END i
, #prev:=field1 prev
FROM my_table x
, (SELECT #prev:=null,#i:=0) vars
ORDER
BY field1
, field2;
+--------+------+------+
| field1 | i | prev |
+--------+------+------+
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 2 | 2 | 2 |
| 2 | 3 | 2 |
| 2 | 4 | 2 |
| 2 | 5 | 2 |
| 3 | 1 | 3 |
| 3 | 2 | 3 |
+--------+------+------+
You probably want to create a trigger. Something like the following:
CREATE TRIGGER `populate_field_2` BEFORE INSERT ON `table_name` FOR EACH ROW BEGIN
DECLARE _new_val INT DEFAULT 0;
SELECT MAX(field_2)+1 INTO _new_val FROM 'table_name' WHERE field_1 = NEW.field_1;
SET NEW.field_2 = _new_val;
END
This would populate field_2 each time you created a new row. You can update the logic to create whatever value for field2 you need.
Related
Let's assume I have the table:
id | val_1 | val_2
1 | 1 | 0
2 | 1 | 1
3 | 1 | 2
4 | 2 | 0
val_2 should be zero at first if there was no rows with val_1 before. Otherwise it should be previous val_2 + 1 for this val_1.
I can't figure it out by myself the best way to do it. The one thing I've invented is trigger after insert, but I think here maybe some other way to do it cleaner and faster?
My code is something like:
DELIMITER $$
CREATE TRIGGER after_table_insert
AFTER INSERT
ON table FOR EACH ROW
BEGIN
UPDATE table SET val_2 = t.val_2 + 1
FROM (
SELECT val_2 FROM table WHERE val_1 = new.val_1 ORDER BY id DESC LIMIT 1
) t
WHERE id = new.id;
END$$
DELIMITER ;
I will appreciate for any help!
Have a great day/night.
You have couple of issues with such setup:
What's going on if you UPDATE or DELETE rows? It can mess up everything with val2. Be careful with that.
Val2 can always be calculated and there is no need to store it.
Having said that, below I will show you a setup with which I will store only id and val1. Then val2 will be calculated within the SELECT statement (so it will always be correct).
CREATE TABLE vals(
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
val INT NOT NULL
);
INSERT INTO vals(val) VALUES(1),(1),(1),(2);
Now what I am going to do is to use the ROW_NUMBER() function (which prints the row number) and run it over a PARTITION BY val:
SELECT id, val,
ROW_NUMBER() OVER (
PARTITION BY val
) AS val2
FROM vals;
We are almost there. Sadly it will offset them by 1 compared to what you need:
+----+-----+------+
| id | val | val2 |
+----+-----+------+
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 1 | 3 |
| 4 | 2 | 1 |
+----+-----+------+
The fix is simple. Just add "-1" to it and you are ready.
SELECT id, val,
-1+ROW_NUMBER() OVER (
PARTITION BY val
) AS val2
FROM vals;
This will produce:
+----+-----+------+
| id | val | val2 |
+----+-----+------+
| 1 | 1 | 0 |
| 2 | 1 | 1 |
| 3 | 1 | 2 |
| 4 | 2 | 0 |
+----+-----+------+
With this solution there is no need to store val2 at all (you can create it as a VIEW if you wish) and it is not vulnerable to the issue when you delete a row (it will continue to work properly).
This is one possibility
Schema (MySQL v5.7)
CREATE TABLE table1 (
`id` INTEGER,
`val_1` INTEGER,
`val_2` INTEGER
);
DELIMITER //
CREATE TRIGGER after_table_insert
BEFORE INSERT
ON table1 FOR EACH ROW
BEGIN
SET #maxval2 = 0;
SELECT max(val_2) + 1 into #maxval2 FROM table1 WHERE val_1 = new.val_1;
IF #maxval2 IS NULL THEN
SET #maxval2 = 0;
END IF;
SET NEW.val_2 = #maxval2;
END//
DELIMITER ;
INSERT INTO table1
(`id`, `val_1`, `val_2`)
VALUES
('1', '1', '0'),
('2', '1', '0'),
('3', '1', '0'),
('4', '2', '0'),
('4', '2', '0');
Query #1
SELECT * FROM table1;
| id | val_1 | val_2 |
| --- | ----- | ----- |
| 1 | 1 | 0 |
| 2 | 1 | 1 |
| 3 | 1 | 2 |
| 4 | 2 | 0 |
| 4 | 2 | 1 |
View on DB Fiddle
I'm trying to find out if my values inserted are auto-incrementing correctly or if for any reason one has failed to be inserted, deleted or gone "missing". I've tried several answers from Stackoverflow but they were mainly pointing out autoincrementable int values so they did not help since mine is a VARCHAR value that follows the following sequence:
AA000001
AA000002
...
AA000100
...
AA213978
and so on...
Thanks for your time.
You can declare SQL Vars in Query and calculate the difference in each iteration, as shown in the example below:
Schema
create table MyTable
( ai int auto_increment primary key,
id varchar(100) not null
);
insert MyTable (id) values
('AA000001'),
('AA000002'),
('AA000005'),
('AA000008'),
('AA000009'),
('AA000010');
Query
select id
FROM
(
select
t.id,
SUBSTRING(t.id,3) as s,
CAST(SUBSTRING(t.id,3) AS UNSIGNED) - #lastId as diff,
if( #lastId = 0, 0, CAST(SUBSTRING(t.id,3) AS UNSIGNED) - #lastId) as Difference,
#lastId := CAST(SUBSTRING(t.id,3) AS UNSIGNED) as dummy
from
`MyTable` t,
( select #lastId := 0) SQLVars
order by
t.id
) d
WHERE diff>1;
This is the inside query (not the final result set of the above)
+----------+--------+------+------------+-------+
| id | s | diff | Difference | dummy |
+----------+--------+------+------------+-------+
| AA000001 | 000001 | 1 | 0 | 1 |
| AA000002 | 000002 | 1 | 1 | 2 |
| AA000005 | 000005 | 3 | 3 | 5 |
| AA000008 | 000008 | 3 | 3 | 8 |
| AA000009 | 000009 | 1 | 1 | 9 |
| AA000010 | 000010 | 1 | 1 | 10 |
+----------+--------+------+------------+-------+
Actual Results of Above Query:
+----------+
| id |
+----------+
| AA000005 |
| AA000008 |
+----------+
Here's the SQL Fiddle.
To simply test if there are missing values,
select count(*) <> max(right(col, 6))-min(right(col, 6))+1 || count(*) <> count(distinct col)
Trying to sort rows from lowest to highest continually, or rather repeatedly using MySql. For example: if a column has the following values: 1,3,2,4,2,1,4,3,5, then it should end up like this 1,2,3,4,5,1,2,3,4. So it goes from lowest to highest, but tries to sort again from lowest to highest multiple times.
For large sets, the semi-JOIN operation (the approach in the answer from Strawberry) may create an unwieldy resultset. (Then again, MySQL may have some optimizations in there.)
Another alternative available in MySQL is to use "user variables", like this:
SELECT r.mycol
FROM ( SELECT IF(q.mycol=#prev,#seq := #seq + 1,#seq := 1) AS seq
, #prev := q.mycol AS mycol
FROM mytable q
JOIN (SELECT #prev := NULL, #seq := NULL) p
ORDER BY q.mycol
) r
ORDER BY r.seq, r.mycol
Let me unpack that a bit, and explain what it's doing, starting with the inner query (inline view aliased as r.) We're telling MySQL to get the column (mycol) containing the values you want to sort, e.g. 1,3,2,4,2,1,4,3,5 and we're telling MySQL to order these in ascending sequence: 1,1,2,2,3,3,4,4,5.
The "trick" now is to use a MySQL user variable, so that we can compare the mycol value from the current row to the mycol value from the previous row, and we use that to assign an ascending sequence value, from 1..n on each distinct value.
With that resultset, we can tell MySQL to order by that assigned sequence value first, and then by the value from mycol.
If there is a unique id on each row, then a correlated subquery can be used to get an equivalent result (although this approach is very unlikely to perform as well on large sets)
SELECT r.mycol
FROM mytable r
ORDER
BY ( SELECT COUNT(1)
FROM mytable q
WHERE q.mycol = r.mycol
AND q.id <= r.id
)
, r.mycol
Here's the setup for the test case:
CREATE TABLE mytable (id INT, mycol INT);
INSERT INTO mytable (id, mycol) VALUES
(1,1),(2,3),(3,2),(4,4),(5,2),(6,1),(7,4),(8,3),(9,5);
there is no order two time just this
ORDER BY column ASC
Let's pretend that the PK is a unique integer. Consider the following...
CREATE TABLE seq(id INT NOT NULL PRIMARY KEY,val INT);
INSERT INTO seq VALUES (8,1),(4,2),(1,3),(2,4),(7,0),(6,1),(3,2),(5,5);
SELECT * FROM seq ORDER BY val;
+----+------+
| id | val |
+----+------+
| 7 | 0 |
| 6 | 1 |
| 8 | 1 |
| 3 | 2 |
| 4 | 2 |
| 1 | 3 |
| 2 | 4 |
| 5 | 5 |
+----+------+
SELECT x.*
, COUNT(*) rank
FROM seq x
JOIN seq y
ON y.val = x.val
AND y.id <= x.id
GROUP
BY id
ORDER
BY rank
, val;
+----+------+------+
| id | val | rank |
+----+------+------+
| 7 | 0 | 1 |
| 6 | 1 | 1 |
| 3 | 2 | 1 |
| 1 | 3 | 1 |
| 2 | 4 | 1 |
| 5 | 5 | 1 |
| 8 | 1 | 2 |
| 4 | 2 | 2 |
+----+------+------+
I got a set of results like this:
| id |
| 1 |
| 2 |
| 3 |
| 4 |
I'm trying to get
| id |
| 4 |
| 1 |
| 2 |
| 3 |
Is there any elegant way to achieve this using an SQL script?
You can use the mod operator, % to ORDER BY
DECLARE #maxId AS INT
SELECT #maxId = MAX(Id) FROM MyTable
SELECT id FROM MyTable
ORDER BY Id % #maxId
You can get further rotations by adding to Id, ie
ORDER BY (Id + 1) % #maxId
get you
3
4
1
2
Working SQL Fiddle (which I just found out exists)
http://sqlfiddle.com/#!3/a7f15/5
ok i'll take a stab at the SQL.
select case when id > 3 then 0
else 1
end
, id
from mytable
order by 1,2
Let's say I have a mySQL table whose structure is like this:
mysql> select * from things_with_stuff;
+----+---------+----------+
| id | counter | quantity |
+----+---------+----------+
| 1 | 101 | 1 |
| 2 | 102 | 2 |
| 3 | 103 | 3 |
+----+---------+----------+
My goal is to "expand" the table so I end up with something like:
mysql> select * from stuff;
+----+---------+
| id | counter |
+----+---------+
| 1 | 101 |
| 2 | 102 |
| 3 | 102 |
| 4 | 103 |
| 5 | 103 |
| 6 | 103 |
+----+---------+
And I want to do this "expansion" using only mysql. Note that I end up with a row per quantity and per counter. Any suggestions? A stored procedure is not an option here (I know they offer while loops).
Thanks!
The following will do the trick as long as some_large_table has a length greater than or equal to the largest quantity in things_with_stuff. For example, let some_large_table be a big fact table in a data warehouse.
SELECT #kn:=#kn+1 AS id, counter
FROM (SELECT #kn:=0) k, things_with_stuff ts
INNER JOIN (
SELECT #rn:=#rn+1 AS num
FROM (SELECT #rn:=0) t, some_large_table
) nums ON num <= ts.quantity;
Assuming there is a maximum value for quantity, you could do:
INSERT INTO things SELECT counter FROM things_with_stuff WHERE quantity > 0;
INSERT INTO things SELECT counter FROM things_with_stuff WHERE quantity > 1;
INSERT INTO things SELECT counter FROM things_with_stuff WHERE quantity > 2;
--... and so on until the max.
It's a bit of a hack but it should do the job.
If the ordering is important you could do a clean up afterwards.
I have sometimes in databases a table named num (number) with a single column i, filled with all integers from 1 to 1000000. It's not hard to make such a table and populate it.
Then you could use this if stuff.id is auto incremented:
INSERT INTO stuff
( counter )
SELECT ts.counter
FROM things_with_stuff AS ts
JOIN num
ON num.i <= ts.quantity