I am attempting to perform an insert operation inside a select. This is for chaining multiple inserts together that are dependent on each other. As such it looks something like this.
INSERT (Parent)
--SELECT (Return the Child's uuid created and assigned by TRIGGER)
---INSERT (Child)
Every record in the database has a uuid assigned upon insert through a trigger. I have a table for each type of entity I am capturing (e.g. person,address,phone,email).
Solution 1: I can insert a dependent entity such as the phone first, select it's uuid, then include that in the parent entity. In this case "person".
Solution 2: I can create the parent entity, then the dependent, then update the parent after acquiring the dependents uuid.
Solution 3: What I want to do is create the dependents at the same time I am creating the parent, and return their uuid to the parent for the parent record insert. This relies on order of operations to ensure everything is happening in appropriate order.
Because of the issues with mysql array support, I'm fudging a parent/child uuid_array table. As such there are additional operations involved, but the given example is a lot simpler.
Any idea on how to pull this off? Is there a better way to return the uuid?
INSERT INTO person (Name_First,Name_Middle,Name_Last,Phone_UUID_Array)
VALUES (
'John',
'Diddly',
'Doe',
SELECT #last_uuid
INSERT INTO phone (Phone_Number,Phone_Type)
VALUES (
'1-555-555-5555',
(SELECT UUID FROM VIEW_TYPE_CATEGORY WHERE Reference_Type='Personal cell' AND Category='phone')
)
);
Examples of triggers I've created for every table
CREATE TRIGGER uuid_person BEFORE INSERT ON person FOR EACH ROW SET NEW.UUID = UUID();
CREATE TRIGGER last_uuid_person AFTER INSERT ON person FOR EACH ROW SET #last_uuid = NEW.UUID;
CREATE TRIGGER uuid_phone BEFORE INSERT ON phone FOR EACH ROW SET NEW.UUID = UUID();
CREATE TRIGGER last_uuid_phone AFTER INSERT ON phone FOR EACH ROW SET #last_uuid = NEW.UUID;
No, you cannot execute an INSERT "inside" a SELECT, nor can you execute an INSERT inside another INSERT.
You can play around with triggers, but IMHO that's more trouble than it's worth.
I would recommend not doing this with a trigger, but instead generate the uuid and save it in a session variable. Then you can use it in as many subsequent statements as you want.
SELECT UUID() INTO #phone_uuid;
INSERT INTO phone (Uuid,Phone_Number,Phone_Type) VALUES (#phone_uuid, ...);
INSERT INTO person (Name_First,Name_Middle,Name_Last,Phone_UUID_Array)
VALUES ('John', 'Diddly', 'Doe', #phone_uuid);
These session variables are scoped to the session, so you don't have to worry about concurrent clients doing their own work overwriting yours, even if each session is using the same variable names.
Also, I fear from your description that Phone_UUID_Array is a comma-separated list of UUID's. You are probably not going to be happy with that design, for a number of other reasons.
You've got the reference in the wrong direction. Person shouldn't try to have a reference to all its dependent phone numbers. Go the other way -- each row in phone should have a reference to its parent person. That way you don't need to have a comma-separated list, and you can insert them in a more sensible order -- parent first, then one or more phone rows that each references the same parent. You don't need to update parent after inserting the phone.
Related
Is it possible to check if a certain field was included in an INSERT statement in the context of a trigger that this INSERT statement fired?
For instance, say there's a customers table with the following fields: customerid INT NOT NULL and isCrazy BIT(1) DEFAULT b'1'. This table has a trigger before insert and it is necessary to check if the INSERT statement includes the isCrazy field.
INSERT INTO customers (customerid) VALUES (1);
OR
INSERT INTO customers (customerid, isCrazy) VALUES (1, b'0');
If the field is defined, then it should proceed to be saved, otherwise there are certain actions to be taken to determine the right value that should be inserted into this field; which would not necessarily be equal to the default.
The problem I'm having is that, if the isCrazy field was not included in the INSERT statment, then NEW.isCrazy holds the default value (b'1'); same as if the inserted value was actually defined as such. Is it possible to tell the two cases apart?
I don't think there's any explicit way to tell if the value was supplied explicitly or came from a default.
The only option I can think of is to use a default value that should never be provided explicitly by the application code. Then you can test if NEW.option is equal to this; if it is, they let it default, and the trigger can replace it with what you really want the default to be.
If two individual records are added to a database such as two patients being added to the table 'Paitents' and when they are added the Primary_Key such as Paitient_ID is created automatically and given to each new account.
.(Auto Incremented)
That bit is quite straight forward and understand I can just use an 'INSERT INTO SONGS' statement.
But what if the two patients are related and I have another table called "Relations"
Where by I need it to pull in the two Paitent_ID's and create a relation from the same insert query. Can this be done?
ID generated by MySQL automatically in auto_increment column can be obtained using the LAST_INSERT_ID() function. Languages/libraries often offer a function for this (e.g. in PHP PDO you call PDO::lastInsertId() instead of making another query).
How to solve this exactly depends on how you are inserting the values into a database, but the basics can be:
Insert patient one
Get the ID
Insert patient two
Get the ID
Create the relation
So I'm designing a function that inserts a row into the MySQL database. The table has a Primary key with Auto-Increment enabled. So I don't insert the value of this column. But the PK is the only unique column of the entire table. How can I fetch the row I just inserted?
I don't see a problem if the function is in light traffic, but when its load is heavier and heavier, I can see a potential bug: say I inserted a row and the DB's AI value is 1, then before the fetch function starts to request the "latest inserted row", another row is inserted with the AI value 2. Now if the fetch function of Insert 1 runs, Row 2 will be fetched. I know the time gap will need to be so small to allow this bug to actually exist, but is there a better way to fetch the right row, while maintain the table only having the PK as the unique column? (I don't want to implement an additional checksum column, though I see it's a potential solution.)
its not very logical but you could:
insert into `table1` (`column1`,`column2`,`column3`) VALUES ("value1","value2","value3");
select * from `table1` where `PK`=LAST_INSERT_ID();
instead you should only SELECT LAST_INSERT_ID(); as jurgen d suggested and reuse the other data
Please read this php function mysqli_insert_id()
Sorry about the above, I foolishly assumed you were using php. MySQL also has a native LAST_INSERT_ID() function.
The ID that was generated is maintained in the server on a per-connection basis. This means that the value returned by the function to a given client is the first AUTO_INCREMENTvalue generated for most recent statement affecting an AUTO_INCREMENT column by that client. This value cannot be affected by other clients, even if they generate AUTO_INCREMENT values of their own. This behavior ensures that each client can retrieve its own ID without concern for the activity of other clients, and without the need for locks or transactions.
Reference; http://dev.mysql.com/doc/refman/5.5/en/information-functions.html#function_last-insert-id
I read this question, which highlights a solution to conditionally insert values into a table if they don't already exist. My question: is it possible to conditionally insert multiple values at once.
For instance, say I have a table that just contains user names (this is a pointless table, but let's keep it simple). The table's contents look like this:
matthew 20
mark 24
luke 25
john 56
buddy 68
A user enters jimmy 34, mark 25 and bobby 54 in a web form and submits, and I'd like to check whether those three values exist in the table already and insert the ones that don't in one statement. Yes, for this example, I'm assuming names are unique.
Here is a paraphrase of the code snippet from the question I linked to, adapted to this example:
INSERT INTO users(name)
SELECT 'jimmy'
FROM dual
WHERE NOT EXISTS (SELECT * FROM users
WHERE user = 'jimmy')
How can I adapt this for multiple values being inserted at once? It's also important that the solution work independently of the number of values entered. In my example, I give three (jimmy, mark and bobby) but there may only be one or there may be 20.
Second question: is this wise? I know that reducing the number of queries is desirable but is it worth it here? Should I just set up a for loop and loop through, alternately checking if a value exists and inserting if it doesn't?
Thanks for any help.
Sorry I don't have code that I've tried myself to show, I'm not even sure what to try here.
Update: added an extra column to the table. I wanted to keep things simple but need two columns to illustrate the fact that deleting a row and then inserting or updating are not what I want as they would favor the user's input over what is already in the table.
See INSERT IGNORE combined with a UNIQUE KEY on the field in question.
When a unique key constraint fails the whole row is silently ignored.
ALTER TABLE users ADD UNIQUE KEY `name` (`name`);
INSERT IGNORE INTO `users` (`name`, `age`)
VALUES ("jimmy", 22), ("bob", 45),
("luke", 300), ("john", 456);
Note: this will also suppress errors when datatypes mismatch and accurate conversion is impossible. (e.g. DECIMAL vs INT) MySQL will continue using the nearest result possible. (e.g. INT) You should ensure only pre-validated data is inserted with such statement.
I am not big on programming(especially on php), but can you parse the string into separate names and then do the loop?
something like (hopefuly the syntax is somewhat correct)
$names = split(" ", $user_names);
for ($i=0; $i<count($names); i++)
{
//sql query check - add new name function
function_sql_query($names[$i]);
}
In addition, you can add rules to your SQLDBMS to disallow dublicates in this attribute and add only unique records; this process will be maintained by DBMS
You can use MySQL's REPLACE
http://dev.mysql.com/doc/refman/5.0/en/replace.html
OR INSERT ... ON DUPLICATE KEY UPDATE
INSERT INTO table (a,b,c) VALUES (1,2,3)
ON DUPLICATE KEY UPDATE c=c;
I have a table that uses Adjacency list model for hierarchy storage. My most relevant columns in this table are therefore:
ItemId // is auto_increment
ParentId
Level
ParentTrail // in the form of "parentId/../parentId/itemId"
then I created a before insert tigger, that populates columns Level and ParentTrail. Since the last column also includes current item's ID I had to use a trick in my trigger because auto_increment columns are not available in the before insert trigger. So I get that value from the information_schema.tables table.
All works fine, until I try to write an update trigger, that would update my item and its descendants when the item changes its parent (ParentId has changed). But I can't make an update on my table inside the update trigger. All I can do is to change current record's values but not other's.
I could use a separate table for hierarchy data, but that would mean that I would also have to create a view that would combine these two tables (1:1 relation) and I would like to avoid this is at all possible.
Is there a way to have all these in the same table so that these fields (Level and ParetTrail) set/update themselves automagically using triggers?
This sounds like it might be possible, but it would involve some heavy string matching etc. Wouldn't want to dig myself into it.
If you are willing to change your database model slightly and implement a Nested Set (scroll down on the page) this could probably be done easily by having the trigger update left-right values.