I am new to MYSQL and would like to create a table where a constant Letter depicting the department is added to an auto increment number. This way I would be able to identify the category of the worker upon viewing the ID.
Ex. Dept A and employee 135. The ID I am imaging should read A135 or something similar. I have created the table, the auto increment works fine, the constant letter has been declared and is featuring. However I would like to concatenate them in order to use the A135 as a primary key.
Any Help Please?
This quite tricky, and you would be probably better off doing manual concatenation in a select query.
But since you asked for it...
In normal usage you would have used a computed column for this, but they do not support using autoincremented columns in their declaration. So you would need to use triggers:
on insert, query information_schema.tables to retrieve the autoincremented id that is about to be assigned and use it to generate the custom id
on update, reset the custom id
Consider the following table structure:
create table workers (
id int auto_increment primary key,
name varchar(50) not null,
dept varchar(1) not null,
custom_id varchar(12)
);
Here is the trigger for insert:
delimiter //
create trigger trg_workers_insert before insert ON workers
for each row
begin
if new.custom_id is null then
select auto_increment into #nextid
from information_schema.tables
where table_name = 'workers' and table_schema = database();
set new.custom_id = CONCAT(new.dept, lpad(#nextid, 11, 0));
end if;
end
//
delimiter ;
And the trigger for update:
delimiter //
create trigger trg_workers_update before update ON workers
for each row
begin
if new.dept is not null then
set new.custom_id = CONCAT(new.dept, lpad(old.id, 11, 0));
end if;
end
//
delimiter ;
Let's run a couple of inserts for testing:
insert into workers (dept, name) values ('A', 'John');
insert into workers (dept, name) values ('B', 'Jim');
select * from workers;
| id | name | dept | custom_id |
| --- | ---- | ---- | ------------ |
| 1 | John | A | A00000000001 |
| 2 | Jim | B | B00000000002 |
And let's test the update trigger
update workers set dept = 'C' where name = 'Jim';
select * from workers;
| id | name | dept | custom_id |
| --- | ---- | ---- | ------------ |
| 1 | John | A | A00000000001 |
| 2 | Jim | C | C00000000002 |
Demo on DB Fiddle
Sorry, my answer does not fit in a comment.
I agree with #GMB.
This is a tricky situation and in some cases (selects mainly) will lead in a performance risk due you'll have to split PK in where statements, which is not recommended.
Having a column for department and another for auto_increment is more logical. And the only gap you have is to know the number of employees per department you'll have to make a count grouping by dept. Instead of a max() splitting your concatenated PK, which is is at high performance cost.
Let atomic and logic data remain in separate columns. I would suggest to create a third column with the concatenated value.
If, for some company reason, you need B1 and A1 values for employees of different departments, I'd suggest to have 3 columns
Col1 - letter(not null)
Col2 - ID(Not auto-increment, but calculated as #GMB's solution) (Not NULL)
Col3 - Concatenation of Col1 and Col2 (not null)
PK( Col1, col2)
Related
I want to add a new record in a table if duplicate value enters in a unique field. I don't want to update the existing one but want to add a new record by modifying the unique field value.
Is this possible in mysql?
EDIT:
Edited after user comment on this post:
You need write table locking on both of those two processes.
A WRITE lock has the following features:
The only session that holds the lock of a table can read and write data from the table.
Other sessions cannot read data from and write data to the table until the WRITE lock is released.
Also look at SQL UNIQUE Constraint
BEFORE EDIT:
Yes it is possible. And it took me awhile to figure it out. I build this on your input and compering values as test1, test2 etc, where test is always the same and has trailing number. As you specified.
It can be done as MySQL TRANSACTION in 4 steps.
Lets say you have table testT where name is unique to insure we have no doubles.
| id | name |
| --- | ----- |
| 1 | test1 |
| 2 | test3 |
And you want to insert a new item with name test1 we set is as:
SET #newName = 'test1';
Then we need to check if it already exists in table:
SELECT #check:=COUNT(*) FROM testT WHERE name = #newName;
We do a count here to get true or false and save it as #check here so we can compare it later. This will result into 1 row as test1 already exists in table.
Next we do another selection to get the highest number of test* and store it as #number, this next query selects all tests and does a SUBSTRING after 4 latter's giving us all numbers after first 4 latter's. (99999999999) numbers actually just to be sure we don't miss any but in our case result is only "3" because that is last record "test3" in table.
SELECT
#number:= SUBSTRING(name,5,99999999999)
FROM testT;
Now we can do an insert:
INSERT INTO testT(name)
VALUES
(
IF(#check = "", #newName , CONCAT(LEFT(#newName,4),RIGHT(#number,1)+1)
)
);
This tries to insert our #newName into table under IF condition, and that is if our #check is empty then he will insert #newName, if not it will take word test out of string and append a highest #number from earlier and add + 1 too it.
So result for #newName = 'test1' is below. If you change this into #newName = 'test3' result wold be same new insert test4.
**Schema (MySQL v5.7)**
SET #newName = 'test1';
---
**Query #1**
SELECT * FROM testT
ORDER BY id;
| id | name |
| --- | ----- |
| 1 | test1 |
| 2 | test3 |
| 3 | test4 |
---
And if you change it in ANY test* that number does not already exists it will insert it normally. In case below: #newName = 'test6'
SET #newName = 'test6';
**Query #1**
SELECT * FROM testT
ORDER BY id;
| id | name |
| --- | ----- |
| 1 | test1 |
| 2 | test3 |
| 3 | test6 |
This way an insert will always be made.
You can play with this here : View on DB Fiddle just by changing SET #newName = 'test6'
I am no expert and it took me couple of hours to figure this way out, as I wanted to know if this was even possible.
And I would appreciate if any other user can suggestion any other way or improve my method.
I am trying to insert data from one table into another, and each table has an 'id' field that should be the same, but is stored different datatype. This 'id' field should represent the same unique value, allowing me to update from one to another.
In one table (the new.table one), the 'id' is stored as datatype varchar(35) and in the old.table it is datatype bigint(20) -- I believe this older table represents the integer version of the hex value stored in the new one. I am trying to update data from the new.table back into the old.table
After searching about this for a while
When I try this simple mysql update query it fails:
INSERT INTO old.table (id, field2)
SELECT CAST(CONV(id,16,10) AS UNSIGNED INTEGER), field2
FROM new.table;
It fails with this error:
Out of range value for column 'id' at row 1
I have also tried a simple
SELECT CAST(CONV(id, 16,10) AS UNSIGNED INTEGER) from new.table;
And the result is all the same integer mostly, but each hex value in new.table is unique. I've google this for two days, and could really use to help to figure out what is wrong. Thanks.
EDIT: Some of the example data from console of output of SELECT ID from new.table:
| 1d2353560110956e1b3e8610a35d903a |
| ec526762556c4f92a3ea4584a7cebfe1.11 |
| 34b8c838c18a4c5690514782b7137468.16 |
| 1233fa2813af44ca9f25bb8cac05b5b5.16 |
| 37f396d9c6e04313b153a34ab1e80304.16 |
The problem id is too high values.
MySQL will return limit-value when overflow happened.
Query 1:
select CONV('FFFFFFFFFFFFFFFF1',16,10)
Results:
| CONV('FFFFFFFFFFFFFFFF1',16,10) |
|---------------------------------|
| 18446744073709551615 |
Query 2:
select CONV('FFFFFFFFFFFFFFFF',16,10)
Results:
| CONV('FFFFFFFFFFFFFFFF',16,10) |
|--------------------------------|
| 18446744073709551615 |
I would suggest you, Implement the logic algorithm for id in your case in a function instead of use CONV function.
EDIT
I would use a variable to make new row number and insert to old table.
CREATE TABLE new(
Id varchar(35)
);
insert into new values ('1d2353560110956e1b3e8610a35d903a');
insert into new values ('ec526762556c4f92a3ea4584a7cebfe1.11');
insert into new values ('34b8c838c18a4c5690514782b7137468.16');
insert into new values ('1233fa2813af44ca9f25bb8cac05b5b5.16');
insert into new values ('37f396d9c6e04313b153a34ab1e80304.16');
CREATE TABLE old(
Id bigint(20),
val varchar(35)
);
INSERT INTO old (id, val)
SELECT rn, id
FROM (
SELECT *,(#Rn:=#Rn +1) rn
FROM new CROSS JOIN (SELECT #Rn:=0) v
) t1
Query 1:
SELECT * FROM old
Results:
| Id | val |
|----|-------------------------------------|
| 1 | 1d2353560110956e1b3e8610a35d903a |
| 2 | ec526762556c4f92a3ea4584a7cebfe1.11 |
| 3 | 34b8c838c18a4c5690514782b7137468.16 |
| 4 | 1233fa2813af44ca9f25bb8cac05b5b5.16 |
| 5 | 37f396d9c6e04313b153a34ab1e80304.16 |
I have two table in my MySQL database access & user. I would like to create a trigger that activates every time the user table is accessed and that notes both a time stamp and all columns that are being requested.
So for example if someone requested name from user where ID=20 then the trigger would create a new row in the accessible table that noted the userID, timestamp in unix format, rows column so userID=20, timestamp=1515147950, rowAccessed=name.
How would such a trigger roughly look?
Edit:
User table (InnoDB):
| ID | name | email | age |
+----+--------+-------------+-----+
| 1 | Alice | alice#a.com | 20 |
| 2 | Bo b | bob#b.com | 12 |
| 3 | Carl | carl#c.com | 32 |
Access table (InnoDB):
| ID | userID | timeStamp | column |
+----+--------+-------------+--------+
| 1 | 2 | 1515149281 | name |
| 2 | 1 | 1515148251 | email |
The data in the access table is what I would like the trigger to fill in.
The userID column in the Access table is linked to the ID of the user table through a InnoDB relation
No need to say that the best option for your question is to handle it from code. But if it is necessary to do it from Mysql... this is a approach, probably won't work, I don't have access to a MySQL to test it, but this is where I would start from:
create table user (
ID int primary key,
name text,
email text,
age int
)
create table access (
ID int primary key auto_increment,
userID int,
time timestamp,
columnName text
)
insert into user values (1, 'Alice', 'alice#alice.com', 20), (2, 'Bob', 'bob#bob.com', 25)
create procedure selectUser(colName Boolean, colEmail Boolean, colAge Boolean, id INTEGER)
BEGIN
DECLARE justNow timestamp;
select now() into justNow;
IF colName THEN
insert into access(userID, time, columnName) values (id, justNow, 'name');
END IF;
IF colEmail THEN
insert into access(userID, time, columnName) values (id, justNow, 'email');
END IF;
IF colAge THEN
insert into access(userID, time, columnName) values (id, justNow, 'age');
END IF;
SET #s = CONCAT('SELECT name FROM user');
PREPARE stmt FROM #s;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END
call selectUser(true, true, true, 1)
I haven't finished the part of the column query, but that's easy. Let us know if this approach works for you.
how to set an unique primary key to all the table of database?
for example i don't wanted to repeat any primary key of different table.
table A:
----------
id | name
----------
1 | aaa
3 | bbb
5 | ccc
table B:
-------------
id | surname
-------------
7 | ddd
2 | eee
9 | fff
table C:
-------------
id | nickname
-------------
4 | ggg
6 | hhh
8 | iii
all id are primary key and auto_increment.
All the data is entered dynamically.I am using MYSQL in PHPMYADMIN.
You may add a new table to your schema called ID_Table that will have only one numeric column called current_id with default value of 0 ,when adding a new row to any other table of the schema you have to call a select on the ID_Table returning ID_Table.current_id + 1 as new id value.
Then updating ID_Table must be done
Update ID_Tableset ID_Table.current_id = ID_Table.current_id + 1
the GetNewId function could be implemented by
locking the ID_Table
Updating ID_Table
returning NewID
something like this (I have used Oracle syntax)
create table ID_Table(
current_id number
);
Insert into ID_Table values(0);
CREATE OR REPLACE Function GetNewId RETURN number is
new_id ID_Table.current_id%type;
row_count number;
begin
select nvl(ID_Table.current_id, 0) + 1
INTO new_id
FROM ID_Table
for update;
update ID_Table set ID_Table.Current_Id = new_id;
commit;
RETURN new_id;
end GetNewId;
You can get maximum ID from all three tables then add it in your insert query. But you have to remove the auto_increment attribute.
INSERT INTO TableA
SELECT MAX(ID)+1, 'jjj'
FROM
(SELECT MAX(ID) AS ID FROM TableA
UNION
SELECT MAX(ID) AS ID FROM TableB
UNION
SELECT MAX(ID) AS ID FROM TableC
) A;
See this SQLFiddle
Use the same sequence as the id generator for each inserted row, regardless of the table. Assuming you're using a DB that allows a sequence to be named as the id generator for the field.
This looks like it will do what you want in MySQL: http://devzone.zend.com/1786/mysql-sequence-generator/
Look at using Sequence. I'm not sure what DB you are using. Postgresql and Oracle have sequence that you can share between tables.
I am using MySQL 5.5. I need to add a Trigger to my table using mysql trigger syntax: http://dev.mysql.com/doc/refman/5.0/en/trigger-syntax.html
The example they have given doesn't explain how I can go about doing this -
I have a table - table(a INT, b INT, c INT);. field a and b are numbers, while field c should be a + b. Now i'm sure you are wondering why not just slap this in a view and be done with it, or why not put this in my code. The reason is because I am working with a client that needs the convenience of an auto calc'ed field, with the ability to modify the value incase it needs variation. They are an auditing company and massaging the numbers is often required because of companies missing audit dates etc.
So how can I create a trigger that will:
on insert:
make `c` the value of `a` + `b`.
on update:
if the value of NEW.`c`==OLD.`c` THEN
make `c` the value of `a` + `b`.
ELSE
no change
The reason for the update not changing if the new value is different to the old value is because that would mean they want to modify the number to be slightly different to what the actual sum is.
Please feel free to change my logic - my aim is to preserve the value of c if it has been entered manually and to blast it if it hasn't been touched manually.
Thanks!
I know this is an old question, but if the answer is still needed here it is.
First of all an id column has been added to the table for example's sake to have more direct updates.
CREATE TABLE table1
(
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
a INT, b INT, c INT
);
Now in INSERT trigger the logic is changed to allow an insert of a pre-calculated value to C column.
CREATE TRIGGER tg_table1_before_insert
BEFORE INSERT ON table1
FOR EACH ROW
SET NEW.c = IF(NEW.c IS NULL, NEW.a + NEW.b, NEW.c);
An update trigger implements the logic per your requirements
CREATE TRIGGER tg_table1_before_update
BEFORE UPDATE ON table1
FOR EACH ROW
SET NEW.c = IF(NEW.c <=> OLD.c, NEW.a + NEW.b, NEW.c);
Now lets do some inserts and updates
INSERT INTO table1 (a, b) VALUES (1, 2), (3, 4);
INSERT INTO table1 (a, b, c) VALUES (5, 6, 0), (7, 8, 100);
UPDATE table1 SET c = 25 WHERE id = 2;
UPDATE table1 SET c = c WHERE id = 3;
As a result we have
| ID | A | B | C |
--------------------
| 1 | 1 | 2 | 3 | -- calculated on insert
| 2 | 3 | 4 | 25 | -- explicitly set on update
| 3 | 5 | 6 | 11 | -- re-calculated on update
| 4 | 7 | 8 | 100 | -- explicitly set on insert
Here is SQLFiddle demo