sql delete from start - mysql

Is there anyway I can restrict the amount of rows allowed in a table to say 40, and then when 41st is added, the table deletes the first one?

Yes, you could do this with a trigger. What RDMS are you using?

CREATE TABLE animals (
id MEDIUMINT NOT NULL AUTO_INCREMENT,
name CHAR(30) NOT NULL,
PRIMARY KEY (id)
);
-- if this is 41st record, ...
-- the statement below will delete the id with 1, and so forth
insert into animals(name) values('wolverine');
delete from animals where id <= LAST_INSERT_ID() - 40;

Related

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.

How to enforce insert with specific field?

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)
);

Is it possible to update only a single field with ON DUPLICATE KEY UPDATE in a table with multiple fields?

Is it possible to update only a single field with ON DUPLICATE KEY UPDATE in a table with multiple fields?
If I have a table with three fields; key, cats, dogs where key is the primary key is it possible to update a record on duplicate key, changing only one field, (for my example cats) without changing the value in the other non-key fields (dogs). This is without knowing what the value of dogs from outside of the database at the time of insertion (i.e. I have a variable holding cats value, but not one holding dogs value)
INSERT INTO `myTable` (`key`,`cats`) VALUES('someKey1','Manx') ON DUPLICATE KEY UPDATE `cats` = 'Manx';
At the moment when I run something like this and the key already exists in the table dogs is set to NULL even when it had a value previously.
Gordon is right, it does not work the way I described. If you see this, it is not caused by the ON DUPLICATE UPDATE statement, but something else. Here is the proof:
CREATE TABLE IF NOT EXISTS `myTable` (
`key` varchar(20) NOT NULL default '',
`cats` varchar(20) default NULL,
`dogs` varchar(20) default NULL,
PRIMARY KEY (`key`)
)
The run
INSERT INTO `myTable` (`key`, `cats`, `dogs`) VALUES
('someKey1', NULL, 'jack-russell');
Then run
INSERT INTO `myTable` (`key`,`cats`) VALUES
('someKey1','Manx') ON DUPLICATE KEY UPDATE `cats` = 'manx';
Then check the table
I think you should try to UPSERT.
Please examine this:
INSERT INTO `item` (`item_name`, items_in_stock) VALUES( 'A', 27)
ON DUPLICATE KEY UPDATE `new_items_count` = `new_items_count` + 27
MySQL UPSERT

Defining Composite Key with Auto Increment in MySQL

Scenario:
I have a table which references two foreign keys, and for each unique combination of these foreign keys, has its own auto_increment column. I need to implement a Composite Key that will help identify the row as unique using combination of these three (one foreign keys and one auto_increment column, and one other column with non-unique values)
Table:
CREATE TABLE `issue_log` (
`sr_no` INT NOT NULL AUTO_INCREMENT ,
`app_id` INT NOT NULL ,
`test_id` INT NOT NULL ,
`issue_name` VARCHAR(255) NOT NULL ,
primary key (app_id, test_id,sr_no)
);
Of course, there has to be something wrong with my query, because of which the error thrown is:
ERROR 1075: Incorrect table definition; there can be only one auto
column and it must be defined as a key
What I am trying to achieve:
I have an Application Table (with app_id as its primary key), each Application has a set of Issues to be resolved, and each Application has multiple number of tests (so the test_id col)
The sr_no col should increment for unique app_id and test_id.
i.e. The data in table should look like:
The database engine is InnoDB.
I want to achieve this with as much simplicity as possible (i.e. avoid triggers/procedures if possible - which was suggested for similar cases on other Questions).
You can't have MySQL do this for you automatically for InnoDB tables - you would need to use a trigger or procedure, or user another DB engine such as MyISAM. Auto incrementing can only be done for a single primary key.
Something like the following should work
DELIMITER $$
CREATE TRIGGER xxx BEFORE INSERT ON issue_log
FOR EACH ROW BEGIN
SET NEW.sr_no = (
SELECT IFNULL(MAX(sr_no), 0) + 1
FROM issue_log
WHERE app_id = NEW.app_id
AND test_id = NEW.test_id
);
END $$
DELIMITER ;
You can do this with myISAM and BDB engines. InnoDB does not support this. Quote from MySQL 5.0 Reference Manual.
For MyISAM and BDB tables you can specify AUTO_INCREMENT on a secondary column in a multiple-column index. In this case, the generated value for the AUTO_INCREMENT column is calculated as MAX(auto_increment_column) + 1 WHERE prefix=given-prefix.
http://dev.mysql.com/doc/refman/5.0/en/example-auto-increment.html
I don't fully understand your increment requirement on the test_id column, but if you want an ~autoincrement sequence that restarts on every unique combination of (app_id, test_id), you can do an INSERT ... SELECT FROM the same table, like so:
mysql> INSERT INTO `issue_log` (`sr_no`, `app_id`, `test_id`, `issue_name`) SELECT
IFNULL(MAX(`sr_no`), 0) + 1 /* next sequence number */,
3 /* desired app_id */,
1 /* desired test_id */,
'Name of new row'
FROM `issue_log` /* specify the table name as well */
WHERE `app_id` = 3 AND `test_id` = 1 /* same values as in inserted columns */
This assumes a table definition with no declared AUTO_INCREMENT column. You're essentially emulating autoincrement behavior with the IFNULL(MAX()) + 1 clause, but the manual emulation works on arbitrary columns, unlike the built-in autoincrement.
Note that the INSERT ... SELECT being a single query ensures atomicity of the operation. InnoDB will gap-lock the appropriate index, and many concurrent processes can execute this kind of query while still producing non-conflicting sequences.
You can use a unique composite key for sr_no,app_id & test_id. You cannot use incremental in sr_no as this is not unique.
CREATE TABLE IF NOT EXISTS `issue_log` (
`sr_no` int(11) NOT NULL,
`app_id` int(11) NOT NULL,
`test_id` int(11) NOT NULL,
`issue_name` varchar(255) NOT NULL,
UNIQUE KEY `app_id` (`app_id`,`test_id`,`sr_no`)
) ENGINE=InnoDB ;
I have commented out unique constraint violation in sql fiddle to demonstrate (remove # in line 22 of schema and rebuild schema )
This is what I wanted
id tenant
1 1
2 1
3 1
1 2
2 2
3 2
1 3
2 3
3 3
My current table definition is
CREATE TABLE `test_trigger` (
`id` BIGINT NOT NULL,
`tenant` varchar(255) NOT NULL,
PRIMARY KEY (`id`,`tenant`)
);
I created one table for storing the current id for each tenant.
CREATE TABLE `get_val` (
`tenant` varchar(255) NOT NULL,
`next_val` int NOT NULL,
PRIMARY KEY (`tenant`,`next_val`)
) ENGINE=InnoDB ;
Then I created this trigger which solve my problem
DELIMITER $$
CREATE TRIGGER trigger_name
BEFORE INSERT
ON test_trigger
FOR EACH ROW
BEGIN
UPDATE get_val SET next_val = next_val + 1 WHERE tenant = new.tenant;
set new.id = (select next_val from get_val where tenant=new.tenant);
END$$
DELIMITER ;
This approach will be thread safe also because any insertion for the same tenant will happen sequentially because of the update query in the trigger and for different tenants insertions will happen parallelly.
Just add key(sr_no) on auto-increment column:
CREATE TABLE `issue_log` (
`sr_no` INT NOT NULL AUTO_INCREMENT ,
`app_id` INT NOT NULL ,
`test_id` INT NOT NULL ,
`issue_name` VARCHAR(255) NOT NULL ,
primary key (app_id, test_id,sr_no),
key (`sr_no`)
);
Why don't you try to change the position of declare fields as primary key, since when you use "auto_increment" it has to be referenced as the first. Like in the following example
CREATE TABLE `issue_log` (
`sr_no` INT NOT NULL AUTO_INCREMENT ,
`app_id` INT NOT NULL ,
`test_id` INT NOT NULL ,
`issue_name` VARCHAR(255) NOT NULL ,
primary key (sr_no,app_id, test_id)
);

MySQL INSERT INTO... SELECT - How to determine which select clauses result in NULL?

I'm trying to insert a row into a table which references keys in other tables. There are instances where the insert/select will fail because the selected value can not be null. I'd like to know which selects fail so that I can create the necessary rows in other tables as needed.
The following example is a bit contrived, but should illustrate the challenge here:
CREATE TABLE TableOne
(
TableOneId INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
Value VARCHAR(64) NOT NULL
) ENGINE=InnoDB;
CREATE TABLE TableTwo
(
TableTwoId INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
Value VARCHAR(64) NOT NULL
) ENGINE=InnoDB;
CREATE TABLE DependentTable
(
DependentId INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
TableOneId INT UNSIGNED NOT NULL,
TableTwoId INT UNSIGNED NOT NULL,
Value FLOAT NOT NULL,
FOREIGN KEY (TableOneId) REFERENCES TableOne(TableOneId),
FOREIGN KEY (TableTwoId) REFERENCES TableOne(TableTwoId)
) ENGINE=InnoDB;
INSERT INTO DependentTable (Value, TableOneId, TableTwoId) SELECT 1.0, TableOne.TableOneId, TableTwo.TableTwoId FROM TableOne,TableTwo WHERE TableOne.Value='TableOneValue' AND TableTwo.Value='TableTwoValue';
Query OK, 0 rows affected (0.00 sec)
Records: 0 Duplicates: 0 Warnings: 0
If either 'TableOneValue' or 'TableTwoValue' doesn't exist, then the select will return null and 0 rows will be inserted - as expected. However, I was hoping to get some other piece of information that would allow me to determine which value didn't exist so I can create it.
The problem, at least in my actual situation, is that blindly creating each value to satisfy the foreign key reference would be overkill/expensive as there are multiple foreign keys and some of those dependent tables will have other required dependencies. I'd like to approach it a little more intelligently and determine which reference(s) didn't exist and only create those instead.
I could try to create the rows in dependent tables one-by-one, wait until a row is actually inserted ( versus getting a duplicate entry ), and then try the original insert again - but it feels like there should be a better/more elegant way to do this...
Thanks in advance for any suggestions/ideas...
What I'd do is insert everything into an intermediate table without the Foreign Keys. Then use that table to query against TableOne and TableTwo, to see which items in TableOneId and TableTwoId do not exist in TableOne and TableTwo. Then adjust, cleanup, etc.
INSERT INTO IntermediateTable(DependentId,
TableOneId,
TableTwoId,
Value) blah blah blah
The following will show you the missing values (repeat for TableTwo):
SELECT *
FROM IntermediateTable i
LEFT JOIN TableOne t1 ON t1.TableOneid = i.TableOneId
WHERE t1.TableOneId IS NULL
When cleanup is done, you can copy everything from the intermediate table into DependentTable with foreign keys defined.
INSERT INTO DependentTable SELECT * FROM IntermediateTable --everything has been cleaned up so that there are now no missing ids in TableOne and TableTwo.
Without this intermediate table, you lack something to use for a query against TableOne and TableTwo for undefined ids.