mysql On Duplicate value in field, insert new row with new value - mysql

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.

Related

How to implement Update Mechanism in MySql?

My Use Case :
I am trying to create a GUI and implement it with MYSQL Database. The problem I am facing is the scenario when I have to update a certain entry in the Database.
I know that we can update an Entry in MYSQL database using :
ALTER TABLE <TABLENAME> SET <PARAMETERS=NEW VALUES> WHERE <CONDITION> ;
For eg : If I want to change the name of the guy who id is 2 , I have to write :
ALTER TABLE StudentInfo SET Name='ABC' WHERE id=2 ;
But the problem is , in a GUI based environment , a user can choose to update any particular value wihtout having a constant condition like id in the previous example.
In the UI , the user can opt to select anything from the parameters and modify it and then click the update button.
Now How will I figure out what <CONDITION> to put in the MYSQL query when I need to update the database ?
Any help would be greatly appreciated !
you update a by using the UPDATE command not ALTER, which will change table. Your gui already knows ho tow identify the row in your case for example by the column name
UPDATE StudentInfo SET Name='ABC' WHERE Name='QUERTY';
SEE example
CREATE TABLE StudentInfo(
Name VARCHAR(20),
class int,
section VARCHAR(2),
roll_no int
);
INSERT INTO StudentInfo VALUES ('abc',12,'A',18), ('xyz',12,'A',17),('QUERTY',12,'A',16)
UPDATE StudentInfo SET Name='ABC',class = 15,section = 'B',roll_no= 99 WHERE Name='QUERTY';
SELECT * FROM StudentInfo
Name | class | section | roll_no
:--- | ----: | :------ | ------:
abc | 12 | A | 18
xyz | 12 | A | 17
ABC | 15 | B | 99
db<>fiddle here
The main problem is to identify the correct row, so you should have a field that is unique.
Like an id auto_increment, that is invisible for the user, but you can identify every row and use this id to update the row.
UPDATE StudentInfo SET Name='ABC' WHERE id = 3;
So that if you have two rows with John Smith you still could update the right one

Creating Primary key from 2 autonumber and constant letter when creating table

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)

How to update based on json keys?

I have a table of an app setting that looks like this:
Code | Value |
---------------------
MAC_ADDR | 'SAMPLE'|
PC_OPT | 0 |
SHOW_ADDR | 1 |
Then I'm receiving a json in my trigger function like this:
{MAC_ADDR: 'NEWADDR', PC_OPT: 1, SHOW_ADDR: 0}
How do I perform an update based on all the keys from my json?
you can just use json_populate_record, eg:
t=# create table tj("MAC_ADDR" text, "PC_OPT" int, "SHOW_ADDR" int);
CREATE TABLE
t=# insert into tj select 'SAMPLE',0,1;
INSERT 0 1
t=# select * from tj;
MAC_ADDR | PC_OPT | SHOW_ADDR
----------+--------+-----------
SAMPLE | 0 | 1
(1 row)
t=# update tj set "MAC_ADDR"=j."MAC_ADDR", "PC_OPT"=j."PC_OPT", "SHOW_ADDR"=j."SHOW_ADDR"
from json_populate_record(null::tj,'{"MAC_ADDR": "NEWADDR", "PC_OPT": 1, "SHOW_ADDR": 0}') j
where true;
UPDATE 1
t=# select * from tj;
MAC_ADDR | PC_OPT | SHOW_ADDR
----------+--------+-----------
NEWADDR | 1 | 0
(1 row)
keep in mind - you did not specify PK or other column to update rows so all rows will be updated in example above. Which suits your data sample, but would not in case of more data
Update
I misunderstood the question, in (code, value) table it's even easier, eg:
update some_tbl
set "Value" = '{"MAC_ADDR": "NEWADDR", "PC_OPT": 1, "SHOW_ADDR": 0}'::json->'MAC_ADDR'
where "Code"='MAC_ADDR'
o again, using the code above you can map update with json keys...

SELECT(MAX) not going past 10 - mysql with phpmyadmin

I'm creating a PHP script to insert rows into a database called orders based on a shopping cart that is stored in an associative array using a sessional array $_SESSION['cart']. The database looks something like this:
orders
----------+--------------+-------------+-------------+-------------+
Id | Username | Item1Id | Item2Id | Item3Id |
----------+--------------+-------------+-------------+-------------+
1 | a#aa.com | 8000001 | 8000002 | 800003 |
----------+--------------+-------------+-------------+-------------+
5 | a#aa.com | 7000001 | 6000002 | 700003 |
----------+--------------+-------------+-------------+-------------+
7 | b#bb.com | 8000001 | 8000002 | NULL |
----------+--------------+-------------+-------------+-------------+
10 | a#aa.com | 3000001 | 1000002 | 800009 |
----------+--------------+-------------+-------------+-------------+
Id column type is CHAR(20) as I may choose to use letters later on.
As part of inserting the row, I need to assign an Id (Primary Key) to the order row which will be set to 1 higher than the current highest Id number found.
The whole script works perfectly; query finds highest Id in the table and I increment that by 1 and assign it to a variable to use as part of the insert query. The only problem is that "SELECT MAX(Id) FROM orders" can't seem to find anything higher than 9. Is there a condition which prevents the SELECT MAX(Id) from identifying anything in double digits?
I've got it written like:
$highestID = mysqli_query($conn, "SELECT MAX(Id) FROM orders");
$orderID = $highestID +1;
I've emptied the database except for Id numbers1 and 2. Running the PHP script inserts new rows with Id numbers 3, 4, 5 except when it gets to 10, the script is unable to as it produces an error of having duplicate primary key of '10' (from $orderID's value). Even when manually entering a row into the database with Id of '25', $orderID still only returns '10' when I echo out its result.
I have not set any specific limits to the amount of rows that can be entered or anything like that.
Id is char(20) so order by Id using string sort. You could use cast or convert function to sort numbers.
Like:
select max(cast(Id as unsigned)) from orders
You really do not need to go through ALL that trouble for an auto-incremental PK. Here's how you can go about it.
Step 1 : In your phpmyadmin, edit your table, and check the A_I checkbox for your PK column.
Step 2 : While inserting from PHP, leave the field blank. It will automatically assign a value of the current max + 1 to your PK.
Eg,
$query = "Insert into mytable (id, name) values ('', 'Name1'), ('', 'Name2')";
Edit : You really cannot have a CHAR(20) PK and then expect the increment to work btw.

Reorder rows in a MySQL table

I have a table:
+--------+-------------------+-----------+
| ID | Name | Order |
+--------+-------------------+-----------+
| 1 | John | 1 |
| 2 | Mike | 3 |
| 3 | Daniel | 4 |
| 4 | Lisa | 2 |
| 5 | Joe | 5 |
+--------+-------------------+-----------+
The order can be changed by admin hence the order column. On the admin side I have a form with a select box Insert After: to entries to the database. What query should I use to order+1 after the inserted column.
I want to do this in a such way that keeps server load to a minimum because this table has 1200 rows at present. Is this the correct way to save an order of the table or is there a better way?
Any help appreciated
EDIT:
Here's what I want to do, thanks to itsmatt:
want to reorder row number 1 to be after row 1100, you plan to leave 2-1100 the same and then modify 1 to be 1101 and increment 1101-1200
You need to do this in two steps:
UPDATE MyTable
SET `Order` = `Order` + 1
WHERE `Order` > (SELECT `Order`
FROM MyTable
WHERE ID = <insert-after-id>);
...which will shift the order number of every row further down the list than the person you're inserting after.
Then:
INSERT INTO MyTable (Name, `Order`)
VALUES (Name, (SELECT `Order` + 1 FROM MyTable WHERE ID = <insert-after-id>));
To insert the new row (assuming ID is auto increment), with an order number of one more than the person you're inserting after.
Just add the new row in any normal way and let a later SELECT use ORDER BY to sort. 1200 rows is infinitesimally small by MySQL standards. You really don't have to (and don't want to) keep the physical table sorted. Instead, use keys and indexes to access the table in a way that will give you what you want.
you can
insert into tablename (name, `order`)
values( 'name', select `order`+1 from tablename where name='name')
you can also you id=id_val in your inner select.
Hopefully this is what you're after, the question isn't altogether clear.