Hi i want to achieve following
insert into t1 (c1,c2) values (v1,v2)
above transaction will create a unique identifier for the row created. lets say UIDT1
now i want to execute multiple inserts in another table like
insert into t2 (c1,c2,c3) values (UIDT1,v2,v3)
insert into t2 (c1,c2,c3) values (UIDT1,v2,v3)
insert into t2 (c1,c2,c3) values (UIDT1,v2,v3)
.
.
.
using Java, i can write all queries in a text file and read one by one and create all transactions but i wanted to know if there is any more efficient way of doing the same.
Need your inputs..
Note : I am using Spring JDBC
Purpose : Unit testing, creating User(t1) and UserDetails(t2) considering tests run with a fresh DB i am creating users and user details first and then using that user i will test other scenarios.
You can do somthing like this
String [] queries = {
"insert into t2 (c1,c2,c3) values (UIDT1,v2,v3)",
"insert into t2 (c1,c2,c3) values (UIDT1,v2,v3)",
"insert into t2 (c1,c2,c3) values (UIDT1,v2,v3)"
};
try{
connectionObject.setAutoCommit(false);
Statement statement = connectionObject.createStatement();
for (String query : queries) {
statement.addBatch(query);
}
statement.executeBatch();
connectionObject.commit();
}
catch(SQLException e){
e.printStackTrace();
}
finally{
//closing statements
}
Related
I am using jdbc to retrieve data from database. I have four different queries for that, however, the result of first query is used to get the data of second and fourth query. But, the resultset, gets updated as i run other queries. So is there any way that i can keep the resultset and add new results in it.
Here is my code:
class GetData{
String toDate;
String fromDate;
GetData(String d1,String d2) throws ClassNotFoundException, SQLException, ParseException, TransformerException, ParserConfigurationException {
toDate=d1;
fromDate=d2;
Connection connection= null;
ResultSet resultset= null;
String customerquery="SELECT o.ordernumber,o.orderdate,o.customernumber,c.customername,c.addressLine1,c.postalCode,c.city,c.country from orders o join customers c on o.customernumber=c.customernumber where orderdate between ? and ?";
String orderdetailquery="SELECT orderNumber,productCode,quantityOrdered,priceEach,orderLineNumber,(quantityOrdered * priceEach) as total FROM orderdetails where ordernumber=?";
String productsquery="SELECT productName,productLine,productVendor FROM products where productcode=?";
String employeequery="SELECT c.salesRepEmployeeNumber,e.firstname,e.lastname,o.officecode,o.city from customers c join employees e on c.salesRepEmployeeNumber = e.employeeNumber join offices o on e.officecode=o.officecode where c.customernumber=?";
Class.forName("com.mysql.cj.jdbc.Driver");
connection=DriverManager.getConnection("jdbc:mysql://localhost:3306/classicmodels","root","root");
if(d1!=null || d2!=null) {
PreparedStatement p1= connection.prepareStatement(customerquery);
p1.setString(1, toDate);
p1.setString(2,fromDate);
resultset= p1.executeQuery();
PreparedStatement p2= connection.prepareStatement(orderdetailquery);
while(resultset.next()) {
p2.setString(1, resultset.getString("orderNumber"));
}
resultset=p2.executeQuery();
PreparedStatement p3= connection.prepareStatement(productsquery);
while (resultset.next()) {
p3.setString(1, resultset.getString("productcode"));
}
resultset=p3.executeQuery();
PreparedStatement p4=connection.prepareStatement(employeequery);
while(resultset.next()) {
p4.setString(1, resultset.getString("customernumber"));
}
resultset=p4.executeQuery();
resultset.close();
connection.close();
}
}
I am trying to use the result of first query that contains the column customernumber to fetch the data. While, executing the code a error comes that customernumber column not found. So, how can I use the result of first query in other queries. Also, i am trying to get all the result of queries in one resultset as I am trying to create a xml out of it by using DOM.
You can put everything in a single query. using joins.
select o.ordernumber,o.orderdate
, o.customernumber,c.customername
, c.addressLine1,c.postalCode,c.city,c.country
, t1.productCode, t1.quantityOrdered, t1.priceEach, t1.orderLineNumber,(t1.quantityOrdered * t1. priceEach) as total
, t2.productName, t2.productLine, t2.productVendor
, c.salesRepEmployeeNumber,t4.firstname,t4.lastname,t3.officecode,t3.city
from orders o
join customers c on o.customernumber=c.customernumber
left join orderdetails t1 on t1.orderNumber = o.orderNumber
left join products t2 on t2.productCode = t1.productCode
left join offices t3 on t3.offiecode = c.customernumber
left join employees t4 on t3.officecode = t4.officecode
ResultSet is getting changed because you are using the same reference (object) every time you are executing the sql statement, hence it is overriding the old result, so if you want to deal with previously returned ResultSet, you can create new ResultSet instance to use, and also you can create a Bean class to set the elements and make it List type to keep on adding the results as per your logic.
Create a POJO to store required properties retrieved from different queries.
The resultset is getting updated because the same reference variable is being used to assign the resultset of subsequent queries.
If you want to use the same variable, you can follow like this -
create a POJO (which can be used to create a xml out of it by using DOM).
get result of first query in resultset.
populate relevant properties of POJO from this resultset.
re-use the resultset to store result of next queries.
When you need to use previous results, get them from the POJO to be used as parameters in subsequent queries.
We are using Datastax Cassandra java driver (version 3.x). There is a logged batch Select statement with 'IN' clause, having a high number of values. Due to which we are facing a serious issue of low performance. Following is the format of query visible while debugging the Java application:
SELECT COL1, COL2, ... FROM XXXX WHERE PARTITIONKEY IN () AND CLUSTERINGKEY IN();
Can anyone please share how such SELECT with more than one IN clause shall be handled when there is high number of values available to pass inside it.
Does Session#executeAsync can solve this issue.
Thanks.
Don't use IN query for partition key (For a limited number of fixed data you can use if performance is not an issue). It imposes a lot of work to Coordinator node. You can use IN for clustering key but make sure your list is not too large as well.
executeAsync is the best approach here. I am adding a code snippet here.
PreparedStatement getInfo = session.prepare("SELECT COL1, COL2, ... FROM XXXX WHERE PARTITIONKEY = ?");
List<ResultSetFuture> futures = new ArrayList<>();
for (Object key : list) {
ResultSetFuture future = session.executeAsync(getInfo(key));
futures.add(future);
}
for (ResultSetFuture future : futures) {
try {
ResultSet rs = future.getUninterruptibly();
Row rw = rs.one();
if (rw != null) {
// set DB info into list or DTO
}
} catch (Exception e) {
// print log
LOGGER.error("", e);
}
}
It's a sample code. Please read this link for more details:
Cassandra Query Patterns: Not using the “in” query for multiple partitions.
I have some tables in a database. They have some particular pattern. For example, consider I have table employee, then some other table with same pattern like:
table 1:employee
table 2:employee_X
table 3:employee_Y
I want to check if these tables contain data or not and if they do then I have to call some method for each table. I am using following code to retrieve.
DatabaseMetaData meta = con.getMetaData();
ResultSet res = meta.getTables(null, null, "My_Table_Name", new String[] {"TABLE"});
while (res.next()) {
if(rs.getStrin(3).equals(employee)){
//my code to write data of this table to a file
}
if(rs.getString(3).equals(employee_X)){
//my code to write data to the same file
}
if(rs.getString(3).equals(employee_Y)){
//code to write data to the same file from this table
}
}
The code is working fine, but how I can retrieve data from all these tables at once instead of using three checks. If any of these table contains data I want to write it to my file. How I can perform this operation in less lines of code and efficiently?
It would be great if anyone can suggest way to check each of these table either contain data or not in a single statement and then I can call my code to write data to file.
You can use UNION statement in your complex query. Please, check example:
SELECT id, name FROM employee WHERE id = ?
UNION
SELECT id, name FROM employee_x WHERE id = ?
UNION
...
Also you can use UNION ALL statement instead of UNION. The main difference that UNION returns unique result set without duplicates, UNION ALL allows duplicates. Please, check this link https://www.w3schools.com/sql/sql_union.asp for detailed explanation about union statement.
If you need create UNION query with custom filtered tables, please check example:
Set<String> requiredTables = new HashSet<>();
// fill set with required tables for result query
requiredTables.add("employee");
ResultSet res = meta.getTables(null, null, "My_Table_Name",
new String[] {"TABLE"});
List<String> existentTables = new LinkedList<>();
while(res.next()) {
if (requiredTables.contains(res.getString(3)) {
existentTables.add(res.getString(3));
}
}
String query = existentTables.stream().map(table -> String.format("SELECT * FROM %s", table)).collect(Collectors.joinning(" UNION "));
I have a stored procedure that calls another stored procedure. The inner stored procedure returns a result set. After using a CallableStatement to execute the calling stored procedure I am unable to get the result set returned by called stored procedure.
I tried both execute and executeQuery for execution of callable statement. When I execute the calling stored procedure from SQL Server I am getting proper results.
Calling procedure:-
ALTER PROC [User].[Get_Data]
(#UserID NVARCHAR(20))
AS
BEGIN
Select 'User Data'
Exec [Order].[Get_Order] #UserID
END
Called procedure:-
ALTER PROC [Order].[Get_Order]
(#UserID NVARCHAR(20))
AS
BEGIN
Select * from orders where userId=#UserID
END
Your outer stored procedure is returning two result sets:
The results from Select 'User Data'
The results from Exec [Order].[Get_Order] #UserID
You need to call .getMoreResults() in order to retrieve the second result set, e.g.,
CallableStatement cs = connection.prepareCall("{CALL Get_Data (?)}");
cs.setString(1, "gord");
ResultSet rs = cs.executeQuery();
System.out.println("[First result set]");
while (rs.next()) {
System.out.printf("(No column name): %s%n", rs.getString(1));
}
cs.getMoreResults();
rs = cs.getResultSet();
System.out.println();
System.out.println("[Second result set]");
while (rs.next()) {
System.out.printf("userId: %s, orderId: %s%n",
rs.getString("userId"), rs.getString("orderId"));
}
producing
[First result set]
(No column name): User Data
[Second result set]
userId: gord, orderId: order1
userId: gord, orderId: order2
(Tested using mssql-jdbc-6.2.1.jre8.jar connecting to SQL Server 2014.)
For more details, see
How to get *everything* back from a stored procedure using JDBC
You cannot select the results of a stored procedure directly within SQL Server itself. You need to first insert the result into a temp table as per example below.
Example use:
-- Create a tempoary table to store the results.
CREATE TABLE #UserOrderDetail
(
UserData NVARCHAR(50) -- Your columns here
)
-- Insert result into temp table.
-- Note that the columns returned from procedure has to match columns in your temp table.
INSERT INTO #UserOrderDetail
EXEC [Order].[Get_Order] #UserID
-- Select the results out of the temp table.
SELECT *
FROM #UserOrderDetail
If the intent is to simply return one or more result sets to a client application, you should ensure that the SET NOCOUNT ON statement is added to the top of your stored procedures, this will prevent SQL Server from sending the DONE_IN_PROC messages to the client for each statement in the stored procedure. Database libraries like ODBC, JDBC and OLEDB can get confused by the row counts returned by the various insert and update statements executed within a SQL Server stored procedures. Your original procedure will look as follow:
ALTER PROC [User].[Get_Data]
(
#UserID NVARCHAR(20)
)
AS
BEGIN
SET NOCOUNT ON
SELECT 'User Data'
EXEC [Order].[Get_Order] #UserID
END
The correct way to do this with JDBC
Getting this right with JDBC is quite hard. The accepted answer by Gord Thompson might work, but it doesn't follow the JDBC spec to the word, so there might be edge cases where it fails, e.g. when there are interleaved update counts (known or accidental), or exceptions / messages.
I've blogged about the correct approach in detail here. The Oracle version is even more tricky, in case you need it. So here it goes:
// If you're daring, use an infinite loop. But you never know...
fetchLoop:
for (int i = 0, updateCount = 0; i < 256; i++) {
// Use execute(), instead of executeQuery() to handle
// leading update counts or exceptions
boolean result = (i == 0)
? s.execute()
: s.getMoreResults();
// Warnings here
SQLWarning w = s.getWarnings();
for (int j = 0; j < 255 && w != null; j++) {
System.out.println("Warning : " + w.getMessage());
w = w.getNextWarning();
}
// Don't forget this
s.clearWarnings();
if (result)
try (ResultSet rs = s.getResultSet()) {
System.out.println("Result :");
while (rs.next())
System.out.println(" " + rs.getString(1));
}
else if ((updateCount = s.getUpdateCount()) != -1)
System.out.println("Update Count: " + updateCount);
else
break fetchLoop;
}
Using jOOQ
Note that in case you're using jOOQ, you could leverage code generation for your stored procedures and call the simplified API to do this in a few lines only:
GetDatap = new GetData();
p.setUserId("gord");
p.execute(configuration);
Results results = p.getResults();
for (Result<?> result : results)
for (Record record : result)
System.out.println(record);
Disclaimer: I work for the company behind jOOQ
This question already has answers here:
How to get the insert ID in JDBC?
(14 answers)
Closed 7 years ago.
Is there any way of insert a row in a table and get the new generated ID, in only one statement? I want to use JDBC, and the ID will be generated by a sequence or will be an autoincrement field.
Thanks for your help.
John Pollancre
using getGeneratedKeys():
resultSet = pstmt.getGeneratedKeys();
if (resultSet != null && resultSet.next()) {
lastId = resultSet.getInt(1);
}
You can use the RETURNING clause to get the value of any column you have updated or inserted into. It works with trigger (i-e: you get the values actually inserted after the execution of triggers). Consider:
SQL> CREATE TABLE a (ID NUMBER PRIMARY KEY);
Table created
SQL> CREATE SEQUENCE a_seq;
Sequence created
SQL> VARIABLE x NUMBER;
SQL> BEGIN
2 INSERT INTO a VALUES (a_seq.nextval) RETURNING ID INTO :x;
3 END;
4 /
PL/SQL procedure successfully completed
x
---------
1
SQL> /
PL/SQL procedure successfully completed
x
---------
2
Actually, I think nextval followed by currval does work. Here's a bit of code that simulates this behaviour with two threads, one that first does a nextval, then a currval, while a second thread does a nextval in between.
public void checkSequencePerSession() throws Exception {
final Object semaphore = new Object();
Runnable thread1 = new Runnable() {
public void run() {
try {
Connection con = getConnection();
Statement s = con.createStatement();
ResultSet r = s.executeQuery("SELECT SEQ_INV_BATCH_DWNLD.nextval AS val FROM DUAL ");
r.next();
System.out.println("Session1 nextval is: " + r.getLong("val"));
synchronized(semaphore){
semaphore.notify();
}
synchronized(semaphore){
semaphore.wait();
}
r = s.executeQuery("SELECT SEQ_INV_BATCH_DWNLD.currval AS val FROM DUAL ");
r.next();
System.out.println("Session1 currval is: " + r.getLong("val"));
con.commit();
} catch (Exception e) {
e.printStackTrace();
}
}
};
Runnable thread2 = new Runnable(){
public void run(){
try{
synchronized(semaphore){
semaphore.wait();
}
Connection con = getConnection();
Statement s = con.createStatement();
ResultSet r = s.executeQuery("SELECT SEQ_INV_BATCH_DWNLD.nextval AS val FROM DUAL ");
r.next();
System.out.println("Session2 nextval is: " + r.getLong("val"));
con.commit();
synchronized(semaphore){
semaphore.notify();
}
}catch(Exception e){
e.printStackTrace();
}
}
};
Thread t1 = new Thread(thread1);
Thread t2 = new Thread(thread2);
t1.start();
t2.start();
t1.join();
t2.join();
}
The result is as follows:
Session1 nextval is: 47
Session2 nextval is: 48
Session1 currval is: 47
I couldn't comment otherwise I would have added to Vinko Vrsalovic's post:
The id generated by a sequence can be obtained via
insert into table values (sequence.NextVal, otherval)
select sequence.CurrVal
ran in the same transaction as to get a consistent view.
Updating de sequence after getting a nextval from it is an autonomous transaction. Otherwise another session would get the same value from the sequence. So getting currval will not get the inserted id if anothers sesssion has selected from the sequence in between the insert and select.
Regards,
Rob
The value of the auto-generated ID is not known until after the INSERT is executed, because other statements could be executing concurrently and the RDBMS gets to decide how to schedule which one goes first.
Any function you call in an expression in the INSERT statement would have to be evaluated before the new row is inserted, and therefore it can't know what ID value is generated.
I can think of two options that are close to what you're asking:
Write a trigger that runs AFTER INSERT, so you have access to the generated ID key value.
Write a procedure to wrap the insert, so you can execute other code in the procedure and query the last generated ID.
However, I suspect what you're really asking is whether you can query for the last generated ID value by your current session even if other sessions are also inserting rows and generating their own ID values. You can be assured that every RDBMS that offers an auto-increment facility offers a way to query this value, and it tells you the last ID generated in your current session scope. This is not affected by inserts done in other sessions.
The id generated by a sequence can be obtained via
insert into table values (sequence.NextVal, otherval)
select sequence.CurrVal
ran in the same transaction as to get a consistent view.
I think you'll find this helpful:
I have a table with a
auto-incrementing id. From time to
time I want to insert rows to this
table, but want to be able to know
what the pk of the newly inserted row
is.
String query = "BEGIN INSERT INTO movement (doc_number) VALUES ('abc') RETURNING id INTO ?; END;";
OracleCallableStatement cs = (OracleCallableStatement) conn.prepareCall(query);
cs.registerOutParameter(1, OracleTypes.NUMBER );
cs.execute();
System.out.println(cs.getInt(1));
Source: Thread: Oracle / JDBC Error when Returning values from an Insert
I couldn't comment, otherwise I would have just added to dfa's post, but the following is an example of this functionality with straight JDBC.
http://www.ibm.com/developerworks/java/library/j-jdbcnew/
However, if you are using something such as Spring, they will mask a lot of the gory details for you. If that can be of any assistance, just good Spring Chapter 11, which is the JDBC details. Using it has saved me a lot of headaches.