How do you combine tuples with repeating attributes into one tuple? - java

I created a java class with the same name as a table from a SQL database, and using the retrieved information from that table I created multiple objects out of that Java class, and stored them inside An ArrayList.
CREATE TABLE `orderdetails` (
`orderNumber` int(11) NOT NULL,
`productCode` varchar(15) NOT NULL,
`quantityOrdered` int(11) NOT NULL,
`priceEach` decimal(10,2) NOT NULL,
`orderLineNumber` smallint(6) NOT NULL,
PRIMARY KEY (`orderNumber`,`productCode`),
KEY `productCode` (`productCode`),
CONSTRAINT `orderdetails_ibfk_1` FOREIGN KEY (`orderNumber`) REFERENCES `orders` (`orderNumber`),
CONSTRAINT `orderdetails_ibfk_2` FOREIGN KEY (`productCode`) REFERENCES `products` (`productCode`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
tuples have repeating orderNumbers, for example:
(10100,'S18_1749',30,'136.00',3),
(10100,'S18_2248',50,'55.09',2),
(10100,'S18_4409',22,'75.46',4),
Each tuple is then turned into a java object of the class with the same name as the table, and to be stored inside an Arraylist.
public ArrayList<OrderDetails> getOrders(String tableName) throws SQLException{
ArrayList<OrderDetails>od=new ArrayList<OrderDetails>();
try {
String query="SELECT * FROM "+tableName;
Statement s= con.createStatement();
ResultSet rs= s.executeQuery(query);
while(rs.next()) {
int orderNumber=rs.getInt("orderNumber");
String productCode=rs.getString("productCode");
int quantityOrdered=rs.getInt("quantityOrdered");
double priceEach=rs.getDouble("priceEach");
int orderLineNumber=rs.getInt("orderLineNumber");
OrderDetails temp=new OrderDetails(orderNumber, productCode, quantityOrdered, priceEach, orderLineNumber);
od.add(temp);
}
}
catch(SQLException e) {
System.out.println("SQL exception happened while retriving data, close connection");
throw new RuntimeException(e);
}
return od;
}
I am trying to Loop through the ArrayList and output the total amount of value (priceEach times quantityOrdered )for each checkNumber. But if I simply just loop through the ArrayList one object by object, that isn't going to work. As I will simply see the sum for each object, not each checkNumber, on the console.
ArrayList<OrderDetails>od= this.getOrders("orderdetails");
for(int i=0;i<=od.size();i++) {
System.out.println(od.get(i).getPriceEach()*od.get(i).getQuantityOrdered());
}
I'm expecting something like this to show up on console (no repeating checkNumber)
CheckNumber 10100, total value: 8494.62 (if you add up the product between priceEach and quantityOrdered for the three tuples shown above earlier in my question is the rsult)
In short is there a way for me to combine objects with repeating attributes into one?
I apologize if my question seems very vague and you don't know exactly what the problem is.
It would be greatly appreciated if you try to reach out for me and ask for further clarifications, sometimes it is very difficult to describe a problem that's too specific..

Why not simply do the computation in the database? SQL is a set-based language that is very efficient at this type of operation, compared to an iterative loop in Java.
Consider the following aggregate query:
select orderNumber, sum(quantityOrdered * priceEach) totalValue
from mytable
group by orderNumber
This gives you one record per order, with the total value over all corresponding rows.

Related

SQLite Database - RESET information

I'm currently making a code that uses a database. This is the class of the database:
public class ScriptDLL {
public static String getCreateTableCliente(){
StringBuilder sql = new StringBuilder();
sql.append(" CREATE TABLE IF NOT EXISTS CLIENTE (");
sql.append(" CODIGO INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,");
sql.append(" NOME VARCHAR (250) NOT NULL DEFAULT (''),");
sql.append(" ENDERECO VARCHAR (255) NOT NULL DEFAULT (''),");
sql.append(" EMAIL VARCHAR (200) NOT NULL DEFAULT (''),");
sql.append(" TELEFONE VARCHAR (20) NOT NULL DEFAULT ('') )");
return sql.toString();
}
}
Ok, so i want to make a SQLite code to set CODIGO back to 1. How could i write this code?
Thanks!
I would suggest to increment the version by 1 and the old table is dropped and new table is created, this will avoid failure due to uniqueness constraint if records already exist in the table(you take care of migration ofcourse). If you have specific need just to reset codigo then try the below code. It resets the value to 1 in the internal sqllite record SQLITE_SEQUENCE.
Refer documentation which explains more about autoincrement and SQLITE_SEQUENCE : https://sqlite.org/autoinc.html
public static String resetKey(){
return "UPDATE SQLITE_SEQUENCE SET seq = 1 WHERE name = CLIENTE";
}
If this is just a one-off then just Delete the App's data, or uninstall the App and rerun.
If you want to do this frequently (ignoring the fact that relying upon the column being specific values most likely indicates a flaw in the design) then:-
You could DROP and recreate the table as is often done in the onUpgrade method of the Database Helper. Doing this in the onUpgrade method could be problematic/complicated if you had multiple tables and potentially even more complicated if you had multiple versions.
You could have a specific method such as :-
public void resetCODIGO() {
this.getWritableDatabase.execSQL("DROP TABLE IF EXISTS CLIENTE;");
this.getWritableDatabase.execSQL(getCreateTableCliente());
}
DROPing a table will result in the respective row in the sqlite_sequence table being deleted by SQLite.
The sqlite_sequence table has a row per table that has a column with AUTOINCREMENT (only 1 allowed per table).
the sqlite_sequence table has two defined columns name for the table name and seq for the last inserted sequence number.
Another solution that would involve DROP and CREATE BUT does involve updating the sqlite_sequence table could be to delete all rows from the table and to then delete the respective row in the sqlite_sequence table.
Thus alternately you could have :-
public void resetCODIGO() {
this.getWritableDatabase.delete("CLIENTE");
String whereclause = "name=?";
String[] whereargs = new String[]{"CLIENTE"};
this.getWritableDatabase.delete(
"sqlite_sequence",
whereclause,
whereargs
);
}
Note the above code is in-principle code and hasn't necessarily been tested, so it may contain some errors.

SQLite Cursor returning the value for one column as the value for all columns

I am attempting to read from an SQLite database I have created containing information on UK universities.
The table has the following columns:
Institution
Rank_2017
Guardian_score100
Satisfied_with_course
Satisfied_with_teaching
Satisfied_with_feedback
Student_to_staff_ratio
Average_entry_tariff
Career_after_6_months
I have populated the table from a CSV file, and then attempted to call a getUni() method that will return the information in each of the columns, and use them to build a University object, however when I check the values of each member variable of University, the name and rank have been set appropriately, however every variable after that has also been set to the value of Rank_2017.
getUni() Method
public University getUni(String id)
{
SQLiteDatabase db = this.getReadableDatabase();
Cursor cursor = db.query(TABLE_UNIVERSITIES, COLUMNS, " Institution = ?",
new String[] { id }, null, null, null, null);
if(cursor != null){
cursor.moveToFirst();
}
System.out.println("###" + cursor.getCount() + "###");
University uni = new University();
if(cursor != null){
uni.setUni_name(cursor.getString(0));
uni.setRank(cursor.getString(1));
uni.setGuardianScore(cursor.getString(2));
uni.setCourseSatisfaction(cursor.getString(3));
uni.setTeachingSatisfaction(cursor.getString(4));
uni.setFeedbackSatisfaction(cursor.getString(5));
uni.setStudentStaffRatio(cursor.getString(6));
uni.setAverageEntryTariff(cursor.getString(7));
uni.setCareerAfterSixMonths(cursor.getString(8));
}
// return University
return uni;
}
After calling cursor.getColumnCount() i can see that it returns 10 columns as expected, however something isn't quite right.
For example, if I call University uni = getUni("Glasgow"); , it will assign both the University name, and rank_2017 correctly, however assign each member variable after that the same as rank_2017.
CSV row example:
Glasgow,26,70.8,89.3,91.6,67.3,14.5,478.4,78.5
Any help solving this issue would be hugely appreciated.
I suspect when you imported the CSV file, it didn't work correctly. Perhaps you didn't specify a separator, and SQLite created a table for you with only one column. Or it populated the table strangely, and your SQL adapter is "helping" in a surprising way. That could happen if for instance your CSV file had some whitespace.
I would use the SQLite shell to verify that the Glassgow row appears as expected in the database. That at least will let you distinguish between SQLite issues and Java library issues (if any).
HTH.

Android: column _id does not exist [duplicate]

I'm having trouble with something that works in the Notepad example.
Here's the code from the NotepadCodeLab/Notepadv1Solution:
String[] from = new String[] { NotesDbAdapter.KEY_TITLE };
int[] to = new int[] { R.id.text1 };
SimpleCursorAdapter notes = new SimpleCursorAdapter(this,
R.layout.notes_row, c, from, to);
This code seems to work fine. But just to be clear, I ran the ADB
utility and run SQLite 3. I inspected the schema as follows:
sqlite> .schema
CREATE TABLE android_metadata (locale TEXT);
CREATE TABLE notes (_id integer primary key autoincrement, title text
not null, body text not null);
All seems good to me.
Now on to my application, which, as far as I can see, is basically the same with
a few minor changes. I've simplified and simplified my code, but the
problem persists.
String[] from = new String[] { "x" };
int[] to = new int[] { R.id.x };
SimpleCursorAdapter adapter = null;
try
{
adapter = new SimpleCursorAdapter(this, R.layout.circle_row, cursor, from, to);
}
catch (RuntimeException e)
{
Log.e("Circle", e.toString(), e);
}
When I run my application, I get a RuntimeException and the following prints
in LogCat from my Log.e() statement:
LogCat Message:
java.lang.IllegalArgumentException: column '_id' does not exist
So, back to SQLite 3 to see what's different about my schema:
sqlite> .schema
CREATE TABLE android_metadata (locale TEXT);
CREATE TABLE circles (_id integer primary key autoincrement, sequence
integer, radius real, x real, y real);
I don't see how I'm missing the '_id'.
What have I done wrong?
One thing that's different between my application and the Notepad example is
that I started by creating my application from scratch using the
Eclipse wizard while the sample application comes already put together. Is
there some sort of environmental change I need to make for a new application
to use a SQLite database?
I see, the documentation for CursorAdapter states:
The Cursor must include a column named _id or this class will not
work.
The SimpleCursorAdapter is a derived class, so it appears this statement applies. However, the statement is technically wrong and somewhat misleading to a newbie. The result set for the cursor must contain _id, not the cursor itself.
I'm sure this is clear to a DBA because that sort of shorthand documentation is clear to them, but for those newbies, being incomplete in the statement causes confusion. Cursors are like iterators or pointers, they contain nothing but a mechanism for transversing the data, they contain no columns themselves.
The Loaders documentation contains an example where it can be seen that the _id is included in the projection parameter.
static final String[] CONTACTS_SUMMARY_PROJECTION = new String[] {
Contacts._ID,
Contacts.DISPLAY_NAME,
Contacts.CONTACT_STATUS,
Contacts.CONTACT_PRESENCE,
Contacts.PHOTO_ID,
Contacts.LOOKUP_KEY,
};
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
// ...
return new CursorLoader(getActivity(), baseUri,
CONTACTS_SUMMARY_PROJECTION, select, null,
Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC");
}
This has been answered and I would like to make it more comprehensive here.
SimpleCursorAdapter requires that the Cursor's result set must include a column named exactly "_id". Don't haste to change schema if you didn't define the "_id" column in your table.
SQLite automatically added an hidden column called "rowid" for every table. All you need to do is that just select rowid explicitly and alias it as '_id' Ex.
SQLiteDatabase db = mHelper.getReadableDatabase();
Cursor cur = db.rawQuery( "select rowid _id,* from your_table", null);
Tim Wu's code really works...
If you are using db.query, then it would be like this...
db.query(TABLE_USER, new String[] {
"rowid _id",
FIELD_USERNAME,
},
FIELD_USERNAME + "=" + name,
null,
null,
null,
null);
Yes , I also change the SELECT string query to fix this issue.
String query = "SELECT t.*,t.id as _id FROM table t ";
What solved my issue with this error was that I had not included the _id column in my DB query. Adding that solved my problem.
This probably isn't relevant anymore, but I just hit the same problem today. Turns out column names are case sensitive. I had an _ID column, but Android expects an _id column.
If you read the docs on sqlite, creating any column of type INTEGER PRIMARY KEY will internally alias the ROWID, so it isn't worth the trouble of adding an alias in every SELECT, deviating from any common utilities that might take advantage of something like an enum of columns defining the table.
http://www.sqlite.org/autoinc.html
It is also more straightforward to use this as the ROWID instead of the AUTOINCREMENT option which can cause _ID can deviate from the ROWID. By tying _ID to ROWID it means that the primary key is returned from insert/insertOrThrow; if you are writing a ContentProvider you can use this key in the returned Uri.
Another way of dealing with the lack of an _id column in the table is to write a subclass of CursorWrapper which adds an _id column if necessary.
This has the advantage of not requiring any changes to tables or queries.
I have written such a class, and if it's of any interest it can be found at https://github.com/cmgharris/WithIdCursorWrapper

Get Primary Key Column from ResultSet Java

I am trying to get Primary Key Column(s) of table from ResultSet. Following are the Steps
I followed:
1. SQL QUERY: Select id,subid,email,empname from Employee
2. Executed this from java.sql.Statement and got the Results in ResultSet.
Here is the Interesting Part.
3. ResultSetMetadata rsmd = rs.getMetadata();
Now, if i watch this variable "rsmd" , it is showing primary key flags for relevant column names but I am not able to access it or get it into any variable.
I need help regarding the same.
NOTE: I do not want to use DatabaseMetadata and its getPrimaryKeys() function as it will take an additonal hit into External Database. Also, the ResultSetMetadata object is already having the primary key Information which i just need to fetch.
Some thoughts about your question and also a solution (hopefully helpful):
It didn't occur to me in my life time experience working with result sets having primary key information in the results set meta data.
It seems to me even strange because in principal a result set is not limited to show rows organized in columns of only one table and it is not forced to show all columns of one table and even the columns might be no table columns.
For example we might issue a query like
select X.a, X.b, Y.n, Y.m, 'bla'||X.c||'blub'||Y.l from X, Y;
In this case we may have or may not have primary columns from one or from both tables or from none of them.
As you already know the standard ResultSetMetaData-Interface doesn't provide primary key information access. What you see in the debugger is an instance of a class which implements that interface.
There are several ways to deal with your task:
(Not my preferred way)
Cast to the specific implementing ResultSetMetaData-class and access
primary key information if its available. But be aware that not every
ResultSetMetaData implementation provides this information.
(A bit more architectural approach, also not proposed from my side, but needed
if we deal with an incomplete JDBC-driver)
Take advantage of the system tables of the different databases you use
but hiding it of course in an abstraction, for example a bridge pattern.
Depending on the grants you have its normally not a big deal
(including testing up to 4 person days work for the base architecture part and ca. 1 person day for
each database system you want to access)
Then you get any desired meta data information from there including about foreign key relations.
(What I do)
Just use java.sql.DatabaseMetaData-class
It provides among others your desired primary key information for every accessible table.
Here a code-snippet (in Java 7):
public static Set<String> getPrimaryKeyColumnsForTable(Connection connection, String tableName) throws SQLException {
try(ResultSet pkColumns= connection.getMetaData().getPrimaryKeys(null,null,tableName);) {
SortedSet<String> pkColumnSet = new TreeSet<>();
while(pkColumns.next()) {
String pkColumnName = pkColumns.getString("COLUMN_NAME");
Integer pkPosition = pkColumns.getInt("KEY_SEQ");
out.println(""+pkColumnName+" is the "+pkPosition+". column of the primary key of the table "+tableName);
pkColumnSet.add(pkColumnName);
}
return pkColumnSet;
}
I have an idea to check whether a Column in table is Primary key or not using ResultSet.
In MySql JDBC driver, if you take a closer look, the real implementation class of java.sql.ResultSetMetaData would be com.mysql.jdbc.ResultSetMetaData class. This class provides a protected method to get information about each field
protected Field getField(int columnIndex) throws SQLException {
This method can give you the Field instance for every column index. Using the Field instance, you can get to the properties of the Field. To check whether it is a primary key, you can invoke
Field.isPrimaryKey()
Use FQN of com.mysql.jdbc.ResultSetMetaData in your type cast like ((com.mysql.jdbc.ResultSetMetaData) rsmd).getField(i).isPrimaryKey(). This is because you cannot import two class files with the same name and use them across the file
Please read the documentation of Field from MySql JDBC API to learn more about it. Hope this helps!
with #Keerthivasan Approach here is the complete working code. only problem was that answer cannot use the method like that as it is protected method.
here is the working code.
ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
int count = resultSetMetaData.getColumnCount();
for (int x = 1; x <= count; x++) {
Method method = null;
try {
method = com.mysql.jdbc.ResultSetMetaData.class.getDeclaredMethod("getField", int.class);
method.setAccessible(true);
com.mysql.jdbc.Field field = (com.mysql.jdbc.Field) method.invoke(resultSetMetaData, x);
if (field.isPrimaryKey()) {
System.out.println("-----------PK---------------------");
} else {
System.out.println("+++++++++++++++NPK++++++++++++++++++");
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
// find primary keys
try {
ResultSet rs = conn.getMetaData().getPrimaryKeys(null, conn.getSchema(), table);
while (rs.next()) {
System.out.println(rs.getString("COLUMN_NAME") + ":" + rs.getString("KEY_SEQ"));
}
} catch (SQLException e) {
e.printStackTrace();
}
This will work for all RDBMS, not just MSSQL

How to process ResultSet you know has only one record in it

I'm struggling with a homework assignment and am getting hung up on some SQL queries.
My query is interrogating an inventory database for the quantity of some item. The query requests the column with the name quantity_in_stock from the table, given the primary key.
I have initialized some prepared statements. This is the one I'm using here:
stmtFindColumn = Database.getConnection().prepareStatement(String.format("select ? from %s where %s = ?",
INVENTORY_TABLE_NAME, SKU) );
Now a separate method is called. I pass it a static const QTY_IN_STOCK, which is defined as "quantity_in_stock" and the item's SKU number, which is the primary key in the table.
private int getIntegerFromTable(String column, String key) {
int toReturn = 0;
try {
// Complete the prepared statement
stmtFindColumn.setString(1, column);
stmtFindColumn.setString(2, key);
ResultSet result = stmtFindColumn.executeQuery();
toReturn = result.getInt(column);
} catch (SQLException e) {
LOG.error(e.getMessage());
e.printStackTrace();
}
return toReturn;
}
When I run the query I get an sql exception that tells me: Invalid column name quantity_in_stock.
I have tried using a while loop processing result.next() and get the same error. I can't find any examples of how to properly get the results when you know only a single record is being returned.
Help!
EDIT: OK, I've found that part of my problem is I'm not getting a result set, where I should expect one. Any ideas?
UPDATE: I've tested my code, using a garden variety statement and a plain string query instead and it works just fine. So the problem is in my use of the prepared statement. Can someone check if I'm using the ? wildcards correctly? Thanks!
as far as i know, the column name may not be a parameter ...
DarkSquirrel42 is right -- you can't replace the column list of the select using a ? parameter marker. Instead, you can String.format that into place too, for example.
bad:
*select ? from INVENTORY_TABLE_NAME where SKU = ?
good:
select QUANTITY_IN_STOCK from INVENTORY_TABLE_NAME where SKU = ?

Categories