I was wondering how I could set multiple parameters to different values, so I can get 4 different results from the same query. Can you do a loop? If yes, then how would you do it, and can you use the same executeQuery?
mapinfo = c.prepareStatement(
" SELECT TOP 1 M.ID, M.Name, COUNT(DISTINCT C.Name) 'Cities' , "
+ " COUNT(DISTINCT R2.IDfrom + R2.IDto) Roads, (SELECT AVG(R.Distance) 'Average' FROM ROAD R WHERE R.MapID = ?) Average, "
+ " MAX(R.Distance) 'Max Distance', C2.Name 'Start city', C1.Name 'End city' "
+ " FROM MAP M "
+ " LEFT JOIN ROAD R ON R.MapID = R.MapID "
+ " LEFT JOIN CITY C ON C.MapID = R.MapID "
+ " JOIN CITY C1 ON R.IDfrom = C1.ID "
+ " JOIN CITY C2 ON R.IDto = C2.ID "
+ " INNER JOIN ROAD R2 ON M.ID = R2.MapID "
+ " WHERE M.ID = ? AND C.MapID = ? AND R.MapID = ? AND R2.IDfrom < R2.IDto "
+ " GROUP BY M.ID, M.Name, R.MapID, C.MapID, C1.Name, C2.Name "
+ " ORDER BY 'Max Distance' DESC "
);
// Execute MapInfo and loop through the result
mapinfo.setInt(1, 1);
mapinfo.setInt(2, 1);
mapinfo.setInt(3, 1);
mapinfo.setInt(4, 1);
mapinfo.setInt(1, 2);
mapinfo.setInt(2, 2); //I wanna get a different result with setting the parameters to a differnt value
mapinfo.setInt(3, 2);
mapinfo.setInt(4, 2);
mapresult = mapinfo.executeQuery();
while (mapresult.next()) {
// retrieve result
int mapid = mapresult.getInt(1);
String mapname = mapresult.getString(2);
int numOfCities = mapresult.getInt(3);
int numOfRoads = mapresult.getInt(4);
int maxDistance = mapresult.getInt(5);
int average = mapresult.getInt(6);
String start = mapresult.getString(7);
String end = mapresult.getString(8);
// write output
Terminal.put("---------------------------------------\n" + "Map: " + mapname + " (" + mapid + ")\n"
+ "Cities: " + numOfCities + "\n" + "Roads: " + numOfRoads + "\n" + "Average Road Length: "
+ average + " km" + "\n" + "The longest road runs: " + maxDistance + " km" + "\n" + "Start: "
+ start + "\n" + "End: " + end + "\n" + "---------------------------------------");
}
mapresult.close();
mapinfo.setInt(2, 1);
mapinfo.setInt(2, 2);
This just overwrites. You don't wanna do this.
I was wondering how I could set multiple parameters to different values so I can get 4 different results from the same query
First build the SQL query that gives you what you want, then translate it to java. In this case, that means completely redesigning the query.
Alternatively, run this one query, but run it 4 times in a row. Yes, you can re-use a preparedstatement:
for (int i = 0; i < 4; i++) {
mapinfo.setInt(1, i + 1);
mapinfo.setInt(2, i + 1);
mapinfo.setInt(3, i + 1);
mapinfo.setInt(4, i + 1);
try (ResultSet rs = mapinfo.executeQuery()) {
while (rs.next()) {
// process a result row
}
}
}
Related
I'm trying to execute the following query:
String query = "select NUMERO_CHAUFFEUR, avg(DISTANCE) as DISTANCE " +
"from " +
"(select NUMERO_CHAUFFEUR, " +
"6387.7 * ACOS((sin(LATITUDE_DEPART / 57.29577951) * SIN(LATITUDE_ARRIVEE / 57.29577951)) + " +
"(COS(LATITUDE_DEPART / 57.29577951) * COS(LATITUDE_ARRIVEE / 57.29577951) * " +
"COS(LONGITUDE_ARRIVEE / 57.29577951 - LONGITUDE_DEPART / 57.29577951))) as DISTANCE " +
"from " +
"(select l.NUMERO_CHAUFFEUR, " +
"regexp_substr(d1.COORDONNEES, '^[^,]+') as LATITUDE_DEPART, " +
"trim(leading ',' FROM regexp_substr(d1.COORDONNEES, ',.*$')) AS LONGITUDE_DEPART, " +
"regexp_substr(d2.COORDONNEES, '^[^,]+') as LATITUDE_ARRIVEE, " +
"trim(leading ',' FROM regexp_substr(d2.COORDONNEES, ',.*$')) AS LONGITUDE_ARRIVEE " +
"from LIVRAISONS l " +
"inner join DEPOTS d1 on(l.NUMERO_DEPOT_DEPART = d1.NUMERO_DEPOT) " +
"inner join DEPOTS d2 on(l.NUMERO_DEPOT_ARRIVE = d2.NUMERO_DEPOT) " +
")) " +
"group by (NUMERO_CHAUFFEUR)";
But a java.sql.SQLException: ORA-01722: invalid number is being thrown. Does anyone know why? Because if I execute the query directly in sql using sqlplus it works fine.
Result of execution of the query using sqlplus:
NUMERO_CHAUFFEUR AVG(DISTANCE)
---------------- -------------
1 507.064894
2 703.326572
5 846.966137
4 511.914202
I've tried the following but the error still persists:
String query = "select NUMERO_CHAUFFEUR, avg(DISTANCE) as DISTANCE " +
"from " +
"(select NUMERO_CHAUFFEUR, " +
"to_number('6387.7') * ACOS((sin(LATITUDE_DEPART / to_number('57.29577951')) * SIN(LATITUDE_ARRIVEE / to_number('57.29577951'))) + " +
"(COS(LATITUDE_DEPART / to_number('57.29577951')) * COS(LATITUDE_ARRIVEE / to_number('57.29577951')) * " +
"COS(LONGITUDE_ARRIVEE / to_number('57.29577951') - LONGITUDE_DEPART / to_number('57.29577951')))) as DISTANCE " +
"from " +
"(select l.NUMERO_CHAUFFEUR, " +
"to_number(regexp_substr(d1.COORDONNEES, '^[^,]+')) as LATITUDE_DEPART, " +
"to_number(trim(leading ',' FROM to_number(regexp_substr(d1.COORDONNEES, ',.*$')))) AS LONGITUDE_DEPART, " +
"to_number(regexp_substr(d2.COORDONNEES, '^[^,]+')) as LATITUDE_ARRIVEE, " +
"to_number(trim(leading ',' FROM to_number(regexp_substr(d2.COORDONNEES, ',.*$')))) AS LONGITUDE_ARRIVEE " +
"from LIVRAISONS l " +
"inner join DEPOTS d1 on(l.NUMERO_DEPOT_DEPART = d1.NUMERO_DEPOT) " +
"inner join DEPOTS d2 on(l.NUMERO_DEPOT_ARRIVE = d2.NUMERO_DEPOT) " +
")) " +
"group by (NUMERO_CHAUFFEUR)";
An ORA-01722 error occurs when an attempt is made to convert a character string into a number, and the string cannot be converted into a number.
regexp_substr would return string and you called it as LATITUDE_DEPART, then you do mathematics with that value which is not correct. you should first convert it as number. this might help you:
TO_NUMBER(regexp_substr(d1.COORDONNEES, '^[^,]')) LATITUDE_DEPART
Do this for all same parts.
I've this CTE which I'd implement in Java:
It's running on an IBM Iseries 7.3 DB2 machine.
WITH params (from_date, to_date, outlet, product_number)
AS (
values(TO_DATE('01.11.2018', 'DD.MM.YYYY'),
TO_DATE('18.12.2018', 'DD.MM.YYYY'),
'BLK' ,
49 )
),
product
AS (
SELECT DISTINCT cpp.competitor_products_id product
FROM yxdb.competitor_product_prices cpp
INNER JOIN yxdb.competitor_products_comparisons cpc ON cpc.competitor_products_id = cpp.competitor_products_id
AND cpc.deleted = 0
INNER JOIN yxdb.outlets o ON o.outlets_id = cpc.outlets_id
AND o.deleted = 0
INNER JOIN params ON cpp.price_date > params.from_date
AND cpc.product_number = params.product_number
AND o.short_name = params.outlet
WHERE cpp.deleted = 0
)
select * from product;
It's a lot longer, so the params table is used several times.
When implementing it in Java, I replace the hardcoded dates and other parameters in Java as ?1, ?2 etc. I've also tried with named parameters, none works. They all give [SQL0418] Use of parameter marker or NULL not valid.
Java Code snippet:
#RepositoryRestResource
public interface CompetitorPriceDateRepository extends JpaRepository<CompetitorPriceDateEntity, Long> {
#Query(value = "WITH params (from_date, to_date, outlet, product_number) "
+ " AS ( "
+ " values(TO_DATE( :fromDate , 'DD.MM.YYYY'), "
+ " TO_DATE( :toDate , 'DD.MM.YYYY'), "
+ " :outlet , "
+ " :productNumber ) "
+ " ), "
+ " product "
+ " AS ( "
+ " SELECT DISTINCT cpp.competitor_products_id product "
+ " FROM yxdb.competitor_product_prices cpp "
+ " INNER JOIN yxdb.competitor_products_comparisons cpc ON +" cpc.competitor_products_id = cpp.competitor_products_id "
+ " AND cpc.deleted = 0 "
+ " INNER JOIN yxdb.outlets o ON o.outlets_id = cpc.outlets_id "
+ " AND o.deleted = 0 "
+ " INNER JOIN params ON cpp.price_date > params.from_date "
+ " AND cpc.product_number = params.product_number "
+ " AND o.short_name = params.outlet "
+ " WHERE cpp.deleted = 0 "
+ " ) "
+ " select * from product ",nativeQuery = true)
List<CompetitorPriceDateEntity> findAllInterpolatedByDates(
#Param("productNumber") Integer productNumber,
#Param("outlet") String outlet,
#Param("fromDate") String fromDate,
#Param("toDate") String toDate
);
Without the stack trace I dont' have any aidea what's wrong with your query. SQL seems solid.
Try with a named native query
#Entity
#NamedNativeQuery(
name = “competitor.findProducts.byDateOutletProductnumber",
query = "WITH params (from_date, to_date, outlet, product_number) "
+ " AS ( "
+ " values(TO_DATE( :fromDate , 'DD.MM.YYYY'), "
+ " TO_DATE( :toDate , 'DD.MM.YYYY'), "
+ " :outlet , "
+ " :productNumber ) "
+ " ), "
+ " product "
+ " AS ( "
+ " SELECT DISTINCT cpp.competitor_products_id product "
+ " FROM yxdb.competitor_product_prices cpp "
+ " INNER JOIN yxdb.competitor_products_comparisons cpc ON +" cpc.competitor_products_id = cpp.competitor_products_id "
+ " AND cpc.deleted = 0 "
+ " INNER JOIN yxdb.outlets o ON o.outlets_id = cpc.outlets_id "
+ " AND o.deleted = 0 "
+ " INNER JOIN params ON cpp.price_date > params.from_date "
+ " AND cpc.product_number = params.product_number "
+ " AND o.short_name = params.outlet "
+ " WHERE cpp.deleted = 0 "
+ " ) "
+ " select * from product ",
hints = {
#QueryHint(name = "org.hibernate.cacheable", value = "true") })
public class CompetitorPriceDateEntity { ... }
pro bonus 1: Named queries are sort of precompiled, so jpa doesn't need to compile the query into a executable criteria every time it gets called.
pro bonus 2: Query hint cacheable or eclipselink equivalent
I have got a webapp(JSP/Servlet) with Tomcat8 + SQL Server2012
JDBC Driver Type 4: JTDS old version 1.2.5 (http://jtds.sourceforge.net/)
I change this kind of query, adding Prepared Statement (server pagining)
Sting DDXsql = "SELECT '?' *, ( DDX_RECORD_COUNT / '?' + 1 ) AS DDX_PAGE_COUNT
FROM
( SELECT '?' *
FROM ( SELECT '?' *,
(SELECT COUNT(*) " + "FROM "
+ session.getAttribute("DatabaseName") + ".G1_grid "
+ sqlFrom
+ sqlWhere + " "
+ " ) AS DDX_RECORD_COUNT "
+ "FROM " + session.getAttribute("DatabaseName") + ".G1_grid "
+ sqlFrom
+ sqlWhere + " "
+ " ORDER BY '?' '?' , '?' '?' ) AS TMP1 ORDER
BY '?' '?', '?' '?') AS r ORDER BY '?' '?', '?' '?'";
Parameters:
String top1 = DBManager.getTOP(request, "TOP " + Integer.valueOf((String)ResourceManager.findData("pageSize", request)));
Integer pagesizeInt = Integer.valueOf((String)ResourceManager.findData("pageSize", request));
String top2 = DBManager.getTOP(request, "TOP " + Integer.valueOf((String)ResourceManager.findData("ddxrecordcount", request)));
String top3= DBManager.getTOP(request, "TOP " + Integer.valueOf((String)ResourceManager.findData("toRange", request)));
String notSortStr = (String)ResourceManager.findData("notSort", request);
Object[] values = new Object[] {
top1,
pagesizeInt,
top2,
top3,
SortKey,
Sort,
TotalSortKey,
Sort,
SortKey,
notSortStr,
TotalSortKey ,
notSortStr,
SortKey,
Sort,
TotalSortKey,
Sort
};
Before, I didint use PreparedStatement I have this kind of query (replace "?" with the Object array values, without StringEscapeUtils):
String DDXsql = "SELECT " +
DBManager.getTOP(request, "TOP "
+ Integer.valueOf(StringEscapeUtils.escapeSql((String)ResourceManager.findData("pageSize", request)))) + " *,
( DDX_RECORD_COUNT / " + Integer.valueOf(StringEscapeUtils.escapeSql((String)ResourceManager.findData("pageSize", request))) + " + 1 ) AS DDX_PAGE_COUNT FROM
( SELECT "
+ DBManager.getTOP(request, "TOP "
+ Integer.valueOf(StringEscapeUtils.escapeSql((String)ResourceManager.findData("ddxrecordcount", request))))
+ " * FROM ( SELECT " + DBManager.getTOP(request, "TOP " + Integer.valueOf(StringEscapeUtils.escapeSql((String)ResourceManager.findData("toRange", request))))
+ " *, (SELECT COUNT(*) "
+ "FROM " + session.getAttribute("DatabaseName") + ".G1_grid " + sqlFrom + sqlWhere + " " + " ) AS DDX_RECORD_COUNT "
+ "FROM " + session.getAttribute("DatabaseName")
+ ".G1_grid " + sqlFrom + sqlWhere + " " + " ORDER BY "
+ StringEscapeUtils.escapeSql(SortKey) + " " + StringEscapeUtils.escapeSql(Sort) + ", "
+ StringEscapeUtils.escapeSql(TotalSortKey) + " "
+ StringEscapeUtils.escapeSql(Sort) + ") AS TMP1 ORDER BY "
+ StringEscapeUtils.escapeSql(SortKey) + " "
+ StringEscapeUtils.escapeSql((String)ResourceManager.findData("notSort", request))
+ ", " + StringEscapeUtils.escapeSql(TotalSortKey) + " "
+ StringEscapeUtils.escapeSql((String)ResourceManager.findData("notSort", request)) + " ) AS r ORDER BY "
+ StringEscapeUtils.escapeSql(SortKey) + " "
+ StringEscapeUtils.escapeSql(Sort) + ", "
+ StringEscapeUtils.escapeSql(TotalSortKey)
+ " " + StringEscapeUtils.escapeSql(Sort) + " ";
The last query runs without error, System.out of this query give this for example:
SELECT TOP 20 *, ( DDX_RECORD_COUNT / 20 + 1 ) AS DDX_PAGE_COUNT
FROM
( SELECT TOP 20 * FROM
( SELECT TOP 20 *,
(SELECT COUNT(*)
FROM SuiteMA_DIP.dbo.G1_grid
WHERE 1 = 1 ) AS DDX_RECORD_COUNT
FROM SuiteMA_DIP.dbo.G1_grid WHERE 1 = 0 ORDER BY DATA_ISCRIZIONE_ORDER DESC, SOGGETTO_RILEVANTE_PAID DESC) AS TMP1 ORDER BY DATA_ISCRIZIONE_ORDER ASC, SOGGETTO_RILEVANTE_PAID ASC ) AS r ORDER BY DATA_ISCRIZIONE_ORDER DESC, SOGGETTO_RILEVANTE_PAID DESC
But when i run sql with preparedStatement:
java.sql.SQLException: Invalid parameter index 1.
at net.sourceforge.jtds.jdbc.JtdsPreparedStatement.getParameter(JtdsPreparedStatement.java:340)
at net.sourceforge.jtds.jdbc.JtdsPreparedStatement.setParameter(JtdsPreparedStatement.java:409)
at net.sourceforge.jtds.jdbc.JtdsPreparedStatement.setObjectBase(JtdsPreparedStatement.java:395)
at net.sourceforge.jtds.jdbc.JtdsPreparedStatement.setObject(JtdsPreparedStatement.java:667)
at org.apache.tomcat.dbcp.dbcp2.DelegatingPreparedStatement.setObject(DelegatingPreparedStatement.java:188)
at org.apache.tomcat.dbcp.dbcp2.DelegatingPreparedStatement.setObject(DelegatingPreparedStatement.java:188)
at it.netbureau.jfx.db.SQLDBManager.execSQL(SQLDBManager.java:57)
at it.netbureau.jfx.db.SQLDBManager.execSQL(SQLDBManager.java:78)
at org.apache.jsp.G1.select_jsp._jspService(select_jsp.java:691)
The java method execute the query :
class jfx.db.SQLDBManager.execSQL:
public Object execSQL(PreparedStatement stmt, Object values[], String xmlId)
throws SQLException
{
Object result = null;
if(stmt == null)
return null;
try
{
for(int i = 0; i < values.length; i++)
if(values[i] == null)
stmt.setNull(i + 1, 4);
else
stmt.setObject(i + 1, values[i]); <--this give exception!
if(stmt.execute()) result = transform(stmt.getResultSet(), xmlId);
}
catch(SQLException ex)
{
rollback();
throw ex;
}
return result;
}
What's wrong?
Thank you very much
roby
Your query does not contain any parameters, a '?' is just a literal string with a question mark in it, it is not a parameter.
You also can't parameterize object names like column names and clauses (like a TOP 20), so even if you'd change it to - for example - order by ?, ... it wouldn't work, as you'd be sorting by the string value (which would be the same for all rows, so effectively you wouldn't be sorting at all).
To do what you want to do you will need to concatenate the column names (and other clauses) into the query string. This also means that you might open yourself up to SQL injection: be sure to check the values carefully (for example against a whitelist of allowed values).
I have a sample sqlite database that currently has 7 entries which is queried via:
String selectQuery = "SELECT * FROM " + TABLE_TRANSACTIONS + " x " +
"JOIN (SELECT " + KEY_TYPE_ID + ", COUNT(*) count FROM " + TABLE_TRANSACTIONS + " GROUP BY " + KEY_TYPE_ID + " ORDER BY count DESC) y " +
"ON x.type_id = y.type_id " + //type_id vs. KEY_TYPE_ID as alias won't recognize KEY_TYPE_ID
"WHERE x.type_id = " + TagData.TYPE_EXPENSE;
The output is:
Type A = 2500
Type A = 2599
Type B = 45000
Type C = 299
Type C = 2699
Type C = 10000
Type C = 12000
which correctly groups my types by their respective values. However, the ideal output would be:
Type B = 45000
Type C = 24998
Type A = 5099
where each type is then ordered by the sum of each type. Is this possible? If so what else should I be doing in my query? I'm relatively new to SQL and haven't been able to figure this out yet. Thank you in advance for any insight.
EDIT
Based on your input #CL. I now have a more simplified query:
String selectQuery = "SELECT *, SUM(" + KEY_AMOUNT + ") AS amount_sum " +
"FROM " + TABLE_TRANSACTIONS + " " +
"GROUP BY " + KEY_LABEL_ID + " " +
"ORDER BY amount_sum DESC";
which works as expected when I use sqlfiddle at http://www.sqlfiddle.com/#!5/b3615/1 but not when I use the query in Android. In Android, only the most recent entry for each label type is returned. The SUM doesn't seem to actually do its job.
What am I missing here?
Moving the aggregation and ordering into a subquery does not make sense.
If you want to get the count of all expense transactions per type, just use a simple aggregation:
SELECT Type_ID,
COUNT(*) count
FROM TRANSACTIONS
WHERE TYPE_IF = 'TYPE_EXPENSE'
GROUP BY TYPE_ID
ORDER BY count DESC
This is an aggregation query. Fortunately, SQLite supports CTEs, so you can just do something like this:
with t as (
<your query here>
)
select type, sum(value) as sumv
from t
group by type
order by sumv desc;
Even without the with clause, you could just use your query as a subquery.
You would, of course, use the appropriate column names in the query.
#CL. Your response led me down the correct path, for those interested here is the resulting query that achieved what I was after:
String selectQuery = "SELECT " + KEY_LABEL_ID + ", " + " SUM(" + KEY_AMOUNT + ") as total, " + KEY_TYPE_ID + " " +
"FROM (" +
"SELECT " + KEY_LABEL_ID + ", " + KEY_AMOUNT + ", " + KEY_TYPE_ID + " " +
"FROM " + TABLE_TRANSACTIONS + " " +
") as trans_table " +
"WHERE " + KEY_TYPE_ID + " = " + TagData.TYPE_EXPENSE + " " +
"GROUP BY " + KEY_LABEL_ID + " " +
"ORDER BY total DESC";
Lets assume, that we have some 2 tables in SQLite: TBL_PRODUCT and TBL_PRODUCT_ALIASES. Now, I want to query some data from joined two tables:
String sql = "SELECT " + TBL_PRODUCT + "." + C_PROD_PKEY_ID + ", " + TBL_PRODUCT_ALIASES + "." + C_PROD_ALIASES_PKEY_ID +
" FROM " + TBL_PRODUCT + " LEFT JOIN " + TBL_PRODUCT_ALIASES + " ON " + TBL_PRODUCT + "." + C_PROD_PKEY_ID + " = " + TBL_PRODUCT_ALIASES + "." + C_PROD_ALIASES_PKEY_ID +
" WHERE " + C_PROD_SERVER_ID + " = ? LIMIT 1";
Cursor cursor = SQLiteDataHelper.getInstance().rawQuery(sql, new String[] {"" + js_CartProduct.getLong("prod_id")});
Thats works great without any problem. And then I want to acquire some data from the returned cursor:
if (cursor.getCount() > 0) {
cursor.moveToFirst();
Long prodId = cursor.getLong(cursor.getColumnIndexOrThrow(TBL_PRODUCT + "." + C_PROD_PKEY_ID));
//... Some other code
}
Now, here is the problem: C_PROD_PKEY_ID and C_PROD_ALIASES_PKEY_ID are in the real world the same Strings: "_id". And as the result getLong() returns long not from the needed column of cursor.
How should I correctly address to the needed column? Are there other methods besides using AS in sql query OR/AND using definite numbers in cursor.getLong(0) instead of cursor.getLong(cursor.getColumnIndexOrThrow(TBL_PRODUCT + "." + C_PROD_PKEY_ID))?
Update:
cursor.getColumnNames() returns this: [_id, _id].