Getting last record from mysql - java

I am using mysql and facing some problem. I want to retrieve last row that is inserted.
<< Below are details >>
Below is how I created table.
create table maxID (myID varchar(4))
I inserted four values in it as below
insert into maxID values ('A001')
insert into maxID values ('A002')
insert into maxID values ('A004')
insert into maxID values ('A003')
When I execute select myID, last_insert_id() as NewID from maxID, I get output as below
myId NewID
A001 0
A002 0
A004 0
A003 0
When I tried below code,
select myId, last_insert_id() as NewID, #rowid:=#rowid+1 as myrow from maxID, (SELECT #rowid:=0) as init
I get output as below.
myId NewID rowid
A001 0 1
A002 0 2
A004 0 3
A003 0 4
However when I use code select myId, last_insert_id() as NewID, #rowid:=#rowid+1 as myrow from maxID, (SELECT #rowid:=0) as init where #rowid = 4, I get error as Uknown column 'myrow' in where clause
When I use where #rowid=4, I don't get any data in tables.
Link to play with data
Note: Here I am using 4 just to get desired output. Later I can get this from a query (select max(rowid) from maxID)
Please suggest me what need to do if I want to see only last record i.e. A003.
Thanks for your time.
Update:
I already have millions of data in my table so I can't add new column in it as suggested below.

Almost done.
You succeed in getting the insert order.
So:
select myId, #rowid:=#rowid+1 as myrow from maxID, (SELECT #rowid:=0) as init ORDER BY myrow desc LIMIT 1;
In my console I get the following:
mysql> select myId, #rowid:=#rowid+1 as myrow from maxID, (SELECT #rowid:=0) as
init ORDER BY myrow desc LIMIT 1;
+------+-------+
| myId | myrow |
+------+-------+
| A003 | 4 |
+------+-------+
1 row in set (0.00 sec)
Demo
UPDATE
Yak is right. My solution is not deterministic. Maybe it works for small amount of records. I found tons of post abount unreliability of default sorting of a SELECT statement (here for example).
Next steps:
Under which conditions the default SELECT sorting matches the insertion order?
Is it possible to obtain the last inserted record in a table without an incremental id or an insertion timestamp?
I know it's not an answer, but stating the problem limit the problem.

It seems that a SELECT is not guaranteed to return rows in any specific order (without using an ORDER BY clause, of course).
As per the SQL-92 standard (p. 373):
If an < order by clause > is not specified, then the table specified by the < cursor specification > is T and the ordering of rows in T is implementation-dependent.
Okay, MySQL is not fully SQL-92-compliant, but this is a serious hint.
Laurynas Biveinis (apparently affiliated with Percona) also states:
The order of the rows in the absence of ORDER BY clause (...) could be different due to the moon phase and that is OK.
The MySQL manual says about InnoDB:
InnoDB always orders table rows according to [a PRIMARY KEY or NOT NULL UNIQUE index] if one is present.
As far as I am concerned, I assume MySQL could also reorder rows after an OPTIMIZE TABLE or even reuse empty spaces after many deletes and inserts (I have tried to find an example of this, and have failed so far).
Given your table structure, the bottomline is, unfortunately, that so many factors could have altered the order of the rows; I see no solution to reliably determine the order they were inserted. Unless you kept all binary logs since you created the table, of course ;)
Nevertheless, you may still want to add a sequence column to your table. Existing rows would be assigned a possibly inaccurate sequence number, but at least future rows will be correctly sequenced.
ALTER TABLE maxID ADD sequence INT DEFAULT NULL;
ALTER TABLE maxID ADD INDEX(sequence);
ALTER TABLE maxID MODIFY sequence INT AUTO_INCREMENT;
http://sqlfiddle.com/#!2/63a8d/1

From your insert script, A004 is not the last inserted record. It's the third one. If you want to get the last record in alphabetical order (which A004 is), you must use
select myID from maxID order by myID desc limit 1
If you want the last inserted row, why don't you just use add an autoincrement column to your table? That's the point of those kinds of columns. The autoincrement column doesn't have to be the PK (it should be, but doesn't have to if you don't have the choice).

As mentioned in the MySQL help for last_insert_id, you can only use it with **auto-increment columns. This means that you cannot make MySQL find the most recently inserted row for you, unless you know something about the order of the IDs. If they are sorted like your example suggests, then you can use
SELECT *
FROM maxID
WHERE myId = max(myId)
But I suggest adding an auto-increment column to the table and then use = last_insert_id() in your WHERE clause. See also this page for information on how to obtain the last ID.

last_insert_id does not work because it only returns the last value generated by an auto-increment field. However I think that solution may be on the right track. I would suggest adding another column that is a auto-increment integer. You can then insert data and to retrieve the row you just inserted select the last_insert_id(). To retrieve the most recent row (inserted by any process) select the max number for the new column, or sort by it desc.
If you want to use the varchar column you can do an alphabetic sort, but that does not guarantee it will be the last row you inserted row or even the most recently inserted row.
The one other solution I can think of which may do what you need is to create a stored procedure which creates the row, inserts it into the table, and then returns it to you.
Your usage of #rowid is simply counting the order rows are returned to you. There is no guarantee that they are returned to you in the order of oldest to newest. There are a variety of things that can affect the order in which rows are stored.

Please use the following query.
Select * from maxID order by myId desc limit 1,1;

Related

How to auto generate product SKU in MySQL? [duplicate]

I'm trying to make a blog system of sort and I ran into a slight problem.
Simply put, there's 3 columns in my article table:
id SERIAL,
category VARCHAR FK,
category_id INT
id column is obviously the PK and it is used as a global identifier for all articles.
category column is well .. category.
category_id is used as a UNIQUE ID within a category so currently there is a UNIQUE(category, category_id) constraint in place.
However, I also want for category_id to auto-increment.
I want it so that every time I execute a query like
INSERT INTO article(category) VALUES ('stackoverflow');
I want the category_id column to be automatically be filled according to the latest category_id of the 'stackoverflow' category.
Achieving this in my logic code is quite easy. I just select latest num and insert +1 of that but that involves two separate queries.
I am looking for a SQL solution that can do all this in one query.
This has been asked many times and the general idea is bound to fail in a multi-user environment - and a blog system sounds like exactly such a case.
So the best answer is: Don't. Consider a different approach.
Drop the column category_id completely from your table - it does not store any information the other two columns (id, category) wouldn't store already.
Your id is a serial column and already auto-increments in a reliable fashion.
Auto increment SQL function
If you need some kind of category_id without gaps per category, generate it on the fly with row_number():
Serial numbers per group of rows for compound key
Concept
There are at least several ways to approach this. First one that comes to my mind:
Assign a value for category_id column inside a trigger executed for each row, by overwriting the input value from INSERT statement.
Action
Here's the SQL Fiddle to see the code in action
For a simple test, I'm creating article table holding categories and their id's that should be unique for each category. I have omitted constraint creation - that's not relevant to present the point.
create table article ( id serial, category varchar, category_id int )
Inserting some values for two distinct categories using generate_series() function to have an auto-increment already in place.
insert into article(category, category_id)
select 'stackoverflow', i from generate_series(1,1) i
union all
select 'stackexchange', i from generate_series(1,3) i
Creating a trigger function, that would select MAX(category_id) and increment its value by 1 for a category we're inserting a row with and then overwrite the value right before moving on with the actual INSERT to table (BEFORE INSERT trigger takes care of that).
CREATE OR REPLACE FUNCTION category_increment()
RETURNS trigger
LANGUAGE plpgsql
AS
$$
DECLARE
v_category_inc int := 0;
BEGIN
SELECT MAX(category_id) + 1 INTO v_category_inc FROM article WHERE category = NEW.category;
IF v_category_inc is null THEN
NEW.category_id := 1;
ELSE
NEW.category_id := v_category_inc;
END IF;
RETURN NEW;
END;
$$
Using the function as a trigger.
CREATE TRIGGER trg_category_increment
BEFORE INSERT ON article
FOR EACH ROW EXECUTE PROCEDURE category_increment()
Inserting some more values (post trigger appliance) for already existing categories and non-existing ones.
INSERT INTO article(category) VALUES
('stackoverflow'),
('stackexchange'),
('nonexisting');
Query used to select data:
select category, category_id From article order by 1,2
Result for initial inserts:
category category_id
stackexchange 1
stackexchange 2
stackexchange 3
stackoverflow 1
Result after final inserts:
category category_id
nonexisting 1
stackexchange 1
stackexchange 2
stackexchange 3
stackexchange 4
stackoverflow 1
stackoverflow 2
Postgresql uses sequences to achieve this; it's a different approach from what you are used to in MySQL. Take a look at http://www.postgresql.org/docs/current/static/sql-createsequence.html for complete reference.
Basically you create a sequence (a database object) by:
CREATE SEQUENCE serials;
And then when you want to add to your table you will have:
INSERT INTO mytable (name, id) VALUES ('The Name', NEXTVAL('serials')

MySQL auto increment key in table mistakes

so I created a table with a column which I declared EmployeeID as a primary key int with auto increment, but here is the problem:
If I delete a row and then insert a new one the new increment will still count
For example:
ID Name
1 jayden
2 karen
delete karen ...
ID Name
1 jayden
insert new one
ID Name
1 jayden
3 nicolas
So it basically skips the previous ID.
This is not a mistake, it is how MySQL works with auto_increment. It stores the last value which it gave at the insertion and increments it. You can always get the last ID at your next insertion
insert into yourtable(id, Name)
select id + 1, 'foo'
from yourtable
where id = (select max(id) from yourtable);
That's because an auto increment is a independent property of your table, that is only used when assigning new IDs and does not depend on the last existent ID on your table. This is how many relational databases, not only MySQL, were designed for.
To achieve what you want (which seems to be having IDs in sequence without "holes") you would need to query the last ID and increment on it to be set manually.
Ex:
SELECT id from Users ORDER BY id DESC LIMIT 1
Or instead setting the auto_increment of your table according to your last id.
Be aware that both of this is not performatically wise. You should really stick to the auto increment default behavior, since it's optimal, unless you have some strong reason for not doing things as they were designed to be done.
This is expected behavior from the 'delete' command
What would you ever want/need an Unique Identifier that can be reaffected to someone else?
The 'Truncate' command will delete all your data and reset the ID, maybe this is what you are looking for
After delete a row use this query,
ALTER TABLE tbl_name AUTO_INCREMENT = ID;
Actually this is how MySQL works and it's not a mistake

How to count different values in one column of table in sql using java

I have a situation where i have to count number of strings of same type in one column of a table, for eg. a column would be having values such as A=absent P=present
A A A P P P P P P P A
So i need to count all the strings of same type, like P i.e Present that means query should give count result 7.
What can be idol query for this?
I think this is the simplest Query that I can think of in terms of SQL Select count(*) from table where attendence='P'
Update:
Ensure to use parameterized format of prepared statement in your Java code to prevent SQL Injection.
SELECT attendance, COUNT(*)
FROM Roster
GROUP BY attendance;
Will give you the count of each value in the column. So, for a table having 4 A values and 7 P values, the query will return
attendance | COUNT(*)
___________|_________
|
A | 4
P | 7
Aside: Since your table has repetitive values in its attendance column, you should consider pulling all possible values for attendance out into their own "enumeration" of sorts. SQL doesn't offer enumerations, but there are a few ways to achieve a similar effect.
One method is to create a look up table that contains an ID column (can be an auto-increment), and the values that you want for attendance. Then, in your Roster table, use a foreign key to reference the ID of the correct value.
To save yourself time in the future, you can create a View which uses the attendance values rather than the IDs.
You can read up on Database Normalization if you're interested in improving your database's design.

How can get the last value of column

How can I retrieve the last entered value of the column in the database (MS ACCESS 2007)
I used the following code
String sql = "SELECT Last(RegNumber) FROM Death ";
but it does not work in MS ACCESS and when I run the program Error generates as
java.sql.SQLException: Column not found
but I have created a column in database as RegNumber
I am using Java for programming in which I used this query
EDIT:
RegNumber is in String form not in integer form so I cant use DESC or ASC
Please help me
Sort your table by whatever criteria you'd like and use SELECT TOP 1 * FROM myTable ORDER BY RegNumber ASC.
Or ORDER BY incrementingId DESC
Basically there must be some logical order to the sorting for what you refer to as the "last entered column" (which I assume means row, not column)
EDIT: Your function is correct in Access, and should return the correct value. However Java may not interpret it correctly. Try your query in an Access native query, then try debugging your Java. If it's simply that Java does not support this function, consider using the built in ResultSet() functions in Java.sql
ResultSet rs = ....;
rs.last();
int RegNumber = rs.getRow();
I do not know about the last() function in MS ACCESS, but I have another idea:
Usually there is an automatically generated id for each table, so you can sort on it and get the first record from the result set like this:
SELECT RegNumber
FROM Death
ORDER BY id DESC
That depend of your database structure.
Typically with table come some unique identifier, if you are sure that it comes always in order to database you could use function MAX to retrieve the identifier and then whole row.
Another scenario is just to a timestamp columns that describe the time when column was created , this approach satisfying if the sequence is really crucial if not the id should be enough.
Following will return the last and lastest RegNumber :
SELECT RegNumber FROM Death ORDER BY RegNumber DESC

How to manage consecutive column values in table rows

A little presentation for what I want to do:
Consider the case where different people from a firm get, once a year, an all expenses paid trip to somewhere. There may be 1000 persons that could qualify for the trip but only 16 places are available.
Each of this 16 spots has an associated index which must be from 1 to 16. The ones on the reservation have index starting from 17.
The first 16 persons that apply get a definite spot on the trip. The rest end up on the reservation list. If one of the first 16 persons cancels, the first person with a reservation gets his place and all the indexes are renumbered to compensate for the person that canceled.
All of this is managed in a Java web app with an Oracle DB.
Now, my problem:
I have to manage the index in a correct way (all sequential, no duplicate indexes), with possible hundreds of people that simultaneously apply for the trip.
When inserting a record in the table for the trip, the way of getting the index is by
SELECT MAX(INDEX_NR) + 1 AS NEXT_INDEX_NR FROM TABLE
and using this as the new index (this is done Java side and then a new query to insert the record). It is obvious why we have multiple spots or reservations with the same index. So, we get, let’s say, 19 people on the trip because 4 of them have index 10, for example.
How can I manage this? I have been thinking of 3 ways so far:
Use an isolation level of Serializable for the DB transactions (don’t like this one);
Insert a record with no INDEX_NR and then have a trigger manage the things… in some way (never worked with triggers before);
Each record also has a UPDATED column. Could I use this in some way? (note that I can’t lose the INDEX_NR since other parts of the app make use of it).
Is there a best way to do this?
Why make it complicated ?
Just insert all reservations as they are entered and insert a timestamp of when they resevered a spot.
Then in you query just use the timestamp to sort them.
There is offcourse the chance that there are people that reserved a spot at the very same millisecond then just use a random method to assign order.
Why do you need to explicitly store the index? Instead you could store each person's order (which never changes) along with an active flag. In your example if person #16 pulls out you simply mark them as inactive.
To compute whether a person qualifies for the trip you simply count the number of active people with order less than that person:
select count(*)
from CompetitionEntry
where PersonOrder < 16
and Active = 1
This approach removes the need for bulk updates to the database (you only ever update one row) and hence mostly mitigates your problem of transactional integrity.
Another way would be to explicitly lock a record on another table on the select.
-- Initial Setup
CREATE TABLE NUMBER_SOURCE (ID NUMBER(4));
INSERT INTO NUMBER_SOURCE(ID) VALUES 0;
-- Your regular code
SELECT ID AS NEXT_INDEX_NR FROM NUMBER_SOURCE FOR UPDATE; -- lock!
UPDATE NUMBER_SOURCE SET ID = ID + 1;
INSERT INTO TABLE ....
COMMIT; -- releases lock!
No other transaction will be able to perform the query on the table NUMBER_SOURCE until the commit (or rollback).
When adding people to the table, give them an ID in such a way that the ID is ascending in the order in which they were added. This can be a timestamp.
Select all the records from the table which qualify, order by ID, and update their INDEX_NR
Select * from table where INDEX_NR <= 16 order by INDEX_NR
Step #2 seems complicated but it's actually quite simple:
update (
select *
from TABLE
where ...
order by ID
)
set INDEX_NR = INDEXSEQ.NEXTVAL
Don't forget to reset the sequence to 1.
Calculate your index in runtime:
CREATE OR REPLACE VIEW v_person
AS
SELECT id, name, ROW_NUMBER() OVER (ORDER BY id) AS index_rn
FROM t_person
CREATE OR REPLACE TRIGGER trg_person_ii
INSTEAD OF INSERT ON v_person
BEGIN
INSERT
INTO t_person (id, name)
VALUES (:new.id, :new.name);
END;
CREATE OR REPLACE TRIGGER trg_person_iu
INSTEAD OF UPDATE ON v_person
BEGIN
UPDATE t_person
SET id = :new.id,
name = :new.name
WHERE id = :old.id;
END;
CREATE OR REPLACE TRIGGER trg_person_id
INSTEAD OF DELETE ON v_person
BEGIN
DELETE
FROM t_person
WHERE id = :old.id;
END;
INSERT
INTO v_person
VALUES (1, 'test', 1)
SELECT *
FROM v_person
--
id name index_rn
1 test 1
INSERT
INTO v_person
VALUES (2, 'test 2', 1)
SELECT *
FROM v_person
--
id name index_rn
1 test 1
2 test 2 2
DELETE
FROM v_person
WHERE id = 1
SELECT *
FROM v_person
--
id name index_rn
2 test 2 1
"I have to manage the index in a correct way (all sequential, no duplicate indexes), with possible hundreds of people that simultaneously apply for the trip.
When inserting a record in the table for the trip, the way of getting the index is by
SELECT MAX(INDEX_NR) + 1 AS NEXT_INDEX_NR FROM TABLE
and using this as the new index (this is done Java side and then a new query to insert the record). It is obvious why we have multiple spots or reservations with the same index."
Yeah. Oracle's MVCC ("snapshot isolation") used incorrectly by someone who shouldn't have been in IT to begin with.
Really, Peter is right. Your index number is, or rather should be, a sort of "ranking number" on the ordered timestamps that he mentions (this holds a requirement that the DBMS can guarantee that any timestamp value appears only once in the entire database).
You say you are concerned with "regression bugs". I say "Why do you need to be concerned with "regression bugs" in an application that is DEMONSTRABLY beyond curing ?". Because your bosses paid a lot of money for the crap they've been given and you don't want to be the pianist that gets shot for bringing the message ?
The solution depends on what you have under your control. I assume that you can change both database and Java code, but refrain from modifying the database scheme since you had to adapt too much Java code otherwise.
A cheap solution might be to add a uniqueness constraint on the pair (trip_id, index_nr) or just on index_nr if there is just one trip. Additionally add a check contraint check(index_nr > 0) - unless index_nr is already unsigned. Everything else is then done in Java: When inserting a new applicant as described by you, you have to add code catching the exception when someone else got inserted concurrently. If some record is updated or deleted, you either have to live with holes between sequence numbers (by selecting the 16 candidates with the lowest index_nr as shown by Quassnoi in his view) or fill them up by hand (similarily to what Aaron suggested) after every update/delete.
If index_nr is mostly used in the application as read-only, a better solution might be to combine the answers of Peter and Quassnoi: Use either a time stamp (automatically inserted by the database by defining the current time as default) or an auto-incremented integer (as default inserted by the database) as value stored in the table. And use a view (like the one defined by Quassnoi) to access the table and the automatically calculated index_nr from Java. But also define both constraints like for the cheap solution.

Categories