Merging three queries that use 1:* relationships into one query - java

Lets say i have four tables i want to read from:
customer
customer_id, customer_name
1 Joe Bolggs
customer_orders
customer_id, order_no
----------------------------
1 1
1 2
1 3
customer_addresses
customer_id address
----------------------------
1 11 waterfall road
1 23 The broadway
customer_tel_no
customer_id number
----------------------------
1 523423423432
1 234342342343
The customer information shown above (for the customer with id=1) is to be stored in a Java object as shown below
public class Customer{
String customer_id;
String customerName;
ArrayList<String> customerOrders;
ArrayList<String> customerAddress;
ArrayList<String> customerTelephoneNumbers;
}
The only way i can think of to get the above information is by using three queries. The reason is that there is a 1:* relationship between the customer table and each of the other tables. To get the data i am doing something like this:
Customer customer = new Customer()
String customerSQL = "Select * from customer where customer_id = ?";
statement = getConnection().prepareStatement(contactsQuery);
statement.setString(1,1);
resultSet = statement.executeQuery();
while (resultSet.next()){
customer.customer_id = resultSet.get(1); //No getters/setters in this example
customer.customerName = resultSet.get(2);
}
String customerOrdersSQL = "Select * from customer_orders where customer_id = ?";
statement = getConnection().prepareStatement(customerOrdersSQL);
statement.setString(1,1);
resultSet = statement.executeQuery();
customer.customerOrders = new ArrayList();
while (resultSet.next()){
customer.customerOrders.add(resultSet.getString(2); // all the order numbers
}
String customerAddressesSQL = "Select * from customer_addresses where customer_id = ?";
statement = getConnection().prepareStatement(customerAddressesSQL );
statement.setString(1,1);
resultSet = statement.executeQuery();
customer.customerAddresses = new ArrayList();
while (resultSet.next()){
customer.customerAddresses.add(resultSet.getString(2); // all the addresses
}
String customerTelSQL = "Select * from customer_tel_no where customer_id = ?";
statement = getConnection().prepareStatement(customerTelSQL);
statement.setString(1,1);
resultSet = statement.executeQuery();
customer.customerTelephoneNumbers = new ArrayList();
while (resultSet.next()){
customer.customerTelephoneNumbers.add(resultSet.getString(2); // all the order numbers
}
The problem with the above is that i am making three calls to the database. Is there a way i can merge the above into a single query please?
I cant see how a join would work because for example, a join between customer and customer_orders will return a customer row for each row in customer_orders. Is there anyway i can merge the above three queries into one?

I would think that something like this would work:
SELECT c.customer_id, c.customer_name, o.order_no, a.address, t.number
FROM customer c LEFT JOIN customer_orders o ON c.customer_id = o.customer_id
LEFT JOIN customer_addresses a ON c.customer_id = a.customer_id
LEFT JOIN customer_tel_no t ON c.customer_id = t.customer_id
WHERE c.customer_id = ?
Then, in your code, after you execute the query:
while (resultSet.next())
{
customer.customerOrders.add(resultSet.getString(3));
customer.customerAddresses.add(resultSet.getString(4));
customer.customerTelephoneNumbers.add(resultSet.getString(5));
}
Of course, this does not take into account the fact that you will have null values along the way, so I'd advise checking for nulls to make sure that you aren't adding a lot of junk to your array lists. Still, that's probably a lot less costly than 3 separate queries.

Nothing prevents you from iterating and processing the joined result into your customer object. If your application is complex enough, you could look into ORM frameworks which would do that for you under the covers. If you are working with JavaEE, have a look at JPA.

use this query and reduce the number of call. And, in while loop process on data.
select customer.customer_id,customer.customer_name,order_no,address,number
from customer,customer_orders,customer_addresses,customer_tel_no
where customer.customer_id = customer_orders.customer_id
AND
customer.customer_id = customer_addresses.customer_id
AND
customer.customer_id = customer_tel_no.customer_id

Related

Desc fetch :n rows in java

I have a method that contains query like that
public List<Order> getOrders (int customer_id, int count)
{
String sql= "select * from order where customer_id = ? order by order_id DESC FETCH FIRST ? ROWS
ONLY"
}
What i want to do is to list orders according to given input(count).I get error like that
Missing IN or OUT parameter at index:: 2
If I remove the second ? and I write DESC FETCH FIRST 5 ROWS it executes succesfully.I think I could not use salary parameter in DESC FETCH statement.Do you have any idea?
There is indeed no limitation for DESC order in the order_by_clause and row_limiting_clause
you must somehow not set the second parameter ...
Here is working JDBC example with a marked line, that if commented out leads to your exception
def stmt = con.prepareStatement("""select order_id from getOrders
where customer_id = ?
order by order_id DESC
FETCH FIRST ? ROWS ONLY""")
stmt.setInt(1,1001) /* Bind customer_id = 1001 */
stmt.setInt(2,3) /* bind count of rows = 3; uncomment to get java.sql.SQLException: Missing IN or OUT parameter at index:: 2 */
def rs = stmt.executeQuery()
while(rs.next())
{
println "order_id= ${rs.getInt(1)}"
}

Adding distinct data to jComboBox from database

I have a jComboBox which i want to fill up with the departments of the students in a database. Now the same department occurs many times in the table so i want each department name to go only once to the list of items. The present code i wrote is not giving the desired result. It puts the same department name multiple times on the ComboBox list. How can i solve this?
My code to fetch department names is given below:
conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/mydaatabase1","root","Password123");
String sql1 = "select distinct (dept) from droptest";
PreparedStatement pss = conn.prepareStatement(sql1);
ResultSet rs = pss.executeQuery(sql1);
while(rs.next())
{
String d = rs.getString("dept");
jComboBox1.addItem(d);
}
I guess, you need use group by in select data...
select columnName
from tablename
Group by columnName
Select dept
From droptest
Group by dept
Group by is like distinct

Prepared statement with set null in query doesn't return any record

I use prepared statements to read/write data in my DB (SQLite). In my table INVENTORY, there are records which have null value in the column paleta (the column is defined as VARCHAR in the table). I want to select these records and I tried:
sq = "SELECT * FROM INVENTORY WHERE paleta = ? AND product = ? AND lot = ?";
//...
stm = c.prepareStatement(sq);
stm.setNull(1, java.sql.Types.VARCHAR);
stm.setString(2, "theIdOftheProduct");
stm.setString(3, "theLotOftheProduct");
ResultSet rs = stm.executeQuery();
The above query doesn't return anything.. I removed the paleta = ? and I get the records I want.. How can I define the query like SELECT * FROM INVENTORY WHERE paleta is null etc.. using the query parameters?
What you are trying to do is equivalent to writing SELECT * FROM INVENTORY WHERE paleta = NULL ..., which doesn't work.
Since you are essentially searching for rows having a constant value in the paleta column (which happens to be NULL), you can eliminate the first query parameter and explicitly check for null:
sq = "SELECT * FROM INVENTORY WHERE paleta IS NULL AND product = ? AND lot = ?";
stm = c.prepareStatement(sq);
stm.setString(1, "theIdOftheProduct");
stm.setString(2, "theLotOftheProduct");
I found my answer in https://stackoverflow.com/a/4215618/1052284
You'll have to decide upon an unused value. I simply kept it at '' since I don't have empty values.
sq = "SELECT * FROM INVENTORY WHERE IFNULL(paleta, '') = ? AND product = ? AND lot = ?";
//...
stm = c.prepareStatement(sq);
stm.setString(1, ""); // '' for NULL, otherwise a specific value
stm.setString(2, "theIdOftheProduct");
stm.setString(3, "theLotOftheProduct");
But beware if you many queries, it's VERY slow. I clock in at about 4000 times slower, on average, than queries without IFNULL. ~50ms instead of microseconds.

column is invalid in select list because it is not contained in an aggregate function or group by clause

I have a Java method that looks like this:
public ArrayList<Orders> GetOrders(Connection connection, int id) throws Exception
{
ArrayList<Orders> feedData = new ArrayList<Orders>();
ArrayList<Integer> itemsId = new ArrayList<Integer>();
try
{
PreparedStatement ps = connection.prepareStatement("SELECT o.id, o.userId FROM dbo.Orders o join dbo.Items I on o.itemId = I.id join dbo.Users u on o.UserId = u.Id where o.userId = ? group by o.id, o.userId");
ps.setInt(1, id);
ResultSet rs = ps.executeQuery();
while(rs.next())
{
Orders o = new Orders();
o.setId(rs.getInt("id"));
ps = connection.prepareStatement("SELECT o.ItemId, SUM(price) as price FROM dbo.Orders o join dbo.Items I on o.itemId = I.id WHERE o.UserId= ? GROUP BY o.id");
ps.setInt(1, id);
ResultSet rs2 = ps.executeQuery();
while(rs2.next()){
itemsId.add(rs2.getInt("ItemId"));
//total = total + rs2.getInt("price");
}
o.setItem(itemsId);
o.setUserId(rs.getInt("userId"));
o.setTotalAmount(rs2.getInt("total"));
feedData.add(o);
}
return feedData;
}
}
In the second prepared statement, where I have this query:
SELECT o.ItemId, SUM(price) as price FROM dbo.Orders o join dbo.Items
I on o.itemId = I.id WHERE o.UserId= ? GROUP BY o.id
I get above mentioned exception.
i.e
com.microsoft.sqlserver.jdbc.SQLServerException: Column 'dbo.Orders.ItemId' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
The problem is that you are selecting sum(price) as total in your second snippet, hence the result set column is named total. Since you're grouping by item id you get the sum of prices per item and need to do the total calculation yourself or use another query.
Try using total += rs2.getInt("total"); and o.setTotalAmount(total) after the while loop.
This appears to be the query you are referring to:
SELECT ItemId, price
FROM dbo.Orders o join
dbo.Items I
on o.itemId = I.id
WHERE o.UserId= ?
GROUP BY o.id, o.ItemId;
Your query does not work because price is not in the group by clause.
If you want the sum of prices by item, then I would think you want:
SELECT o.ItemId, SUM(price) as price
FROM dbo.Orders o join
dbo.Items I
on o.itemId = I.id
WHERE o.UserId= ?
GROUP BY o.ItemId;
You've ran into a fundamental requirement of SQL. Any time you have a a "group by" clause, SQL only permits the columns that are groupped on, or aggregates.
The reason is simple: you're groupping on id, so there will be a single group per value of id, and that translates into a single row in the result of select - per value of id. Now supppose there are multiple itemIdvalues for given id, say the rows with id = 5 have itemId values of 10, 20 and 30.
The SQL engine cannot put 3 different values into one row!
And that's why, when you do group by the select result can only include columns from group by and aggregates.
(Some databases relax this requirement a little, and it leads to really messy and hard to understand results)

How to fetch MySQL data without creating Object from Class in Java

I want to fetch data from MySQL without creating object from class
Normally I do something like
public ArrayList getInventoryByItemId(String ItemId) throws SQLException {
ArrayList list = new ArrayList<Inventory>();
String sql = "SELECT iid, i.uid, item_data, item_id, i.ctime, username, gender FROM Inventory i JOIN user u ON i.uid = u.uid WHERE item_id = '"+ItemId+"'";
Statement stmt = con.createStatement();
ResultSet rset = stmt.executeQuery(sql);
while (rset.next()) {
a = new Inventory(rset.getInt(1), rset.getInt(2), rset.getString(3), rset.getString(4), rset.getTimestamp(6), rset.getString(7), rset.getString(8));
list.add(a);
}
return list;
}
the problem is because Inventory object does not have user data from the joined user table, I cannot create new Inventory.
I just want to automatically make an object where it has all the data attributes, that I can access using the column name.
Thank You
If I got you problem,
You can create new map(HashMap I reccomend) and put values using column name or index as key.
So, your list will be list of maps.
while (rset.next()) {
a = new HashMap<Integer,Object>();
a.put(1,rset.getInt(1));
..........
list.add(a);
}
Or, If you know exact number of columns, you can user array instead of Map (it will be faster)
Based on what you are saying, I take it that your query is not returning data because the inventory does not have the user data that it is being joined with. You need to modify your query to use a left outer join.
String sql = "SELECT iid, i.uid, item_data, item_id, i.ctime, username, gender FROM Inventory i LEFT JOIN user u ON i.uid = u.uid WHERE item_id = '"+ItemId+"'"
This will allow your query to return Inventory data even if the corresponding User data does not exist.

Categories