SQL Trigger for comparing on insert - mysql

I'd like to write a trigger that checks top value in column (table consists of 4 double columns and one of them acts as a primary key (value within that field should always be bigger then previous entry)). Now is there a way to compare a top value of id column with new value that should be inserted, and rollback transaction if value of new id is equal or lower then previous top value(by using sql triggers of course).
Thank you in advance.

One and most easy way I found is to check if you get any result. I mean this:
if (SELECT COUNT(*) FROM tbl_name WHERE id = id_you_want_check >= 1) {
rollback;
}
If you know to use triggers in mysql or whatever DBMS you are using (look at the documentation of your DBMS).
PS: As said Colin, next time post your SQL DBMS to easily find an appropiate solution for you. ;)

Related

#1109 - Unknown table 'ConcertDetails' in field list

I was attempting to run a simple Insert into my CustomerOrders table and got the error: #1109 - Unknown table 'ConcertDetails' in field list
I did some searches on this and looked at about 7 different stack overflow posts on it but still not sure what is wrong. I also looked up information on triggers and there seems to be different syntax on different sites. The weird thing is this trigger used to work just fine, not sure what has been altered since 4 days ago.
I tried changing some things, for instance I removed my trigger and it let me insert, but when I put the trigger back I couldn't insert any more, so there must be something wrong with the trigger. This is what I have for the trigger:
DROP TRIGGER IF EXISTS `alterPurchasePrice`;
DELIMITER //
CREATE TRIGGER `alterPurchasePrice` BEFORE INSERT ON `CustomerOrders`
FOR EACH ROW BEGIN
IF new.DiscountCode = 'yes' THEN
SET new.PurchasePrice = ConcertDetails.Cost - 10;
END IF;
END
//
DELIMITER ;
The purpose of the trigger is to lower the price by $10 if the user types 'yes' into the DiscountCode field.
This involves the tables:
CustomerOrders: ConcertID, CustomerName, Discount Code, OrderID,
PurchasePrice ConcertDetails: ConcertDate, ConcertID, Cost
I think you need a SELECT to retrieve values from other tables.
To get the value of "cost" from "ConcertDetails" table, for a specific concert, we could write a query like this:
SELECT ConcertDetails.cost
FROM ConcertDetails
WHERE ConcertDetails.concertid = ?
assuming that "concertid" is the primary key (or unique key) of the ConcertDetails table, we would be guaranteed that the query would return at most one row.
To put that to use in the trigger, we should be able to do something like this
SET NEW.PurchasePrice
= ( SELECT d.cost - 10 AS discount_price
FROM ConcertDetails d
WHERE d.concertid = NEW.concertid
);
The value of the concertid column of the row to be inserted from the CustomerOrders table will be supplied for the query through the reference to NEW.concertid. The return from that query will be assigned to the purchaseprice column.
If the query returns more than one row (which could happen if we don't have any kind of guarantee that "concertid" is unique in the "ConcertDetails" table), the trigger will throw a "too many rows" error.
If there are no rows returned, we'd expect a NULL value to be assigned would be assigned to "purchaseprice". We would also get a NULL returned if the "cost" column is set to NULL.
Is there some sort of guarantee that "cost" will never be less than 10? If the returned Cost is 6, then the value assigned to "purchaseprice" would be -4. If we want the value assigned to "purchaseprice" to never be less than zero, we could do something like this:
SET NEW.purchaseprice
= ( SELECT GREATEST(d.cost-10,0) AS discount_price
FROM ConcertDetails d
WHERE d.concertid = NEW.concertid
);
Other notes:
I'd recommend a different naming convention for your triggers. With multiple tables and multiple triggers, it can get kind of confusing, when looking for triggers on CustomerOrders table, to remember that the name of the BEFORE INSERT trigger is "alterPurchasePrice". Especially if you (or someone else) is coming back to work on the system six months or six years from now.
The convention we follow for trigger names is to use the name of the table, followed by an underscore, followed by one of: bi, bu, bd, ai, au, ad (for Before/After Insert/Update/Delete). (Since MySQL doesn't allow more than one trigger for each of those, we don't get naming collisions. And it makes it easier to check whether a BEFORE INSERT trigger exists on a table, before someone writes a BEFORE INSERT trigger that does something else.)
I also mention, in regards to the use of CamelCase table names... the MySQL Reference Manual says this:
To avoid problems ... it is best to adopt a consistent convention, such as always creating and referring to databases and tables using lowercase names. This convention is recommended for maximum portability and ease of use.
https://dev.mysql.com/doc/refman/5.7/en/identifier-case-sensitivity.html
Then again, these are just conventions. We also follow a convention to name tables by naming what a single row represents. If we had a requirement to create this table, we would assign the name customer_order. And the BEFORE INSERT trigger on the table would be named customer_order_bi

Switch values in MySQL table

I have a table in MySQL with a field "Ordering" These are just auto incremented numbers. Now I wonder if there is a query to change the values from the last to the first...
So the entry with ordering 205 should become 1, 204 -> 2 and so on...
It's actually not an auto-increment. The problem is I started adding projects from the current website. From page 1 to page 20, but the first item on page 1 is the latest. The way I added the new projects, the newest is on the last page..
If the ordering field is switched, the new items added will be correctly numbered again and added to the front page. It's just a wrong way I started adding old projects...
Structure
Examples of the content
I can't comment due to limitations, but i really agree with #Abhik Chakraborty.
You don't want to do this. Just use the order by as he suggested.
Example:
SELECT * FROM tableName
ORDER BY columnName DESC;
Just in case you would like to know more about it: http://www.w3schools.com/sql/sql_orderby.asp
Try this as one statement call:
SET #MaxSort := (SELECT MAX(Ordering) FROM MyTable);
UPDATE MyTable t set t.Ordering = (#MaxSort + 1 - t.Ordering);
This will work if field doesn't have unique constraint.
But this field, should not be an auto_increment field at first place. Auto increment is increasing NOT decreasing counter. Except if you just try to fix existing data and the new records will be increasing.
Additional explanation
Thanks for pointing it out. Multiple query inside single query statement doesn't work with php_mysqli and it is not used because of potential MySQL injection attack if servers allows it. Maybe you can setup PHPMyAdmin to use PHP PDO.
I can use multiple queries, but I'm using PHP PDO or DBeaver database manager.
I can only suggest to supply MaxSort manually (since this is one time job anyway):
UPDATE
MyTable t
set
t.Ordering = 254 - t.Ordering + 1;

best way to get the last inserted record in sql server

Hi all I having a Identity column and a Computed primary key column in my table I need to get the last inserted record immediately after inserting the record in to database, So I have written the following queries can some one tell which is the best one to choose
SELECT
t.[StudentID]
FROM
[tbl_Student] t
WHERE
t.ID = IDENT_CURRENT('tbl_Student')
The other is using MAX as follows
Select
MAX(StudentID)
from tbl_Student
From the above two queries which is the best one to choose.
MAX and IDENT_CURRENT, according to technet, would behave much the same and both would be equally unreliable.
"IDENT_CURRENT is not limited by scope and session; it is limited to a specified table. IDENT_CURRENT returns the identity value generated for a specific table in any session and any scope. For more information, see IDENT_CURRENT (Transact-SQL)."
Basically, to return the last insert within the current scope, regardless of any potential triggers or inserts / deletes from other sessions, you should use SCOPE_IDENTITY. Of course, that's assuming you're running the query in the same scope as the actual insert in the first place. :)
If you are, you also have the alternative of simply using OUTPUT clause to get the inserted ID values into a table variable / temporary table, and select from there.
The original answer, where my assumptions about IDENT_CURRENTwhere wrong.
Use the first one. IDENT_CURRENT should give you the last item for the current connection. If someone else would insert another student concurrently IDENT_CURRENT will give you the correct value for both clients, while MAX might give you a wrong value.
EDIT:
As it was mentioned in the other answer IDENT_CURRENTand MAXare equally unreliable in case of concurrent usage. I would still go for IDENT_CURRENT but if you want to get the last identity used by the current scope or session you can use the functions ##IDENTITY and SCOPE_IDENTITY. This technet article explains the detailed differences between IDENT_CURRENT, ##IDENTITY and SCOPE_IDENTITY.

How to fix a table size in MySQL?

Is there a way or an option available in MySQL to fix the size of a table? Which will work as a circular buffer and overwrites the old data whenever a new data gets inserted. This is useful when an application is collecting the stats and a month old data becomes no longer useful.
I know programmatically this is possible but I don't want to do that, hence this request.
BTW, this is similar to a feature called capped collection in MongoDb.
If you want to throw away last month's data, just use the day-of-the-month in your primary key. Last month's "26" will be overwritten by next month's "26".
I don't believe there is a specific option to achieve this, however, you could make it work using a simple trigger. (I know it's a programmatic way but I believe it is the only pure-sql option in mysql)
CREATE TRIGGER `CapTable` BEFORE INSERT ON `MyTable` FOR EACH ROW BEGIN
SET #NumRows = (SELECT COUNT(*) FROM MyTable);
IF #NumRows > 1000 THEN
DELETE FROM MyTable ORDER BY id LIMIT 1;
/* delete the oldest row, assuming id is auto_increment */
END IF;
END;

Auto Increment Manually

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