Make one query out of two others? - java

I have never been very good at SQL queries (I've always preferred NoSQL databases, but I need to use a SQL database this time).
I need to research how many seats are sold compared to the amount of total seats in cinema halls. At the moment I can accomplish this using 2 different queries in Hibernate, one for the total amount of seats and the other for the amount of sold seats, and then divising these results.
This is my code :
Double amountOfSoldTickets = ((Long) session.createQuery("select count(*) from Ticket t where t.vertoning.zaal.cinemacomplex.id = :id").
setInteger("id", complex.getId()).list().get(0)).doubleValue();
Double capacity= ((Long) session.createQuery("select sum(z.capaciteit) from Zaal z").list().get(0)).doubleValue();
return amountOfSoldTickets / capacity;
I thought about using a subquery though I have no idea how to do this in hibernate.
If anyone has any idea how to solve this in one query it'd be greatly appreciated.
Some additional info: I let hibernate implicitly join my tables in the first query.
Ticket has a many to one to Vertoning, Vertoning has a many to one to Zaal, Zaal has a many to one to cinemacomplex

I encourage you to learn SQL -- and to learn it on a real database instead of through the HQL interface. HQL is useful for what it does, but it misses on some very important SQL functionality.
I believe the following will work in HQL:
select count(*) as SoldSeats,
(select sum(z.capaciteit) from Zaal z) as Capacity
from Ticket t
where t.vertoning.zaal.cinemacomplex.id = :id;
In just MySQL, you could put these as subqueries in the from clause:
select t.SoldSeats, z.Capacity
from (select count(*) as SoldSeats,
from Ticket t
where t.vertoning.zaal.cinemacomplex.id = :id
) t
(select sum(z.capaciteit) as Capacity
from Zaal z
) z;
Note that if this is inside a loop where you are assigning different values to id, then the whole loop can possibly replaced with SQL.

I suggest you run this query:
Long[] amountOfSoldTicketsToCapacity =
((Long[]) session.createQuery(
"select count(*), z.capaciteit, z.id " +
"from Ticket t " +
"inner join t.vertoning v " +
"inner join v.zaal z " +
"inner join z.cinemacomplex c " +
"where c.id = :id " +
"group by z.capaciteit, z.id "
)
.setInteger("id", complex.getId())
.uniqueResult());
if {
throw new IllegalArgumentException("There are no tickets sold for Zaal#" + complex.getId());
}
double capacity = (amountOfSoldTicketsToCapacity.length != 0) ?
((double) amountOfSoldTicketsToCapacity[0]) / amountOfSoldTicketsToCapacity[1] : 0D;
Doing the division in Java is simpler, as in SQL you'd probably have to CAST one operand to NUMERIC.

Related

Average customer spending at each month and number of ordered items at each month

I have 4 tables:
CUSTOMER (idCustomer,name,phone,email, markdown)
ORDER (idOrder,day,Customer_idCustomer)
ORDERITEM (idOrderItem, quantity, Order_idOrder, Product_idProduct)
PRODUCT (idProduct,name,price,description).
What would be the query for:
Average customer spending (for each customer) in individual months
Number of ordered items?
I already wrote some queries on this database.
Since I do it for Java application with JDBC, maybe I can split these two queries and make calculation of average separately but what is the best approach? Is there a way how to do it just with two SQL queries?
Thank you for help.
Query I use for price reduction:
String sql = "SELECT c.markdown, SUM(oi.quantity * p.price)
FROM orderitem oi, `order` o, product p, customer c
WHERE c.idCustomer = o.Customer_idCustomer
AND oi.Order_idOrder=o.idOrder
AND oi.Product_idProduct=p.idProduct
AND o.idOrder=" + idOrderValue + "
GROUP BY oi.Order_idOrder";
Look here for database design (ERD)
You want an aggregation query with JOINs. Use proper JOIN syntax! Do not use commas in the FROM clause:
SELECT year(o.day) as yyyy,
month(o.day) as mm,
SUM(oi.quantity * p.price) / COUNT(DISTINCT c.idCustomer) as avg_customer_spending,
SUM(oi.quantity) as num_items
FROM customers c JOIN
orders o
ON c.idCustomer = o.Customer_idCustomer JOIN
orderitem oi
ON oi.Order_idOrder = o.idOrder JOIN
product p
ON oi.Product_idProduct = p.idProduct
GROUP BY year(o.day), month(o.day);

How to achieve first max record in mysql with Spring boot jpa 1.11

I need the max first record in the application,
I'm working with Spring boot JPA and MYSQL query I have tried are,
#Query("select d from Table where d.StatusType=:StatusType and d.tableNumber< :cuurentTableNumber ORDER BY d.tableNumber DESC LIMIT 1",nativeQuery = true)
Draw findTop1ByOrderByDrawNumberDesc(#Param("StatusType") Table.StatusType StatusType, #Param("tableNumber")int cuurentTableNumber);
#Query("select d from Table d where d.StatusType=:StatusTypeand d.tableNumber<:cuurentTableNumber")
Draw findTop1ByOrderByDrawNumberDesc(#Param("StatusType") Table.StatusType StatusType, #Param("tableNumber")int cuurentTableNumber , Pageable pageable);
And other trials too,
but nothing is working many times it gave me an error of multiple record found. And I need only one record which is Max but less than current number.
LIMIT does not work with JPA, so cant use that one.
Does anybody has any idea how to achieve this.
I achieved it by doing following,
#Query("select d from Table d where " +
"d.tableNumber <> :tableNumber " +
"and (" +
"(d.statusType = :statusType) " +
") ORDER BY d.TableNumber desc")
Iterable<Table> findPreviousNumber(#Param("tableNumber") int tableNumber,
#Param("statusType") statusType closeStatusType);
Still will look into it why this is not working with findFirst/Top

How to query for number of records in select with "group by" clause in JPA/EclipseLink?

Suppose you have a following JPA query:
select car.year, car.month, count(car) from Car car group by car.year, car.month
Before we query for results, we need to know how many records this query will return (for pagination, UI and so on). In other words we need something like this:
select count(*) from
(select car.year, car.month, count(car)
from Car car group by car.year)
But JPA/EclipseLink does not support subqueries in "from" clause. It there a way around it?
(Of course you can use plain SQL and native queries, but this is not an option for us)
A portable JPA solution:
select count(*) from Car c where c.id in
(select MIN(car.id) from Car car group by car.year, car.month)
You could also go with something like:
select COUNT(DISTINCT CONCAT(car.year, "#", car.month)) from car
but I expect this to be less performant due to operations with textual values.
What about:
select count(distinct car.year) from car
I have another approach to solve this issue . by using my approach you don't need to know the no of rows this query is going to return.
here it is your solution :-
you going to need two variables
1) pageNo (your page no should be 1 for first request to data base and proceeding request it should be incremental like 2 ,3 , 4 5 ).
2) pageSize.
int start = 0;
if(pageNo!=null && pageSize!=null){
start = (pageNo-1) * pageSize;
}else{
pageSize = StaticVariable.MAX_PAGE_SIZE; // default page size if page no and page size are missing
}
your query
em.createquery("your query ")
.setfirstResult(start)
.setMaxResult(pageSize)
.getResultList();
As #chris pointed out EclipseLink supports subqueries. But the subquery can't be the first one in the from-clause.
So I came up with the following workaround which is working:
select count(1) from Dual dual,
(select car.year, car.month, count(car)
from Car car group by car.year) data
count(1) is important as count(data) would not work
You have to add an entity Dual (If your database does not have a DUAL table, create one with just one record.)
This works but I still consider it a workaround that would only work if you allowed to create the DUAL table.
Simply you can use setFirstResult and setMaxResult to set record bound for query ,also use size of list to return count of records that query runs. like this :
Query query = em.createQuery("SELECT d.name, COUNT(t) FROM Department d JOIN
d.teachers t GROUP BY d.name");
//query.setFirstResult(5);
//query.setMaxResult(15); this will return 10 (from 5 to 15) record after query executed.
List<Object[]> results = query.getResultList();
for (int i = 0; i < results.size(); i++) {
Object[] arr = results.get(i);
for (int j = 0; j < arr.length; j++) {
System.out.print(arr[j] + " ");
}
System.out.println();
}
-----Updated Section------
JPA does not support sub-selects in the FROM clause but EclipseLink 2.4 current milestones builds does have this support.
See, http://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Basic_JPA_Development/Querying/JPQL#Sub-selects_in_FROM_clause
You can probably rewrite the query with just normal joins though.
Maybe,
Select a, size(a.bs) from A a
or
Select a, count(b) from A a join a.bs b group by a
I hope this helps you.

Count Duplicate Results in Oracle Database

I am working on a database project using both Java and SQL with an Oracle Database. I am new at working with databases and new at SQL. My question is: how would I be able to get the customer count and record each of their purchase history on a pivot table? For example, on the following table, I have Lee a total of 3 times, and purchased Item A, Item B, and Item C. Ann is there 3 times as well and purchased Item D, Item E, and Item F. I want to put their name, the number of occurrences and what they purchased on separate pivot table.
Row Customer Purchase_History
1 Lee Item A
2 Lee Item B
3 Lee Item C
4 Ann Item D
5 Ann Item E
6 Ann Item F
I've written some code attempting to do this, but when I compile and run, it will not give me the desired results. Here is my code:
String TableCount = "SELECT J.Row, J.Customer, J.Purchase_History, C.cnt" +
" FROM Table J INNER JOIN(SELECT Customer, count(Customer) as cnt" +
"FROM Table GROUP BY Customer") C ON J.Customer = C.Customer;
ResultSet rs = st.executeQuery(TableCount);
while(rs.next()){
st.executeUpdate("CREATE TABLE IF NOT EXISTS CUSTOMER_COUNT" +
"(TableCount , Purchase_History )");
String InsertIntoTable = String.format("INSERT INTO CUSTOMER_COUNT" +
"("TableCount","Purchase_History")" +
" VALUES ('%s','%s)");
}
What am I doing wrong here? Any help would be greatly appreciated!
Your query logic is fine. You should print out the query before running it. The problem would be these two lines (at least):
" FROM Table J INNER JOIN(SELECT Customer, count(Customer) as cnt" +
"FROM Table GROUP BY Customer") C ON J.Customer = C.Customer;
I assume you really mean:
" FROM Table J INNER JOIN(SELECT Customer, count(Customer) as cnt" +
"FROM Table GROUP BY Customer) C ON J.Customer = C.Customer";
In any case, when you print this out, you'll have the expression cntFROM table. You need a space:
" FROM Table J INNER JOIN(SELECT Customer, count(Customer) as cnt" +
" FROM Table GROUP BY Customer) C ON J.Customer = C.Customer";
But, an easier way to write your query is to use analytic functions:
select J.Row, J.Customer, J.Purchase_History,
count(*) over (partition by customer) as cnt
from table j

Effective way of constructing MySQL query in Java

I am developing a portlet in which I access a database quite often. I have to specify the query in a way, that offers the possibility of filtering as a reaction on a user input. The parameters used for filtering are two at the moment, but this number can grow in the future.
At the moment, my construction works pretty well for all inputs, however, I dont think that I am doing it in a right/effective way, since I do not use prepared statement and just construct the query manually.
This is example of my code (serviceFilter is an arrayList and typeFlag is a String)
private String prepareQuery() {
String query = "SELECT * from messages ";
// check filters
if (!typeFlag.equals("ALL")) {
if (typeFlag.equals("XML")) {
query += "WHERE type='" + TYPE_XML + "'";
} else {
query += "WHERE type='" + TYPE_JAVA + "'";
}
}
// lets see if user specifies some service filtering
if (serviceFilter.size() > 0) {
if (!typeFlag.equals("ALL")) {
query += " AND (";
} else {
query += " WHERE (";
}
for (int i = 0; i < serviceFilter.size(); i++) {
if (i>0) {
query += " OR ";
}
String service = serviceFilter.get(i);
System.out.println("Filter: " + service);
query += "sender='" + service + "' OR receiver='" + service + "'";
}
query += ")";
}
query += " ORDER BY id DESC LIMIT " + String.valueOf(limit);
System.out.println(query);
return query;
}
First problem is, that this has no way to prevent SQL injection (which would not be such a big problem since all the inputs come from checkBoxes and scrollbars, so the user does not actually type anything). I am not sure how to use a prepared statement here, because the population of my arrayList can be quite long and changes for every query.
The query itself, due to this fact can get really long. Here is an example of a query just for two arguments (imagine this for 20 items):
SELECT * from messages WHERE (sender='GreenServiceESB#GreenListener' OR receiver='GreenServiceESB#GreenListener' OR sender='queue/DeadMessageQueue' OR receiver='queue/DeadMessageQueue') ORDER BY id DESC LIMIT 50
So basically, my question is: Is this an effective way of constructing my query (propably not, right)? What approach would you suggest?
PS: I am using JDBC to connect to db and execute the query, if it is important in any way...
Thanks for any tips!
If you want to use something like
create.selectFrom(BOOK)
.where(PUBLISHED_IN.equal(2011))
.orderBy(TITLE)
instead of
SELECT * FROM BOOK
WHERE PUBLISHED_IN = 2011
ORDER BY TITLE
you can look to http://www.jooq.org/. It will simplify your code and you can avoid things like "if (something) { sql += " WHERE ..." }". This is antipattern and should not be used when possible.
First of all, you hinted at one of your issues - not using a PreparedStatement. Taking user input and using it directly in a SQL statement opens a site to SQL injection attacks.
I think what you're wanting is this:
select * from (
select *, row_number over (order by id_desc) as rowNum
from messages
where sender in (?,?,?,?,?,?,?,?) --8, 16 or however many ?'s you'll need
or receiver in (?,?,?,?,?,?,?,?)
) results
where rowNum between (1 and ?)
order by rowNum
Now, you bind the parameters with whatever the user input is and if you have extra spots in you IN operator left over, you bind them with some value that can't (or likely won't) be in your table such as null or HiMom#2$#. If you need to support an arbitrary number of values, you run the query multiple times and sort the results in memory yourself.
As far as the row_number function, that may not work in MySQL (I'm not a MySQL guy) but it does have an equivalent (or it could be that limit may be parameterizable, I don't know.

Categories