Java SQL Operation Requiring Multiple Sub Operations - java

I have a table with 3 columns and approximately 11,000 rows. The first row is an auto-increment primary key, followed by two Integers. These integers are the primary key ID of another table, which has the information I require. (The information I'm looking for is first names).
My plan is to output the necessary information to a standard text file using a FileWriter with each row being represented by one line like this "user1, user2". Once all ~11,000 rows have been fully processed, I would like to print a success message, then terminate the program.
The problem is I don't know the best way to go about retrieving this information in bulk, without it skipping rows. I've also attempted to use a while loop, however have been gotten stuck in the loop, resulting in corruption. Here's my latest attempt, which is terminating before it completing:
//Code to get total number of rows in table
//
for(int i = 0; i < size; i++) {
statement1 = connect.createStatement();
rs1 = statement1.executeQuery("select * from table2 limit (i-1), 1");
while(rs1.next() {
int user1 = rs1.get("user1");
int user2 = rs1.get("user2");
statement2 = connect.createStatement();
StringBuilder builder = new StringBuilder();
rs2 = statement2.executeQuery("select * from table1 where id = user1");
while(rs2.next()) {
String name = rs2.get("name");
builder.append(name).append(", ");
}
rs3 = statement2.executeQuery("select * from table1 where id = user2");
while(rs3.next()) {
String name = rs2.get("name");
builder.append(name);
}
}
//Code here to write the row to file, ensure empty StringBuilder, and close necessary SQL
}
System.out.println("Successfully processed" + size + " rows!");
System.out.println("Terminating program");
System.exit(0);

Related

Navigation button is not working

I have read a similar post but I still cannot get what is the problem.
I created a table in ms access, named DOCTOR, there are columns: DoctorID(number), Name(text), PhoneNumber(number), Department(text) and Specialization(text)
I connect the database to java through UCanAccess, below is the code to get connection
import java.sql.*;
public class Doctor
{
public static Connection connection; //sharing the memory
public static Connection connect() throws ClassNotFoundException, SQLException
{
String db = "net.ucanaccess.jdbc.UcanaccessDriver";
Class.forName(db);
String url = "jdbc:ucanaccess://C:/Users/user.oemuser/Documents/Doctor.accdb";
connection = DriverManager.getConnection(url);
return connection;
}
}
In my GUI class, i have a method called getConnect to show the data from database to textfield
public void getConnect()
{
try
{
connection = Doctor.connect();
statement=connection.createStatement();
String sql = "SELECT * FROM DOCTOR";
results = statement.executeQuery(sql);
results.next();
id = results.getInt("DoctorID");
name = results.getString("DoctorName");
phone = results.getInt("PhoneNumber");
dept = results.getString("Department");
spec = results.getString("Specialization");
textField1.setText("" +id);
textField2.setText(name);
textField3.setText("" +nf3.format(phone));
textField4.setText(dept);
textField5.setText(spec);
}
catch(Exception ex)
{
ex.printStackTrace();
}
}
and below is the code for the button1 which is the next button.
if(evt.getSource() == button1)
{
try
{
connection = Doctor.connect();
connection.setAutoCommit(false);
statement=connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE);
String sql1 = "SELECT * FROM DOCTOR";
results = statement.executeQuery(sql1);
if(results.next())
{
textField1.setText("" +results.getInt("DoctorID"));
textField2.setText(results.getString("DoctorName"));
textField3.setText("" +nf3.format(results.getInt("PhoneNumber")));
textField4.setText(results.getString("Department"));
textField5.setText(results.getString("Specialization"));
}
else
{
results.previous();
JOptionPane.showMessageDialog(null, "No more records");
}
connection.commit();
}
catch(Exception ex){
ex.printStackTrace();
}
}
Obviously the best component to use here is a JTable if you want to query all records within a particular database table or at the very least place the result set into an ArrayList mind you database tables can hold millions+ of records so memory consumption may be a concern. Now, I'm not saying that your specific table holds that much data (that's a lot of Doctors) but other tables might.
You can of course do what you're doing and display one record at a time but then you should really be querying your database for the same, one specific record at a time. You do this by modifying your SQL SELECT statement with the addition of the WHERE clause statement and playing off the ID for each database table record, something like this:
String sql = "SELECT * FROM DOCTOR WHERE DoctorID = " + number + ";";
But then again we need to keep in mind that, if the schema for your DoctorID field is set as Auto Indexed which of course allows the database to automatically place a incrementing numerical ID value into this field, the Index may not necessarily be in a uniform sequential order such as:
1, 2, 3, 4, 5, 6, 7, 8, 9, 10,.......
instead it could possibly be in this order:
1, 3, 4, 5, 6, 9, 10, 11, 16, 17,....
This sort of thing happens in MS Access Tables where a table record has been deleted. You would think that the ID slot that is deleted would be available to the next record added to the table and would therefore hold that removed ID value but that is not the case. The Auto Index Increment (autonumber) simply continues to supply increasing incremental values. There are of course ways to fix this sequencing mismatch but they are never a good idea and should truly be avoided since doing so can really mess up table relationships and other things within the database. Bottom line, before experimenting with your database always make a Backup of that database first.
So, to utilize a WHERE clause to play against valid record ID's we need to do something like this with our forward and reverse navigation buttons:
Your Forward (Next) Navigation Button:
if(evt.getSource() == nextButton) {
try {
connection = Doctor.connect();
connection.setAutoCommit(false);
number++;
long max = 0, min = 0;
ResultSet results;
Statement statement=connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE);
// Get the minimum DoctorID value within the DOCTOR table.
String sql0 = "SELECT MIN(DoctorID) AS LowestID from DOCTOR";
results = statement.executeQuery(sql0);
while (results.next()){ min = results.getLong("LowestID"); }
// Get the maximum DoctorID value within the DOCTOR table.
sql0 = "SELECT MAX(DoctorID) AS HighestID from DOCTOR";
results = statement.executeQuery(sql0);
while (results.next()){ max = results.getLong("HighestID"); }
if (max <= 0) {
JOptionPane.showMessageDialog(null, "No records found in Doctor Table.");
return;
}
if (number > min) { previousButton.setEnabled(true); }
if (number > max) {
nextButton.setEnabled(false);
JOptionPane.showMessageDialog(null, "No more records");
number--;
}
results = null;
while (results == null) {
String sql1 = "SELECT * FROM DOCTOR WHERE DoctorID = " + number + ";";
results = statement.executeQuery(sql1);
long id = 0;
// Fill The GUI Form Fields....
while (results.next()){
//id = results.getLong("DoctorID");
textField1.setText("" +results.getInt("DoctorID"));
textField2.setText(results.getString("DoctorName"));
textField3.setText("" + results.getString("PhoneNumber"));
textField4.setText(results.getString("Department"));
textField5.setText(results.getString("Specialization"));
connection.commit();
return;
}
// ----------------------------------------------------------
if (id != number) { results = null; number++; }
}
}
catch(Exception ex){ ex.printStackTrace(); }
}
Your Reverse (Previous) Navigation Button:
if(evt.getSource() == previousButton) {
try {
connection = Doctor.connect();
connection.setAutoCommit(false);
number--;
long max = 0, min = 0;
ResultSet results;
Statement statement=connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE);
// Get the minimum DoctorID value within the DOCTOR table.
String sql0 = "SELECT MIN(DoctorID) AS LowestID from DOCTOR";
results = statement.executeQuery(sql0);
while (results.next()){ min = results.getLong("LowestID"); }
// --------------------------------------------------------------------------
// Get the maximum DoctorID value within the DOCTOR table.
sql0 = "SELECT MAX(DoctorID) AS HighestID from DOCTOR";
results = statement.executeQuery(sql0);
while (results.next()){ max = results.getLong("HighestID"); }
// --------------------------------------------------------------------------
if (max <= 0) {
JOptionPane.showMessageDialog(null, "No records found in Doctor Table.");
return;
}
if (number < min) {
previousButton.setEnabled(false);
JOptionPane.showMessageDialog(null, "No more records");
number++;
}
if (number < max) { nextButton.setEnabled(true); }
results = null;
while (results == null) {
String sql1 = "SELECT * FROM DOCTOR WHERE DoctorID = " + number + ";";
results = statement.executeQuery(sql1);
long id = 0;
// Fill The GUI Form Fields....
while (results.next()){
textField1.setText("" +results.getInt("DoctorID"));
textField2.setText(results.getString("DoctorName"));
textField3.setText("" + results.getString("PhoneNumber"));
textField4.setText(results.getString("Department"));
textField5.setText(results.getString("Specialization"));
connection.commit();
return;
}
// ----------------------------------------------------------
if (id != number) { results = null; number--; }
}
}
catch(Exception ex){ ex.printStackTrace(); }
}
Things To DO...
So as to remove duplicate code, create a method named
getMinID() that returns a Long Integer data type. Allow this method to accept two String Arguments (fieldName and
tableName). Work the above code section used to gather the minimum DoctorID value within the DOCTOR table into the new
**getMinID() method. Use this new method to replace the formentioned code for both the Forward (Next) and Revese (Previous)
buttons.
So as to remove duplicate code, create a method named
getMaxID() that returns a Long Integer data type. Allow this method to accept two String Arguments (fieldName and
tableName). Work the above code section used to gather the maximum DoctorID value within the DOCTOR table into the new
getMaxID() method. Use this new method to replace the formentioned code for both the Forward (Next) and Revese (Previous)
buttons.
So as to remove duplicate code, create a void method named
fillFormFields(). Allow this method to accept two arguments, one as Connection (*connection) and another as ResultSet
(results) . Work the above code section used to Fill The GUI
Form Fields into the new fillFormFields() method. Use this new
method to replace the formentioned code for both the Forward (Next)
and Revese (Previous) buttons.
Things To Read That Might Be Helpful:
The SQL WHERE clause statement and the SQL ORDER BY statement for sorting your result set.
Searching For Records

Select one random data that have a common characteristic from a sqlite database

I have a database in sqlite that it has questions on it. Each question has a subject, question, answer, and level column. At the moment, I can ask for a subject and it will print me in a textField the question; however, It always selects the same question. How can I choose a random question that belongs to the subject Math?
My code looks like this at the moment:
question = new TextField();
question.setPrefWidth(400.0);
question.setPrefHeight(70.0);
question.setStyle("-fx-font: 30 timesnewroman; -fx-base: #190707;-fx-text-fill: black;");
startTestButton = new Button("START");
startTestButton.addEventHandler(ActionEvent.ACTION, (e)-> {
counterQuestions = nQTextField.getText();
System.out.println("Number of questions: " + counterQuestions);
try
{
String sql = "select * from Questions where Subject = ?";
PreparedStatement pst = connectionQuestions.prepareStatement(sql);
pst.setString(1,SsubjectTestField.getText());
ResultSet rs = pst.executeQuery();
if(rs.next())
{
question.setText(rs.getString("Question"));
}
else
{
System.out.println("No data for this subject");
}
}
catch(Exception a)
{
System.out.println(a);
}
primaryStage.setScene(sceneMathTest);
});
startTestButton.setPrefWidth(200.0);
startTestButton.setPrefHeight(50.0);
startTestButton.setStyle("-fx-font: 30 timesnewroman; -fx-base: #2EFE64; -fx-text-fill: black;");
createTestButtonPane.add(startTestButton,5,6);
THe frase that I use is : "select * from Questions where Subject = ?", however the * means all of them but it is choosing always the same one.
Thank you so much in advance.
You only call rs.next() once so it will only select the first one from the result set. If the database always retrieves them in the same order (which it will), then you will always get the same one. (And, by the way, * here means all columns, not all rows; the rows you get are determined by the where clause.)
If you don't have a large number of data in the database, you can just select everything into a list and choose a random element of the list:
ResultSet rs = pst.executeQuery();
List<String> allQuestions = new ArrayList<>();
while(rs.next())
{
allQuestions.add(rs.getString("Question"));
}
if (allQuestions.isEmpty()) {
System.out.println("No data for this subject");
} else {
Random random = new Random();
question.setText(allQuestions.get(random.nextInt(allQuestions.size())));
}
If you have a very large number of items in the database, this is however a bad option, as you will load them all into memory. In that case you might want to use some SQL tricks to select a specific row from the database. You can get a count of all rows first using
SELECT COUNT(*) FROM Questions WHERE Subject = ?
Then choose a random number between 1 and the returned value and use the techniques in How to select the nth row in a SQL database table? to get that specific row.

How do I show changes in my SQL table when I combined the values using GROUP BY?

I have this database table named Store_Data and I show three columns in the JTable.
Here are the columns:
NUMBERS
AMOUNT
DATE
How do I show the other columns in the jtable? The other columns are missing
I managed to obtain combined values using this SQL command, "SELECT NUMBERS, SUM(AMOUNT) FROM Store_Data GROUP BY NUMBERS", and I managed show it in the JTable.
However, In the JTable I only see the column NUMBERS and another column showing all the sum of the AMOUNT values. I don't see the other columns in the Jtable.
Here is my code,
private JTable showRecords(Connection con)
{
try
{
column.clear();
data.clear();
_stmt = con.createStatement();
//String getColumn = "SELECT * FROM APP.NYPMTRIPLESTRAIGHT";
/*this is only a test*/
String test = "SELECT NUMBERS, SUM(AMOUNT) FROM Store_Data GROUP BY NUMBERS";
ResultSet rs = _stmt.executeQuery(test);
//this will collect the data from the database
ResultSetMetaData metaData = rs.getMetaData();
//this will count all the columns from
int column_count = metaData.getColumnCount();
for(int j = 1; j <= column_count; j++)
{
column.add(metaData.getColumnName(j));
}
while(rs.next())
{
Vector<Object> vector = new Vector<Object>();
for(int i = 1; i <= column_count; i++)
{
vector.add(rs.getObject(i));
}
data.add(vector);
}
_records = new JTable(data, column);
return _records;
} catch (SQLException ex)
{
JOptionPane.showMessageDialog(null, ex.getMessage());
}
return _records;
}
NOTE: I know it is wrong to use Vector. I am only using it for testing.
Your query sums all the values of AMOUNT and displays the sum using a Group by clause.
Group by will group similar values into one entity based on the functions used. ["sum" in your case].
You need to get the numbers and amount from your database simply
SELECT NUMBERS, AMOUNT FROM APP.NYPMTRIPLESTRAIGHT;
then display the resultset data in your JTable.
Try this:
SELECT
t1.NUMBERS,
CONCAT(YEAR(T1.DATE) , '-' , MONTH(t1.DATE) , '-' ,DAY(t1.DATE)) as DATE,
SUM(AMOUNT) AS AMOUNT
FROM table1 t1
GROUP BY 1, 2
And here you have the sqlfiddle

Java MySQL updating rows when deleting a row

I have a database with a AUTO_INCREMENTING id column and a name column. If I delete a row from the data I would like to update all of the other rows id so the sequence is right again, I am very lost please help me.
I tried this:
stmt = conn.createStatement();
rs = stmt.executeQuery("SELECT COUNT(id) FROM " + table_name_products);
int count = 0;
while(rs.next()){
count = rs.getInt("COUNT(id)");
}
stmt = conn.createStatement();
for(int i = 1; i <= count; i++){
if(i > id){
stmt.addBatch("UPDATE " + table_name_products + " SET id = "+ i-- +" WHERE id = "+ i +";");
}
}
I don't think this is possible.
I think the only solution would be to cache all rows after the one you are deleting some place, delete those rows, then add the cached rows to the table again.
There might be database management command to do this, but you wouldn't want to be doing it after every delete.
What about a database trigger. Have a look here.
A database trigger is procedural code that is automatically executed
in response to certain events on a particular table or view in a
database.

ResultSet not contain new inserted row

I am working with Oracle Database 10g, java and try to use ResultSet to insert new row.
I have object of ResultSet which is updateble and insensitive in direction meaning that you can traverse in any direction.
When I insert row in ResultSet using moveToInsertRow, insertRow, and setter method, the row is inserted in Database, but while traversing ResultSet, I am not able to view newly inserted row
Can any one help me.
My Code is :
import java.sql.*;
import java.io.*;
import java.util.Date;
public class TestResultSet{
public static void main(String...args){
try{
Connection con = DriverManager.getConnection("jdbc:oracle:thin:#//localhost:1521/xe", "system", "admin");
Statement stmt = con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE);
ResultSet rs = stmt.executeQuery("Select EnrNo, Name, Gender, DOB From Student");
int i;
while(rs.next()){
for(i = 1; i < 5; i++)
System.out.print(rs.getString(i) + ", ");
System.out.println();
}
//Inserting New Row
rs.moveToInsertRow();
rs.updateLong(1, 117020365276L);
rs.updateString(2, "Ashfaq");
rs.updateInt(3, 1);
Date d = new Date();
rs.updateDate(4, new java.sql.Date(d.getYear(), d.getMonth(), d.getDay()));
rs.insertRow();
//New Row Insertion ends here
rs.first(); //Moving to first row
do{
for(i = 1; i < 5; i++) //Index starts from 1, not from 0
System.out.print(rs.getString(i) + ", ");
System.out.println();
}while(rs.next());
}
catch(SQLException ex){ ex.printStackTrace(); }
catch(Exception ex){ ex.printStackTrace(); }
}
}
/*
Student Table Schema
EnrNo Numeric(12) Primary Key
Name varchar2(25);
Gender Numeric(1);
DOB date
*/
The problem is that the new row has been added to the database, not to the current rows recovered from database. So, in order to recognize the new row, you must do another read to your table(s). It could be inneficient (since it depends on how many rows and how complex your query is) but that's how it works.
Knowing this, your code should be like this:
ResultSet rs = stmt.executeQuery("Select EnrNo, Name, Gender, DOB From Student");
int i;
while(rs.next()) {
for(i = 1; i < 5; i++)
System.out.print(rs.getString(i) + ", ");
System.out.println();
}
//Inserting New Row
rs.moveToInsertRow();
rs.updateLong(1, 117020365276L);
rs.updateString(2, "Ashfaq");
rs.updateInt(3, 1);
Date d = new Date();
rs.updateDate(4, new java.sql.Date(d.getYear(), d.getMonth(), d.getDay()));
rs.insertRow();
//New Row Insertion ends here
//Moving to first row of the current recovered resultset
//thus not working as expected
//rs.first();
//close the resultset
rs.close();
//retrieve the rows from database again
rs = stmt.executeQuery("Select EnrNo, Name, Gender, DOB From Student");
do{
for(i = 1; i < 5; i++) //Index starts from 1, not from 0
System.out.print(rs.getString(i) + ", ");
System.out.println();
} while(rs.next());
I remember there were third party libraries that supported the functionality you are looking for, they are pretty expensive.
In general, yes, you will have to refetch. However, you can be smart about it: you can intelligently partition your query to fetch only a few hundred rows
e.g. by using two tables, one for production data, other for new
data, and sometimes merging them (you can use in-memory tables for the new data, and you can create cross-table views, if needed, although not neccessary)
you can create an autoincrement index, and fetch only the latest rows,
or you can use ROWNUM in Oracle, SELECT TOP in SQL Server, LIMIT in MySQL, etc.
and of course, you can implement a custom database driver :-)
In general, if you fetch more than a few hundred rows regularly, something is wrong, and maybe you should reconsider your client side interface and implementation.
A ResultSet that is TYPE_SCROLL_INSENSITIVE is not meant to detect updates to the underlying database (that is what the insensitive means here). If you want the ResultSet to detect changes, then you should use TYPE_SCROLL_SENSITIVE.
However as the changes usually occur in a different transaction, I believe most databases are unable to offer TYPE_SCROLL_SENSITIVE, and if they can then they probably only allow you to see changes to the data of the selected rows, but not detect additional (or removed) rows.
If you try to use TYPE_SCROLL_SENSITIVE, you might want to check if your database actually supports that type (eg using DatabaseMetaData.supportsResultSetType(int)). Or by checking if the created ResultSet is actually of the specified type (the JDBC spec allows drivers to 'downgrade' type and/or concurrency if it isn't supported).
You might also want to check DatabaseMetaData.ownInsertsAreVisible(int) and related methods for your specific database and driver.
Try this:
String query = "Select EnrNo, Name, Gender, DOB From Student";
try {
conn = DriverManager.getConnection(DB_URL, USER, PASS);
// change this to reflect your specific situation
stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(query);
while (rs.next()) {
String EnrNo = rs.getString("EnrNo");
String name = rs.getString("Name");
String gender = rs.getString("Gender");
String date = rs.getString("DOB");
System.out.println(EnrNo + "\t" + name + "\t" + gender + "\t" + date);
}
} catch (SQLException e) {
e.printStackTrace();
}
// make sure you add these as your class variables:
Connection conn = null;
Statement stmt = null;

Categories

Resources