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
Related
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>
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)
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.
I am new to MySQL and I wrote a simple query bellow:
CREATE Table tblFeedBack
(
`FeedBackID` INT AUTO_IncremeNT,
`UserID` INT,
`Inserted_TS` TIMESTAMP ,
`FeedBackValue` VARCHAR(100),
PRIMARY KEY (FeedBackID)
);
CREATE PROCEDURE tblFeedBack_InsertUpdate
(
IN U_ID INT,
IN FB_Value VARCHAR(50)
)
BEGIN
IF ((Select COUNT(*) From 'tblFeedback') < 3)
BEGIN
INSERT INTO 'tblFeedBack' (`UserID`,`FeedBackValue`)
VALUES (U_ID,FB_Value);
END
ELSE
BEGIN
DECLARE #MostRecentFID INT;
SELECT TOP 1 `FeedBackID` FROM tblFeedback
WHERE UID = U_ID
ORDER BY `Inserted_TS` DESC
INTO #MostRecentFID;
UPDATE tblfeedback
SET `FeedBackValue` = #FeedBackValue
WHERE `FeedBackID` = #MostRecentFID
END
END
I am getting this error:
Schema Creation Failed: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''tblFeedBack' (`UserID`,`FeedBackValue`) VALUES (1,'12')' at line 9
Can anyone please help me solve this one?
Thanks in advance.
MySQL does not support TOP you want to use LIMIT 1 instead as they are ordered.
Select `FeedBackID` FROM tblFeedback
WHERE UID = U_ID
ORDER BY `Inserted_TS` DESC
LIMIT 1
INTO #MostRecentFID;
Also, when you query a table using quotes it becomes case sensitive.
IF ((Select COUNT(*) From 'tblFeedback') < 3)
Should be
IF ((Select COUNT(*) From 'tblFeedBack') < 3)
Fix these 2 errors and it should work.
The table names are different in the insert query. One contains 'tblFeedback' and other contains 'tblFeedBack'
Also, please try to remove single quotes near table names in the query.
I have a list of serial numbers: 111111, 222222, AAAAAA, FFFFFF and I want to return a corresponding value or null from a table depending on whether or not the value exists.
Currently I loop through my list of serial numbers, query using the following statement:
"SELECT cnum FROM table WHERE serial_num = " + serialNumber[i];
and then use the value if one is returned.
I would prefer to do this is one query and get results similar to:
Row | cnum
------------
1 | 157
2 | 4F2
3 | null
4 | 93O
5 | null
6 | 9F3
Is there a query to do this or am I stuck with a loop?
It sounds as if you have some sort of Java Array or Collection of serial numbers, and perhaps you want to check to see if these numbers are found in the DB2 table, and you'd like to do the whole list all at once, rather than one at a time. Good thinking.
So you want to have a set of rows with which you can do a left join to the table, with null indicating that the corresponding serial was not in the table. Several answers have started to use this approach. But they are not returning your row number, and they are using SELECT UNION's which seems a round-about way to get what you want.
VALUES clause
Your FROM clause can be a "nested-table-expression"
which can be a (fullselect)
with a correlation-clause. The (fullselect) can, in turn, be a VALUES clause. So you could have something like this:
FROM (VALUES (1, '157'), (2, '4F2'), (3, '5MISSING'), (4, '93O'), ...
) as Lst (rw, sn)
You can then LEFT JOIN this to the table, and get a two-column result table like you asked for:
SELECT Lst.rn, t.serial_num
FROM (VALUES (1, '157'), (2, '4F2'), (3, '5MISSING'), (4, '93O'), ...
) as Lst (rw, sn)
LEFT JOIN sometable t ON t.serial_num = Lst.sn
With this method, you will probably need a loop to build your dynamic SQL statement string, using the values from your collection.
If it was embedded SQL, we might be able to reference a host array variable containing your serial numbers. But alas, in Java I am not sure how to manage using the list directly in SQL, without using some loop.
If you use only an "in" it is not going to return null for the missing value forcing you to do some coding in the application (probably the most efficient way).
If you wanted the database to do all the work (may or may not be ideal) then
you would have to trick db2 into returning your list regardless.
Something like this might work, faking the null values to be returned from sysdummy with the common table expression (with part):
with all_serials as (
select '111111' as serialNumber from sysibm.sysdummy1 union all ,
select '222222' as serialNumber from sysibm.sysdummy1 union all ,
select 'AAAAAA' as serialNumber from sysibm.sysdummy1 union all ,
select 'FFFFFF' as serialNumber from sysibm.sysdummy1
)
select
t1.serialNumber,
t2.serialNumber as serialNumberExists
from
all_serials as t1 left outer join
/* Make sure the grain of the_Table is at "serialNumber" */
the_table as t2 on t1.serialNumber = t2.serialNumber
You can use the SQL IN keyword. You'd need to dynamically generate the list, but basically it'd look like:
SELECT cnum FROM table WHERE serial_num in ('111111', '2222222', '3333333', 'AAAAAAA'...)
Try something like:
select t.cnum
from
(select '111111' serial_num from sysibm.sysdummy1 union all
select '222222' serial_num from sysibm.sysdummy1 union all
select 'AAAAAA' serial_num from sysibm.sysdummy1 union all
select 'FFFFFF' serial_num from sysibm.sysdummy1) v
left join table t on v.serial_num = t.serial_num
I'm not sure if I get you correctly, but this could help:
String query = "SELECT cnum FROM table WHERE ";
for(int i = 0; i < serialNumber.length; i++)
query += "serial_num='" + serialNumber[i] + "' OR ";
query += "serial_num IS NULL "
System.out.println(query);