MyBatis insert all with generated ID - java

I'm trying to make an insert all method inside my mapper.
The problem is with the the selectKey inside the foreach (it seems I cannot use it).
If I call, from the outside, a nextVal method it returns always the same number.
<select id="nextValKey" resultType="java.lang.Long">
SELECT MY_SEQUENCE.nextVal from dual
</select>
<insert id="insertAll" parameterType="list">
INSERT ALL
<foreach collection="items" item="item" index="i">
<![CDATA[
into MY_TABLE (ID)
values (
#{item.id, jdbcType=DECIMAL}
]]>
</foreach>
SELECT * FROM dual
</insert>

If I understand correctly you generate ids for items via call to nextValKey.
The problem is that mybatis used cached value if you invoke the same select statement for the second time in the same session.
If you have query that returns different values each time you can instruct mybatis to clear the cache after statement execution (by default this is off for select and is on for insert, update and delete):
<select id="nextValKey" resultType="java.lang.Long" flushCache="true">
SELECT MY_SEQUENCE.nextVal from dual
</select>

I don't know Java nor MyBatis.
However, why would you want to use INSERT ALL in this case? It is usually used when you want to insert rows into different tables, using the same INSERT statement.
In your case, though - as far as I understand it - all you do is (pseudocode)
insert into my_table (id) a_sequence_of_numbers
If that's so, and as that "sequence_of_numbers" gets its value from my_sequence, then just do it as
insert into my_table (id)
select my_sequence.nextval
from dual
connect by level <= 10; -- it would insert 10 numbers
[EDIT: how to insert bunch of values]
You'd do it as simple as that:
SQL> create table my_table (id number);
Table created.
SQL> set timing on
SQL>
SQL> insert into my_table
2 select level from dual
3 connect by level <= 10000;
10000 rows created.
Elapsed: 00:00:00.02
SQL>
Or, if you insist on a sequence you created,
SQL> insert into my_table
2 select seqa.nextval from dual
3 connect by level <= 10000;
10000 rows created.
Elapsed: 00:00:00.08
SQL>

Related

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)

SQL for return all the id which does not exist in db and passing in sql

I need some help writing an SQL statement for the below requirement.
I have list of employee_id which I need to check whether they are exist in the DB or not from the java layer I want to use one query for this.
Sample query:
SELECT *
FROM employee
WHERE employee_id IN (1001,1002,1002,10000004).
In this query 10000004 does not exist in DB.
One approach in my mind is to use the below query:
SELECT Count(employee_id)
FROM employee
WHERE employee_id IN (1001,1002,1002,10000004).
Then check the list size and the result from the query in java layer. But I don’t want this because I need all those employee_id which does not exist in DB.
declare #employeeids varchar(1000) --to store ids as comma seperated string
declare #tmpEmployee table (employee_id varchar(50)) --temp employee table to store ids from string
declare #pointer int
select #employeeids = '1001,1002,1002,10000004' --list of ids to check against database
while (charindex(',', #employeeids, 0) > 0)
begin
set #pointer = charindex(',', #employeeids, 0)
insert into #tmpEmployee (employee_id)
--remove white spaces if exists
select ltrim(rtrim(substring(#employeeids, 0, #pointer)))
set #employeeids = stuff(#employeeids, 1, #pointer, '')
end
insert into #tmpEmployee (employee_id)
select ltrim(rtrim(#employeeids))
select r.employee_id -- required ids which does not exists in database
,e.employee_id
from #tmpEmployee r
left join employee e on r.employee_id=e.employee_id
where e.employee_id is null
If you are using Oracle, then this link may help you. It's all about usage of built in SYS.DBMS_DEBUG_VC2COLL function
Exist a very bad way to do that is this:
SELECT * FROM (SELECT 5930 id UNION SELECT 8109 id
UNION SELECT 8110 id UNION SELECT 8115 id UNION SELECT 8112 id
UNION SELECT 8113 id UNION SELECT -1 id) b
WHERE b.id NOT IN (SELECT f.id FROM employee f)
I recommed you do that in other way.

Static list MINUS select statement

I have a java program that returns a list of Long values (hundreds).
I would like to subtract to this list the values obtained from a select on an oracle database,
something like this:
SELECT 23 as num FROM DUAL UNION ALL
SELECT 17 as num FROM DUAL UNION ALL
SELECT 19 as num FROM DUAL UNION ALL
SELECT 67 as num FROM DUAL UNION ALL...
...
...
SELECT 68 as num FROM DUAL MINUS
SELECT NUM FROM MYTABLE
I presume that this operation has some performance issues...
Are there other better approaches?
Thank you
Case 1:
Use Global Temporary Tables (GTT):
CREATE GLOBAL TEMPORARY TABLE my_temp_table (
column1 NUMBER
) ON COMMIT DELETE ROWS;
Insert the List (Long value) into my_temp_table:
INSERT ALL
INTO my_temp_table (column1) VALUES (27)
INTO my_temp_table (column1) VALUES (32)
INTO my_temp_table (column1) VALUES (25)
.
.
.
SELECT 1 FROM DUAL;
Then:
SELECT * FROM my_temp_table
WHERE column1 NOT IN (SELECT NUM FROM MYTABLE);
Let me know if you have any issue.
Case 2:
Use TYPE table:
CREATE TYPE number_tab IS TABLE OF number;
SELECT column_value AS num
FROM TABLE (number_tab(1,2,3,4,5,6)) temp_table
WHERE NOT EXIST (SELECT 1 FROM MYTABLE WHERE MYTABLE.NUM = temp_table.num);
Assuming MyTable is much bigger than literal values, I think the best option is using a temporary table to store your values. This way your query is a lot cleaner.
If you are working in a concurrent environment (e.g. typical web app), use an id field, and delete when finished. Summarizing:
preliminary: create a table for temporary values TEMPTABLE(id, value)
for each transaction
get new unique/atomic id (new oracle sequence value, for example)
for each literal value: insert into temptable(new_id, value)
select * from temptable where id = new_id minus...
process result
delete from temp_table where id = new_id
Temporary tables are a good solution in oracle. This one can be used with an ORM persistence layer

Java PreparedStatement can't identify placeholders in subquery

When using a Java PreparedStatement, the question-mark placeholders aren't being detected. It would throw an error "The column index is out of range: 1, number of columns: 0" when invoking statementName.setLong(1, 123). My example is from Postgres 8.4, but the problem occurs before the SQL has a chance to make it to the SQL server.
After comparing against some working prepared statements, I realized that the broken one contained a subquery similar to:
SELECT * FROM (
SELECT DISTINCT (name)
id,
name
FROM MyTable
WHERE id > ?
ORDER BY name) AS Level1
ORDER BY 1
The solution that worked for me was to convert the query to a CTE (Common Table Expression). The revised query looks like this:
WITH Level1 AS (
SELECT DISTINCT (name)
id,
name
FROM MyTable
WHERE id > ?
ORDER BY name)
SELECT *
FROM Level1
ORDER BY 1
In JDBC, the parameter indexes for prepared statements begin at 1 instead of 0.

SQL with rank and partition

I need to execute this sql:
select * from
(select nt.*,
rank() over (partition by feld0 order by feld1 desc) as ranking
from (select bla from test) nt)
where ranking < 3
order by 1,2
This sql works fine in my oracle database but in the h2 database which i use sometimes this doesnt work because rank and partition are not defined.
So i need to transform this sql so that it works in h2 and oracle.
I want to use java to execute this sql. So is it possible to split this sql into different sqls without rank and partition? And then to handle it with java?
If feld1 is unique within feld0 partitions, you could:
select *
, (
select count(*)
from YourTable yt2
where yt2.feld0 = yt1.feld0 -- Same partition
and yt2.feld1 <= yt1.feld1 -- Lower or equal rank
) as ranking
from YourTable yt1

Categories