Auto Increment including Year and Month in MySql - java

I have a table called > Project with an auto increment field for project estimate bid number called Project_ID.
This field is auto incremented. I have created it as an 8 digit character field which carries the field rule.
I need it to auto increment as a two number year, two number month, include a hyphen, and then a number starting at 001 for first record in that time period.
An example for the month of April 2012 would be 1204-001 for the first
record, 1204-002 for the 2nd and etc. then when the month of May rolls
around the Project_ID would change to 1205-001.
What I’ve been trying to write is as follows, I kept it as a simple default expression with a default value of
Cyear(date()) + (month()) + “-“ + “001” .
How I have Achieve this?

Basically, you can use BEFORE INSERT TRIGGER on the table you want the column to be incremented.
Here are some steps to create a simple algorithm and put this code inside the trigger:
// get current YEAR
SET #cur_Year = CONCAT(DATE_FORMAT(CURDATE(), '%Y'));
// get current MONTH
SET #cur_MONTH = CONCAT(DATE_FORMAT(CURDATE(), '%m'));
// concatenate YEAR and MONTH
SET #Year_Month = CONCAT(#cur_Year, #cur_MONTH);
// get the last value for the current YEAR and MONTH
SET #max_ID = ( SELECT MAX(ID)
FROM tableName
WHERE ID LIKE CONCAT(#Year_Month, '-%'));
// get the last three characters from the id, convert in to
// integer and increment by 1
SET #last_ID = CAST(RIGHT(#max_ID, 3) AS SIGNED) + 1;
// pad zero on the left using LPAD and
// concatenate it with YEAR and MONTH
SET #new_ID = CONCAT(#Year_Month,'-',LPAD(CAST(#last_ID AS CHAR(3)), 3, '0'));

INSERT INTO (Project_ID, col1, col2, col3)
SELECT DATE_FORMAT(NOW(), CONCAT('%y%m-',
(( SELECT RIGHT(CONCAT('000', (RIGHT(Project_ID, 3) + 1)), 3) AS number
FROM table_name
WHERE LEFT(Project_ID, 5) = DATE_FORMAT(NOW(), '%y%m-'))
ORDER BY Project_ID DESC
UNION
( SELECT '001')
LIMIT 1))),
'Col1 data', 'Col2 data', 'Col3 data'
This might look a bit odd, so I'll just explain the flow:
I use INSERT INTO ... SELECT so that I can check existing data from table_name to see if there are any existing cases already. The WHERE will find existing cases, and thanks to both RIGHT and LEFT it isn't too hard to carve out relevant data needed. If no rows are found, '001' is used instead, then you simply assign the existing columns as shown.

I have absolutely solved it,just take a look..
At first you have to take a sample table where the columns will be same to the columns of your original table except the column project_id.
then first insert a row in the original table where the value of column project_id=0 and the other columns are null,just insert the first row manually like this.
Then create a trigger on the sample table like the following...
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
delimiter ;;
create trigger t after insert on try
for each row
begin
declare v int;
declare c int;
set v=(select max(project_id) from original);
if(v=0)then
insert into original set
project_id=concat((select concat(right(substring_index((select * from(select curdate() from try limit 1) as a),'-','1'),2),right(substring_index((select * from(select curdate() from try limit 1) as a),'-','2'),2)) from try limit 1),'-001'),
project=new.project;
else
set c=(select right((select max(project_id) from original)as x,3) from original limit 1);
insert into original set
project_id=concat((select concat(right(substring_index((select * from(select curdate() from try limit 1) as a),'-','1'),2),right(substring_index((select * from(select curdate() from try limit 1) as a),'-','2'),2)) from try limit 1),concat('-00',c+1)),
project=new.project;
delete from original limit 1;
end if;
end;;
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
In the above trigger my sample table was try(project text) and the original table was original(project_id text,project text).
After creating a trigger like this on the sample table,start inserting rows in the sample table,the rows will automatically inserted in the original table with auto_increment values in the project_id column like..1405-001,1405-002,1405-003.... where 14 is 2014 and 05 is May and the rest are auto_incremented values which is being incremented using the trigger.
Just follow the above steps, your problem will be surely solved.

Related

How can I access a value when inserting into a table?

I'm trying to write a java sql query, the simplified table would be table(name,version) with a unique constraint on (name, version).
I'm trying to insert a row into my database with a conditional statement. Meaning that when a entry with the same name exists, it should insert the row with same name and its version increased by 1.
I have tried with the following:
INSERT INTO table(name,version)
VALUES(?, CASE WHEN EXISTS(SELECT name from table where name=?)
THEN (SELECT MAX(version) FROM table WHERE name = ?) +1
ELSE 1 END)
values are sent by user.
My question is, how can I access the 'name' inside the values so I could compare them?
If you want to write this as a single query:
INSERT INTO table (name, version)
SELECT ?, COLAESCE(MAX(t2.version) + 1, 1)
FROM table t2
WHERE t2.name = ?;
That said, this is dangerous. Two threads could execute this query "at the same time" and possibly create the same version number. You can prevent this from happening by adding a unique index/constraint on (name, version).
With the unique index/constraint, one of the updates will fail if there is a conflict.
I see at least two approaches:
1. For each pair of name and version you first query the max version:
SELECT MAX(VERSION) as MAX FROM <table> WHERE NAME = <name>
And then you insert the result + 1 with a corresponding insert query:
INSERT INTO <table>(NAME,VERSION) VALUES (<name>,result+1)
This approach is very straight-forward, easy-to-read and implement, however, not really performant because of so many queries necessary.
You can achieve that with sql alone with sql analytics and window functions, e.g.:
SELECT NAME, ROW_NUMBER() over (partition BY NAME ORDER BY NAME) as VERSION FROM<table>
You can then save the result of this query as a table using CREATE TABLE as SELECT...
(The assumption here is that the first version is 1, if it is not the case, then one could slightly rework the query). This solution would be very performant even for large datasets.
You should get the name before insertion. In your case, if something went wrong then how would you know about it so you get the name before insert query.
Not sure but you try this:
declare int version;
if exists(SELECT name from table where name=?)
then
version = SELECT MAX(version) FROM table WHERE name = ?
version += 1
else
version = 1
end
Regards.
This is actually a bad plan, you might be changing what the user's specified data. That is likely to not be what is desired, maybe they're not trying to create a new version but just unaware that the one wanted already exists. But, you can create a function, which your java calls, not only inserts the requested version or max+1 if the requested version already exists. Moreover it returns the actual values inserted.
-- create table
create table nv( name text
, version integer
, constraint nv_uk unique (name, version)
);
-- function to create version or 1+max if requested exists
create or replace function new_version
( name_in text
, version_in integer
)
returns record
language plpgsql strict
as $$
declare
violated_constraint text;
return_name_version record;
begin
insert into nv(name,version)
values (name_in,version_in)
returning (name, version) into return_name_version;
return return_name_version;
exception
when unique_violation
then
GET STACKED DIAGNOSTICS violated_constraint = CONSTRAINT_NAME;
if violated_constraint like '%nv\_uk%'
then
insert into nv(name,version)
select name_in, 1+max(version)
from nv
where name = name_in
group by name_in
returning (name, version) into return_name_version;
return return_name_version;
end if;
end;
$$;
-- create some data
insert into nv(name,version)
select 'n1', gn
from generate_series( 1,3) gn ;
-- test insert existing
select new_version('n2',1);
select new_version('n1',1);
select *
from nv
order by name, version;

Can you check if a column exists and perform different actions with oracle?

My table looks like the following:
id | value1 | count
I have a list of value1 in RAM and I want to do the following:
(if value1 exists in table){
count + 1}else{
insert new row into table}
Is this possible with Oracle or do I have to take it to the code, do a for loop and execute one element of the list at a time? The list contains 5 million values. I'd have to do something like this in the code:
for(int i=0; i<list.size; i++){
boolean exists = checkifexists(list.get(i));
if(exists=true){
countPlusOne(list.get(i);
}else{
createNewRow(list.get(i));
}
}
So I have to do at least two queries for each value, totalling 10m+ queries. This could take a long time and may not be the most efficient way to do this. I'm trying to think of another way.
"I load them into RAM from the database"
You already have the source data in the database so you should do the processing in the database. Instantiating a list of 5 million strings in local memory is not a cheap operation, especially when it's unnecessary.
Oracle supports a MERGE capability which we can use to test whether a record exists in the target table and populate a new row conditionally. Being a set operation MERGE is way more performative than single row inserts in a Java loop.
The tricky bit is uniqueness. You need to have a driving query from the source table which contains unique values (otherwise MERGE will hurl). In this example I aggregate a count of each occurrence of value1 in the source table. This gives us a set of value1 plus a figure we can use to maintain the count column on the target table.
merge into you_target_table tt
using ( select value1
, count(*) as dup_cnt
from your_source_table
group by value1
) st
on ( st.value1 = tt.value1 )
when not matched then
insert (id, value1, cnt)
values (someseq.nextval, st.value1, st.dup_cnt)
when matched then
update
set tt.cnt = tt.cnt + st.dup_cnt;
(I'm assuming the ID column of the target table is populated by a sequence; amend that as you require).
In Oracle, we could use a MERGE statement to check if a row exists and do insertion only if it doesn't.
First create a type that defines your list.
CREATE OR REPLACE TYPE value1_type as TABLE OF VARCHAR2(10); --use the datatype of value1
Merge statement.
MERGE INTO yourtable t
USING (
select distinct column_value as value1 FROM TABLE(value1_type(v1,v2,v3))
)s ON ( s.value1 = t.value1 )
WHEN NOT MATCHED THEN INSERT
(col1,col2,col3) VALUES ( s.col1,s.col2,s.col3);
You may also use NOT EXISTS.
INSERT INTO yourtable t
select * FROM
(
select distinct column_value as value1 from TABLE(value1_type(v1,v2,v3))
) s
WHERE NOT EXISTS
(
select 1 from
yourtable t where t.value1 = s.value1
);
You can do this by two approaches
Approach 1:
Create a temp table in database and insert all your value in RAM into that Temp Table
Write query for updating count on the basis of you main table and temp table join and
set a flag in temp table which values are updated, the value which are not updated
use insert query to insert.
Approach 2:
You can create your own data type, which accepts array of values as input:
CREATE OR REPLACE TYPE MyType AS VARRAY(200) OF VARCHAR2(50);
You can write procedure with your logic,procedure will take value of array as input: CREATE OR REPLACE PROCEDURE testing (t_in MyType)
First fill your RAM list in a temporary table TMP
select * from tmp;
VALUE1
----------
V00000001
V00000002
V00000003
V00000004
V00000005
...
You may use a MERGE statement to handle your logik
if key existe increase the count by 1
if key doesn't exists insert it with the initial count of 1
.
merge into val
using tmp
on (val.value1 = tmp.value1)
when matched then update
set val.count = val.count + 1
when not matched then
insert (val.value1, val.count)
values (tmp.value1, 1)
;
Note that I assume you have IDENTITY key in the column ID, so no key assignment is requeired.
In case there are duplicated record in the TMP table (more records with the same VALUE1 key) you get error as MERGEcan not hanlde more actions with one key.
ORA-30926: unable to get a stable set of rows in the source tables
If you want to count each duplicated key as one -
you must pre-aggregate the temporary table using GROUP BY and add the counts.
Otherwise simple ignore the duplicates using DISTINCT.
merge /*+ PARALLEL(5) */ into val
using (select value1, count(*) count from tmp group by value1) tmp
on (val.value1 = tmp.value1)
when matched then update
set val.count = val.count + 1
when not matched then
insert (val.value1, val.count)
values (tmp.value1, 1)

Is it possible to get partitioned data using SQL?

I have a RDBMS table with a column BIGINT type and values are not sequential. I have a java program where I want each thread to get data as per PARTITION_SIZE i.e. I want a pair of column values like after doing ORDER BY on result,
Column_Value at Row 0 , Column_Value at Row `PARTITION_SIZE`
Column_Value at Row `PARTITION_SIZE+1` , Column_Value at Row `2*PARTITION_SIZE`
Column_Value at Row `2*PARTITION_SIZE+1` , Column_Value at Row `3*PARTITION_SIZE`
Eventually, I will pass above value ranges in a SELECT query's BETWEEN clause to get divided data for each thread.
Currently, I am able to do this partitioning via Java by putting all values in a List ( after getting all values from DB ) and then getting values at those specific indices - {0,PARTITION_SIZE},{PARTITION_SIZE+1,2*PARTITION_SIZE} ..etc but problem there is that List might have millions of records and is not advisable to store in memory.
So I was wondering if its possible to write such a query using SQL itself which would return me those ranges like below?
row-1 -> minId , maxId
row-2 -> minId , maxId
....
Database is DB2.
For example,
For table column values 1,2,12,3,4,5,20,30,7,9,11 ,result of SQL query for a partition size =2 should be {1,2},{3,4} ,{5,7},{9,11},{12,20},{30} .
In my eyes the mod() function would solve your problem and you could choose a dynamic number of partitions with it.
WITH numbered_rows_temp as (
SELECT rownumber() over () as rownum,
col1,
...
coln
FROM table
ORDER BY col1)
SELECT * FROM numbered_rows_temp
WHERE mod(rownum, <numberofpartitions>) = 0
Fill in the appropriate and change the result from 0 to - 1 in your queries.
Michael Tiefenbacher's answer is probably more useful, as it avoids an extra query, but if you do want to determine ID ranges, this might work for you:
WITH parms(partition_size) AS (VALUES 1000) -- or whatever
SELECT
MIN(id), MAX(id),
INT(rn / parms.partition_size) partition_num
FROM (
SELECT id, ROW_NUMBER() OVER (ORDER BY id) rn
FROM yourtable
) t , parms
GROUP BY INT(rn / parms.partition_size)

Performing Increment and Decrement with PreparedStatement [duplicate]

This question already has answers here:
how to increment integer Columns value by 1 in SQL
(8 answers)
Closed 7 years ago.
I have a column in my database which just contains a counter value. The value should be 1 if the entry is new, otherwise it will either be incremented or decremented if the entry is being updated.
I am trying to figure out how to use the java.sql.PreparedStatement for this purpose.
It is to my understanding that the SQL query which eventually has to be created has to look like:
INSERT INTO `DropletNames` (`Title`,`Count`) VALUES ('Travel to Mars',Count + 1)
My question is: how do I create this query? (fyi: I know the Count + 1 is sort of irrelevant here, since INSERT should always set it to 1, but I am trying to use the same syntax for INSERT as I would for UPDATE)
I tried doing the following:
String query = "INSERT INTO `DropletNames` (`Title`,`Count`) VALUES (?,?)";
PreparedStatement stm = con.prepareStatement(query);
stm.setString(1,"Travel to Mars");
stm.setString(2,"Count + 1"); // I used this because I couldn't find a better way of setting this
However, that obvious produces the wrong result, because it attempts to insert "Count + 1" as a String, instead of evaluating Count + 1.
The only other way I can think of doing it is:
String query = "INSERT INTO `DropletNames` (`Title`,`Count`) VALUES (?,Count + 1)";
PreparedStatement stm = con.prepareStatement(query);
stm.setString(1,"Travel to Mars");
But I am unsure if that violates the "best practices" rules for using the PreparedStatement's setter methods when building the SQL Statement. I cannot see how that would leave me open to SQL injection or other vulnerabilities, but maybe there is something I haven't considered.
Is there a way to do this using the PreparedStatement class, or is my second solution the way I should do this?
If you want to increment a column value then the syntax is
UPDATE `DropletNames` SET `Monday` = `Monday` + 1 WHERE `Title = "Travel to Mars"
That will just add 1 to whatever value is in the Monday column. Similarly if you want to decrement the value it will be
UPDATE `DropletNames` SET `Monday` = `Monday` - 1 WHERE `Title = "Travel to Mars"
Now the only thing you need to change to make this into a prepared statement is the placeholder for the WHERE value.
After reading your comments, you appear to want to increment a value that counts a value per day. Try the following example:
--Create Schema
CREATE TABLE #day(
[ID] int,
[DayOfWeek] NVARCHAR(16),
[Value] INT
)
--Add Days
INSERT INTO #day ([ID], [DayOfWeek], [Value]) VALUES
(1,'Sunday',0),
(2,'Monday',0),
(3,'Tuesday',0),
(4,'Wednesday',0),
(5,'Thursday',0),
(6,'Friday',0),
(7,'Saturday',0)
--Increment Day Example
UPDATE #day
SET [Value] += 1
WHERE [DayOfWeek] = 'Sunday'
--Increment Day Based on the current day
UPDATE #day
SET [Value] += 1
WHERE [ID] = DAY(GETDATE())
--Display Table
SELECT * FROM #day
OUTPUT:
ID DayOfWeek Value
1 Sunday 1
2 Monday 1
3 Tuesday 0
4 Wednesday 0
5 Thursday 0
6 Friday 0
7 Saturday 0

Getting the totalworkingdays dates through passing the dates dynamically

This is my MySQL database table structure and attendancedate datatype is Date:
attendancedate-------admissionnumber------------attendance
2013-10-03-----------LSTM-0008/2013-2014--------present
2013-10-09-----------LSTM-0008/2013-2014--------present
2013-11-02-----------LSTM-0008/2013-2014--------absent
and i want to disaply like that
monthname---------totalworkingdays---------present----absent
october-------------- 2--------------------2----------0
November--------------1--------------------0-----------1
so am writing the below MySQL query:
select monthname(attendencedate) as monthname,
(select distinct count(*)
from lstms_attendence
where attendencedate>='2013-10-03'
and attendencedate<='2013-10-09'
and addmissionno='LSTM-0008/2013-2014')as totalworkingdays,
sum(CASE WHEN attendence = 'present' THEN 1 ELSE 0 END) as present,
SUM(CASE WHEN attendence = 'absent' THEN 1 ELSE 0 END) AS absent
FROM lstms_attendence
WHERE addmissionno='LSTM-0008/2013-2014' GROUP BY monthname(attendencedate);
But the query is display like this:
monthname---------totalworkingdays---------present----absent
November-----------3-----------------------0------------2
October------------3-----------------------2------------1
please give me the exact query and here am passing dates as hardcoded but that dates are passing dynamically through java to that query.
Let me know the how to create dynamically dates and how to pass values to that query.
Try this:
SELECT
MONTHNAME(attendencedate) as the_month,
COUNT(*) as working_days,
SUM(IF(attendance='present',1,0)) as is_present,
SUM(IF(attendance='absent',1,0)) as is_absent
FROM
lstms_attendence
WHERE
addmissionno='LSTM-0008/2013-2014'
GROUP BY
the_month
Your question doesn't make clear what you're asking. What's wrong with your resultset? Is it the presence of your "november" row?
At any rate, you've got an excessively complex query here, as well as one which will break if you happen to include data from multiple years in it.
Instead of using MONTHNAME(attendancedate), we'll use
DATE_FORMAT(attendancedate, '%Y-%c-01') AS attendancemonth
to figure out in which month an attendancedate lies. This simply turns '2013-11-15' into '2013-11-01', the first day of the month in question. In other words, it truncates (rounds down) the date to the first day of the month.
SELECT admissionnumber,
DATE_FORMAT(attendancedate, '%Y-%c-01') AS attendancemonth,
COUNT(*) AS working_days,
SUM(IF(attendance='present',1,0)) AS is_present,
SUM(IF(attendance='absent',1,0)) AS is_absent
FROM lstms_attendence
WHERE attendancedate >= '2013-10-03'
AND attendancedate < '2013-10-06' + INTERVAL 1 DAY
AND admissionnumber='LSTM-0008/2013-2014'
GROUP BY admissionnumber,attendancemonth
Here is a sqlfiddle.http://sqlfiddle.com/#!2/a2dfb/7/0
This summarizes by both admissionnumber and month, so you can produce a report that contains multiple admissions if you want that.
It uses attendancedate < 'last' + INTERVAL 1 DAY in place of attendancedate <= 'last' because that is robust in cases where attendancedate contains a timestamp other than midnight.
If you need to pass in your parameters from Java, use a prepared statement. Replace the constants in the SQL query with ?, and use setTimestamp and setString. For one example, see JDBC Prepared Statement . setDate(....) doesn't save the time, just the date.. How can I save the time as well?

Categories