I'm adding stuff to a MySQL table, and each item has a position that it shows up in my system like a question number.
I could find out what the largest position is before adding a new question, +1 it, and then add it - but I was wondering if there's a more intelligent way that doesn't require a second query.
Something like INSERT INTO questions (id, position) values (0, MAX(position)).
This field is not the primary key, auto_increment is of no use to this situation.
I cannot use position as the key, because the key relates to many other things, and the position can be changed at any time.
I am a pretty confident MySQL query writer, so please don't offer any suggestions other than the question asked - I know of plenty of alternatives, this is just a syntax question.
I'm sure you get my drift!
Cheers.
Use auto_increment like most other folk do?
This is the intelligent way because it concurrency safe.
Best way would be to change the field properties within MySQL, meaning let MySql do the work without providing input.
TABLE TableName CHANGE position position INT( 11 ) NOT NULL AUTO_INCREMENT
Now when inserting, do not provide any values for this field.
INSERT INTO questions (var1, var2) values (val1,val2)
MySQL will automatically increment the position value. You can set current counter as well as reset if need be. This way you are not running any quarries at all.
something like this should work
INSERT INTO questions (id, position) SELECT 0, MAX(position)+1 FROM questions;
Obviously, you will need to handle the 0 value, as in set it for something appropriate, i just copied that value off your example.
You could also create a stored procedure to use in the query, or even have a trigger which determines the MAX(position), adds one and stores it in your newly created row.
Quite alot of options there, I would go for the above solution though (with a index on position)
There's no way to set a default value to a specific calculation, it seems.
Related
I'm studying MySQL so I'm a bit new to all this stuff, but the last time I asked you guys, you proved to be really helpful, so perhaps you can help me again because I haven't been able to find the answer on my own.
Ok, so the thing is, I need to add a new column to an already existing table, and it should be like:
ALTER TABLE `medicos`
ADD COLUMN `tipo_medico` VARCHAR(20) NOT NULL DEFAULT 'indiferente' AFTER `sueldo`;
Now I'm supposed to run an INSERT instruction to add the data there. After adding that column, the table looks like this:
These values equal to: Medic_number, Surnames, Speciality, Day of birth, University, Wage and Medic_type (the new column I've added). The data I add on all the columns except the last one doesn't really matter, but the last column must be filled following the next conditions:
-By default, it'll be "indiferente" (that's working).
-If a medic has studied in an university ending in -a (i.e. Valencia), it'll say "Excellent".
-If a medic is 40 years old or older, it'll say "Expert".
Now, I know how to do these conditions ("... university like = '%a'", etc), but I don't know how to add that into the INSERT instruction. This is what I've gotten so far:
insert into medicos values ('A021', 'Apellidos :D', 'Loquesealogia', '1970-03-14', 'Valencia', '3500');
That adds everything but the last column, which is the one where I'm lost. What can I do to tell the INSERT instruction to, depending on which of the previously named conditions are true, add one value or another?
I hope I've been clear enough, I'm not a native English speaker so I'm sorry if that wasn't good enough. Also I've tried to format the codes this time, I hope that'll work.
Thanks.
This seems to be a case when we need to apply a logic on column values before a record is inserted. I would suggest creating a BEFORE INSERT trigger on this table, which will be automatically executed by MySql before each record is inserted.
We can write the logic to determine the value of last column depending upon values supplied for the other columns. Have a look at this link for trigger example.
If the requirement here is to do a one time bulk insert then, we can drop this trigger once insertion is complete.
I would advise you to do it either with BEFORE INSERT trigger as Darshan Mehta recommends, or do your logic in the programming side, or with a stored procedure.
Still it is doable at query time, so to answer your question specifically, try something like this:
INSERT INTO medicos (num_colegiado, apellidos, especialidad, fecha_nac, universidad, sueldo, tipo_medico) SELECT 'A021', 'Apellidos :D', 'Loquesealogia', '1970-03-14', 'Valencia', '3500', IF('Loquesealogia' like '%a','Excellent',IF(DATE_ADD('1970-03-14', interval 40 YEAR)>NOW(),'Expert','indiferente'))
What is the best way to prevent duplicate values in databases ?
I have a table called names that has only one column called name that is unique (declared as unique attribute).
What is the best way to insert a new name (x) ?
Way1: Should I make a select query for the name x first to check if exist or not. Then make another query to insert the name iff it is not exists in the table.
Way2: Make only one query to insert the name and ignore the error if name already exists.
The second way is the better way. Why run two queries when you can just run one?
When you declare the column as unique, you have told the database to do the extra work for ensure that this is true. You don't need to do anything else -- other than check the errors on the return.
Database constraint will definitely take care about uniqueness, but if you have logic where you need to use last inserted ID to other child table, then only I think you will require to perform manual check before insert, else just ignore exception if raise due to duplication.
The first way works. After the action you can be sure that the record exists (unless some other error occured) You do need a second query (or some another mechanism) to retrieve the actual tuple, either the existing one or a fresly inserted one.
The second way is terrible: the DBMS session is in error-state, {your current work has implicitely been rolled back, and your all cursors have been closed} So, you'll have to start your work allover again, hopefully without the duplicate.
The case you give is a simplified "upsert". Do a search for upsert and you will find answers to the more general question. Some databases, like mysql provide for
insert ignore for this simple case.
Otherwise for the simple case you mention you can use the second approach. For the more general upsert, it is surprisingly difficult to get it right. The issue is concurrent updates. In fact, I have not seen a satisfactory answer for general upserts. Some say to use "merge" but that is subject to concurrency issues.
I have had a good look around and I, so far, haven't been able to find an answer to my question.
Bit of background;
I am new to SQL but very interested in learning. Due to moving country soon, instead of finding a job at an entry level, I am studying towards my certifications, but there are some things that books do not cover.
Now, when creating tables I understand the concept of creating a unique identifier, and I understand that there are many ways to do it, either by using;
** 'uniqueidentifier default newsequentialid()'
or
'int primary key identity' **
when creating the table.
I understand the use for each one, but my question is,
How would someone create an ID for SQL that is sequential but doesn't start at 1?
is it possible to start with 8 digits(00000001), like an employee ID for example, or are there limitations in place preventing SQL from starting anywhere other than (1)
Thanks for reading Ladies and Gents.
You guys don't mess around! thanks for the help!
You can create custom sequences using the CREATE SEQUENCE statement.
In T-SQL you can set an identity seed using the following syntax
YourID INT PRIMARY KEY IDENTITY(1000, 1)
This will start at 1000 and go up by one with each increment.
See http://msdn.microsoft.com/en-gb/library/ms186775.aspx
If you want to pad the number with zeros, I don't think you can do this with an integer type.
To start with an AUTO_INCREMENT value other than 1, you can set that value with CREATE TABLE or ALTER TABLE, like this:
mysql> ALTER TABLE tbl AUTO_INCREMENT = 100;
using dbcc checkident, you can set your identity column to any integer value any time.
There is a table with an int field - field_1.
I want to insert a new row.
The field_1 value will be Maximum value from all the entries plus one.
I've tried:
INSERT INTO table (field names, `field_1`)
VALUES (values, '(SELECT MAX(field_1) FROM table)');
I get '0' in the field_1.
I know I can do it in separate queries.
Is there a way to perform this action with one query? I mean one call from php.
I have an auto-increment field 'id' and I want to add 'position' field. I want to be able to make changes in position but the new item will always have highest position
Whatever it is that you are trying to do, it will not work, because it is not guaranteed to be atomic. So two instances of this query executing in parallel are guaranteed to mess each other up at some random point in time, resulting in skipped numbers and duplicate numbers.
The reason why databases offer auto-increment is precisely so as to solve this problem, by guaranteeing atomicity in the generation of these incremented values.
(Finally, 'Auto Increment Manually' is an oxymoron. It is either going to be 'Auto Increment', or it is going to be 'Manual Increment'. Just being a smart ass here.)
EDIT (after OP's edit)
One inefficient way to solve your problem would be to leave the Position field zero or NULL, and then execute UPDATE table SET Position = Id WHERE Position IS NULL. (Assuming Id is the autonumber field in your table.)
An efficient but cumbersome way would be to leave the Position field NULL when you have not modified it, and give it a value only when you decide to modify it. Then, every time you want to read the Position field, use a CASE statement: if the Position field is NULL, then use the value of Id; otherwise, use the value of Position.
EDIT2 (after considering OP's explanation in the comments)
If you only have 30 rows I do not see why you are even trying to keep the order right on the database. Just load all rows in an array, programmatically assign incrementing values to any Position fields that are found to be NULL, and when the order of the rows in your array changes, just fix the Position values and update all 30 rows in the database.
Try this:
INSERT INTO table (some_random_field, field_to_increment)
SELECT 'some_random_value', IF(MAX(field_to_increment) IS NULL, 1, MAX(field_to_increment) + 1)
FROM table;
Or this:
INSERT `table`
SET
some_random_field = 'some_random_value',
field_to_increment = (SELECT IF(MAX(field_to_increment) IS NULL, 1, MAX(field_to_increment) + 1) FROM table t);
P.S. I know it's 4 years late but I was looking for the same answer. :)
ALTER TABLE table_name AUTO_INCREMENT = 1 allows the database to reset the AUTO_INCREMENT to:
MAX(auto_increment_column)+1
It does not reset it to 1.
This prevents any duplication of AUTO_INCREMENT values. Also, since
AUTO_INCREMENT values are either primary/unique, duplication would
never happen anyway. The method to do this is available for a reason.
It will not alter any database records; simply the internal counter so
that it points to the max value available. As stated earlier by
someone, don't try to outsmart the database... just let it handle it.
It handles the resetting of AUTO_INCREMENT very well. See gotphp
I have a content table in my MySQL database (a Drupal content table, for what it's worth), which has an auto incremented primary key, nid. I want to be able to implement an odd and even id solution, where content created on production has even ids and content created on dev has odd ids, to avoid id conflicts when I sync. Unfortunately, MySQL doesn't support sequences, or per-table auto increment increment values (i.e. increment by 2 only for db.node, rather than 1).
The best solution I can think of, is to have a BEFORE INSERT and AFTER INSERT trigger which sets the session value of auto_increment_increment to 2 in the BEFORE INSERT trigger, and then resets it to 1 in the AFTER INSERT trigger. Since it only sets the session variable, it shouldn't have any effect on other processes, and since it's a Drupal CMS table and nothing complicated is happening, it seems safe, even though it feels wrong.
However, I'm an intermediate MySQL Admin (at best :) ) and as I said it certainly feels hackish, so I thought I'd put this out there and see if anyone has any strong negative reactions to this, perhaps some issue I'm not foreseeing. ( And I suppose if no one does then maybe someone else will find this useful).
Here's a simple example of what you want to do - assuming there is an integer column 'seq'
in the 'my_table_name' table:
DROP trigger my_trigger_name;
CREATE TRIGGER my_trigger_name
BEFORE INSERT ON my_table_name
FOR EACH ROW
SET NEW.seq = (select ifnull(max(seq)+1,1) from source_table_name);