How to enforce insert with specific field? - mysql

Given the following table:
DROP TABLE IF EXISTS my_table;
CREATE TABLE IF NOT EXISTS my_table(
id INT NOT NULL,
timestamp TIMESTAMP(3) DEFAULT CURRENT_TIMESTAMP(3) NOT NULL,
data BLOB NULL,
PRIMARY KEY (id)
);
I can insert on it with:
INSERT INTO my_table (timestamp, data) VALUES
('2014-07-11 11:25:48.185', LOAD_FILE('sql/file.bin'));
In the above insert I was not enforced to insert the id field.
How may I create the table (my_table) so that it prevents inserts without id?
I would every insert to be made (providing the id) like, i.e.:
INSERT INTO my_table (id, timestamp, data) VALUES
(7, '2014-07-11 11:25:48.185', LOAD_FILE('sql/file.bin'));
I was thinking NOT NULL was there for it.

To prevent inserts with an empty value for ID (or not value passed), simply define the column as NOT NULL as you defined it.
I can't see how your example worked (i.e. inserting only into (timestamp, data)).
Now, the fact that there is another table with a trigger that inserts in this one does not have any effect on the ID column of this table. If you define it as AUTO_INCREMENT, whenever you insert a new row, the ID will automatically get a new value which will be fully independent from any data of the first table.
You can have as many tables as you wish with auto-incremented fields, each running a different sequence (and hence their numbering will be fully independent).
To summarize:
CREATE TABLE IF NOT EXISTS my_table(
id INT NOT NULL AUTO_INCREMENT ,
timestamp TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ,
data BLOB NULL ,
PRIMARY KEY (id)
);

Related

Trigger INSERT SQL multiple data

I'm trying to create a trigger with sql so that When I insert a row in Point I insert before it a row in PointAbs.
CREATE TABLE PointAbs (
ID INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
X INTEGER NOT NULL,
Y INTEGER NOT NULL
);
CREATE TABLE Point(
ID INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
Name VARCHAR(50) ,
IDPointAbs INTEGER NOT NULL,
FOREIGN KEY (IDPointAbs) REFERENCES PointAbs(ID) ON DELETE CASCADE
);
the problem is that I need to provide "X" and "Y" for PointAbs and "Name" for Point at the same time. Ho can I achieve that?
I could use a JDBC functionality to get the last insertedID but I don't like it that way.
It seems like the relation is 1 to 1, as you have to create a new PointAbs for each Point. Unless you have another table that relates to PintAbs, there will be one PointAbs for each Point. If you don't need two separated objects, you can state X and Y as an index of Point:
CREATE TABLE Point(
ID INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
Name VARCHAR(50) ,
IDPointAbs INTEGER NOT NULL,
X INTEGER NOT NULL,
Y INTEGER NOT NULL,
INDEX INDEX_X_Y ON Point(X,Y)
);
Of course, this might not be possible nor desirable in your design.
As you can't send parameters to the trigger like the X and Y values, your best option is to use a single transaction for both inserts.
BEGIN;
INSERT INTO PointAbs(X,Y) VALUES (10,15);
INSERT INTO Point(Name, IDPointAbs) VALUES ('Fancy Name', LAST_INSERT_ID(PointAbs));
COMMIT;
You can control the insertions using the programming language of the system back-end, but as there is no specific language mentioned other than mysql, I won't enter into details.

MySQL: Get last inserted primary key without auto_increment

I removed my record ID while I'm using unique hashes as a primpary key. This primary key obviously cannot auto increment.
Now my question is how to retreive the last inserted primary key? MySQL returns 0 on LAST_INSERT_ID() while it's not an auto increment column.
The fact
There's no equivalent to LAST_INSERT_ID() returning a non integer value.
One can simply
The easy approach
Add an integer column which can either be auto incremented or non auto incremented.
To have it auto incremented correctly one has at least to implement an algorithm in MySQL itself or with a language of their choice to fill the existing records with the new IDs.
The more complex approach
https://stackoverflow.com/a/53481729/2323764 (#kellymandem)
Add a second table managing the ID and triggered by the origin table without IDs.
One cannot
I found this very promising Q/A.
Is there a way to get last inserted id of a NON - auto incremented column in MySQL?
It's mentioned there to use LAST_INSERT_ID() in the INSERT statement already.
But
INSERT INTO `table` ( `non_integer_column` ) VALUES ( LAST_INSERT_ID( 42 ) );
SELECT LAST_INSERT_ID( );
-> 42
INSERT INTO `table` ( `non_integer_column` ) VALUES ( LAST_INSERT_ID( 'a0b1c2d3e4f5' ) );
SELECT LAST_INSERT_ID( );
-> 0
Non integer values will be ignored.
I think your problem could best be solved by creating a new table and a trigger to keep track of the newly inserted hash values in the main table.
For example
CREATE TABLE test_table (
hash VARCHAR(30) NOT NULL PRIMARY KEY,
fullname VARCHAR(120) NOT NULL
);
CREATE TABLE hash_tracker(
hash VARCHAR(30) NOT NULL,
created_at DATETIME NOT NULL
);
DELIMITER $$
CREATE TRIGGER `test_trigger`
AFTER INSERT ON `test_table`
FOR EACH ROW
BEGIN
INSERT INTO hash_tracker VALUES (NEW.`hash`, NOW());
END$$
DELIMITER ;
Then after each insert on my test_table, i can run the following query
SELECT hash FROM hash_tracker ORDER BY created_at DESC LIMIT 1;
to get the most recently inserted hash.

SQL Multiple Tables Insertion

So I'm new to the use of multiple tables. Prior to today, 1 table suited my needs (and I could probably get away with using 1 here as well).
I'm creating a plugin for a game I play but I'm using a MySQL database to store all the information. I have 3 tables, Players, Warners and Warns. Warns has 2 foreign keys in it (one referencing to Players and the other to Warners).
At the moment I need to do 3 queries. Add the information to Players & Warners, and then to Warns. Is there a way I can cut down the amount of queries and what would happen if I were to just omit the first 2 queries?
Query Examples:
INSERT INTO slimewarnsplayers VALUES ('123e4567-e89b-12d3-a456-426655440000', 'Spedwards');
INSERT INTO slimewarnswarners VALUES ('f47ac10b-58cc-4372-a567-0e02b2c3d479', '_Sped');
INSERT INTO slimewarnswarns VALUES ('', '123e4567-e89b-12d3-a456-426655440000', 'f47ac10b-58cc-4372-a567-0e02b2c3d479', 'spamming', 'medium');
Tables:
CREATE TABLE IF NOT EXISTS SlimeWarnsPlayers (
uuid VARCHAR(36) NOT NULL,
name VARCHAR(26) NOT NULL,
PRIMARY KEY (uuid)
);
CREATE TABLE IF NOT EXISTS SlimeWarnsWarners (
uuid VARCHAR(36) NOT NULL,
name VARCHAR(26) NOT NULL,
PRIMARY KEY (uuid)
);
CREATE TABLE IF NOT EXISTS SlimeWarnsWarns (
id INT NOT NULL AUTO_INCREMENT,
pUUID VARCHAR(36) NOT NULL,
wUUID VARCHAR(36) NOT NULL,
warning VARCHAR(60) NOT NULL,
level VARCHAR(60) NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY (pUUID) REFERENCES SlimeWarnsPlayers(uuid),
FOREIGN KEY (wUUID) REFERENCES SlimeWarnsWarners(uuid)
);
Is there a way I can cut down the amount of queries?
NO, I don't see that. From your posted INSERT statements (as depicted below) it's clear that those are 3 different tables and you are inserting different data to them. so, you will have to perform the INSERT operation separately for them.
INSERT INTO slimewarnsplayers
INSERT INTO slimewarnswarners
INSERT INTO slimewarnswarns
Another option would be (May not be considered good), creating a procedure which will accept the data and table name and create a prepared statement/dynamic query to achieve what you are saying. something like (A sample pseudo code)
create procedure sp_insert(tablename varchar(10), data1 varchar(10),
data2 varchar(10))
as
begin
--dynamic query here
INSERT INTO tablename VALUES (data1, data2);
end
To explain further, you can then call this procedure from your application end passing the required data. Do note that, if you have a Foreign Key relationship with other table then you will have to catch the last inserted key from your master table and then pass the same to procedure.
No, you can't insert into multiple tables in one MySQL command. You can however use transactions.
BEGIN;
INSERT INTO slimewarnsplayers VALUES(.....);
last_id = LAST_INSERT_ID()
INSERT INTO SlimeWarnsWarners VALUES(last_id, ....);
INSERT INTO SlimeWarnsWarns VALUES(last_id, ....);
COMMIT;
I would also take a look at http://dev.mysql.com/doc/refman/5.0/en/getting-unique-id.html
and this post MySQL Insert into multiple tables? (Database normalization?)

WEBSQL simple insert into populated table

Can't seem to figure out why this simple statement doesn't work
tx.executeSql("INSERT INTO history SELECT * FROM scan");
It works correctly if the table history is empty which is not of much use but if the table history has any data then it does not carry out the insert I must do:
tx.executeSql("DELETE FROM history", []);
tx.executeSql("INSERT INTO history SELECT * FROM scan");
Any ideas? Cheers
Edit:
Structures are the same:
tx.executeSql("CREATE TABLE IF NOT EXISTS scan(ID INTEGER NOT NULL PRIMARY KEY, sunum TEXT, binnum TEXT, userid TEXT, added_on DATETIME, upload_on DATETIME)");
tx.executeSql("CREATE TABLE IF NOT EXISTS history(ID INTEGER NOT NULL PRIMARY KEY, sunum TEXT, binnum TEXT, userid TEXT, added_on DATETIME, upload_on DATETIME)");
The problem is that you're attempting to insert a duplicate primary key value into the History table. From your structure, they both have ID listed as a PRIMARY KEY, which cannot contain duplicate values.
Try specifying all columns except for that key:
INSERT INTO History
(sunum, binnum, userid, added_on, upload_on)
SELECT sunum, binnum, userid, added_on, upload_on
FROM Scan
Though, looking at the structure, the ID values aren't auto-incremented. If you don't care what the ID is, you can declare the ID column as: ID INT NOT NULL AUTO_INCREMENT.
If you need to pull the ID over from the other table, you'll have to do an upsert or merge into that table.

MySQL comparing two tables rows hash

I have two tables one is master table and other is just for cache. From time to time I check whether cache table is up to date and there is no missing data. Cache table is using MyISAM engine and master table is using InnoDB engine.
To explain it in more detail I give you an example
Cache table contains fields from following two tables
product_categories (cat-id, cat_name, parent_cat_id DEFAULT NULL, parent_cat_name DEFAULT NULL)
products (product_num, product_name, product_desc, price, image, product_date, availability)
It may be possible cache table does not contain products or it may contain products data but it may not be accurate.
In this question Compare two MySQL databases a tool Toad for MySQL has mentioned but I want to do it using PHP.
Cache table schema
products_cache | CREATE TABLE `products_cache` (
`product_num` int(10) unsigned NOT NULL AUTO_INCREMENT,
`cat_id` int(10) unsigned NOT NULL,
`parent_cat_id` int(10) unsigned DEFAULT NULL,
`cat_name` varchar(50) NOT NULL,
`parent_cat_name` varchar(50) DEFAULT NULL,
`product_desc` text NOT NULL,
`price` float(10) unsigned NOT NULL,
`image` varchar(65) NOT NULL DEFAULT '',
`product_date` DATE DEFAULT NULL,
`availability` tinyint(1) NOT NULL DEFAULT '1',
PRIMARY KEY (`product_num`),
) ENGINE=MyISAM
Possible solution
Compute the md5 of the fields and store it in cache table and then next time check the md5 in cache table if data is changed. It will work fine except there will be performance issue (I run cache fixer every month, so I think I can compromise with that). Please comment on this.
Instead of computing MD5 sums for all of your data every month you could simply record changes to a table using triggers.
CREATE TABLE changes (
table char(30) NOT NULL, -- TODO use an enum for better performance
id int NOT NULL,
UNIQUE KEY tableId (table, id),
)
CREATE TRIGGER insert_products AFTER INSERT ON products FOR EACH ROW INSERT IGNORE INTO changes (table, id) values ("products", OLD.id);
CREATE TRIGGER update_products AFTER UPDATE ON products FOR EACH ROW INSERT IGNORE INTO changes (table, id) values ("products", OLD.id);
CREATE TRIGGER delete_products AFTER DELETE ON products FOR EACH ROW INSERT IGNORE INTO changes (table, id) values ("products", OLD.id);
CREATE TRIGGER insert_product_categories AFTER INSERT ON product_categories FOR EACH ROW INSERT IGNORE INTO changes (table, id) values ("product_categories", OLD.id);
CREATE TRIGGER update_product_categories AFTER UPDATE ON product_categories FOR EACH ROW INSERT IGNORE INTO changes (table, id) values ("product_categories", OLD.id);
CREATE TRIGGER delete_product_categories AFTER DELETE ON product_categories FOR EACH ROW INSERT IGNORE INTO changes (table, id) values ("product_categories", OLD.id);
-- do this for every involved table
once in a while, you could than update changed rows (in a nightly batch job) (pseudo code):
for {table,id} in query(select table, id from changes) {
cacheRow = buildCacheRow($table, $id)
doInTransaction {
query(replace into product_cache values $cacheRow)
query(delete from changes where table = $table and id = $id)
}
}