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";
Related
I want to implement this JPQL query using JPA:
String hql = "SELECT new org.plugin.service.PaymentTransactionsDeclineReasonsDTO(count(e.id) as count, e.status, e.error_class, e.error_message) " +
" FROM " + PaymentTransactions.class.getName() + " e " +
" WHERE e.terminal_id = :terminal_id AND (e.createdAt > :created_at) " +
" AND (e.status != 'approved') " +
" GROUP BY e.error_message " +
" ORDER BY count DESC";
But I get error: Caused by: org.hibernate.hql.internal.ast.QuerySyntaxException: expecting OPEN, found 'DESC' near line 1, column 334 [SELECT new org.plugin.service.PaymentTransactionsDeclineReasonsDTO(count(e.id) as count, e.status, e.error_class, e.error_message) FROM org.datalis.plugin.entity.PaymentTransactions e WHERE e.terminal_id = :terminal_id AND (e.createdAt > :created_at) AND (e.status != 'approved') GROUP BY e.error_message ORDER BY count DESC]
What is the proper way to order by count?
You cannot use the SQL keyword COUNT as an alias (not even in lower case). There is no way around it, you have to use alias names that are not SQL keywords (like SELECT, FROM, AS and so on).
I think you should use the following query, which will at least get rid of the error you posted:
String hql = "SELECT "
+ "new org.plugin.service.PaymentTransactionsDeclineReasonsDTO("
+ "count(e.id) as amount, " // <--- alias name changed here
+ "e.status, "
+ "e.error_class, "
+ "e.error_message) "
+ "FROM "
+ PaymentTransactions.class.getName() + " e "
+ "WHERE "
+ "e.terminal_id = :terminal_id "
+ "AND (e.createdAt > :created_at) "
+ "AND (e.status != 'approved') "
+ " GROUP BY e.error_message "
+ " ORDER BY amount DESC"; // <-- alias name used here
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
When I execute this query using a self-join from my java program
Query query = session.createSQLQuery("SELECT DISTINCT * " +
"FROM lerneinheit AS le1 JOIN lerneinheit AS le2 " +
"ON le1.datum = le2.datum AND le1.pid = le2.pid " +
" WHERE " +
" le1.datum BETWEEN '2016-10-20' AND '2016-10-20' AND " +
" le1.pid = 3 AND " +
" (le1.abgesagtrechtzeitig = false OR le1.nichtabgesagt = true OR le1.erschienen=true) AND " +
" (le2.abgesagtrechtzeitig = false OR le2.nichtabgesagt = true OR le2.erschienen=true) AND " +
" le1.lernid!= le2.lernid AND " +
" (le2.beginn+1 BETWEEN le1.beginn AND le1.ende OR le2.ende-1 BETWEEN le1.beginn AND le1.ende) " +
" ORDER BY le1.beginn");
I get the following error:
org.hibernate.loader.custom.NonUniqueDiscoveredSqlAliasException: Encountered a duplicated sql alias [LERNID] during auto-discovery of a native-sql query
Although it works fine if I do this from phpAdmin. Everything I found on that topic wasn't helpful at all. Anyone got any idea how to solve that?
It may occurred when there are two or more column/alias exists with the SAME NAME in your select query.
Like...
Select A.NAME, A.ADDRESS, B.NAME, B.AGE from TABLE_A A join TABLE_B B on A.SOME_ID = B.SOME_ID;
Encountered a duplicated sql alias [NAME] during auto-discovery of a native-sql query will show.
I'm not sure but the HQL equivalent for != is <>, so you should write "le1.lernid <> le2.lernid AND"
And by the way I recommend :
Query query = session.createSQLQuery("SELECT DISTINCT * " +
"FROM lerneinheit AS le1 JOIN lerneinheit AS le2 " +
"ON le1.datum = le2.datum AND le1.pid = le2.pid " +
" WHERE " +
" le1.datum BETWEEN :dateMin AND :dateMax AND " +
" le1.pid = :le1Pid AND " +
" (le1.abgesagtrechtzeitig = false OR le1.nichtabgesagt = true OR le1.erschienen = true) AND " +
" (le2.abgesagtrechtzeitig = false OR le2.nichtabgesagt = true OR le2.erschienen = true) AND " +
" le1.lernid != le2.lernid AND " +
" (le2.beginn + 1 BETWEEN le1.beginn AND le1.ende OR le2.ende - 1 BETWEEN le1.beginn AND le1.ende) " +
" ORDER BY le1.beginn");
query.setParametter("dateMin", "2016-10-20");
query.setParametter("dateMax", "2016-10-20");
query.setParametter("le1Pid", 3);
Here is my queries split up that work perfectly fine...
String sqlstatement = "SELECT WBLinkWebsiteID, WBLinkCategoryParentID, WBLinkTitle, WBLinkURL FROM WEBSITECATEGORY_TABLE WHERE WBLinkCategoryID = ?";
String[] args = { CategorySubID };
Part 2
sqlstatement = "SELECT LOCATIONS_TABLE.LocationWebsiteID, "
+ "LOCATIONS_TABLE.locationCity, "
+ "LOCATIONS_TABLE.locationState, "
+ "LOCATIONS_TABLE.locationCountry, LOCATIONS_TABLE.locationType, "
+ "LOCATIONS_TABLE.locationUrl, "
+ "PREF_TABLE.Pref_SavedTitle "
+ "FROM PREF_TABLE INNER JOIN "
+ "LOCATIONS_TABLE ON PREF_TABLE.Pref_LocationID = LOCATIONS_TABLE.LocationID "
+ "WHERE "
+ "PREF_TABLE.Pref_SavedTitle = '" + theSavedPref + "' ORDER BY LOCATIONS_TABLE.locationState, LOCATIONS_TABLE.locationCity";
Now here is my attempt to combine the two instead of just having 2 go in row back to back and burn time/resources...
String NewSqlstatement = "SELECT LOCATIONS_TABLE.LocationWebsiteID, "
+ "LOCATIONS_TABLE.locationCity, "
+ "LOCATIONS_TABLE.locationState, "
+ "LOCATIONS_TABLE.locationCountry, "
+ "LOCATIONS_TABLE.locationUrl, "
+ "LOCATIONS_TABLE.LocationID, "
+ "PREF_TABLE.Pref_SavedTitle, "
+ "WEBSITECATEGORY_TABLE.WBLinkTitle, "
+ "WEBSITECATEGORY_TABLE.WBLinkURL "
+ "FROM PREF_TABLE INNER JOIN "
+ "LOCATIONS_TABLE ON PREF_TABLE.Pref_LocationID = LOCATIONS_TABLE.LocationID "
+ "INNER JOIN WEBSITECATEGORY_TABLE "
+ "ON WEBSITECATEGORY_TABLE.WBLinkWebsiteID = PREF_TABLE.Pref_WebsiteID "
+ "WHERE "
+ "PREF_TABLE.Pref_SavedTitle = '" + theSavedPref + "'";
Now when I try to do my "SINGLE" way it keeps returning the WHOLE database of Locations in the LOCATIONS_TABLE query. It doesn't do just the exact ones I need.
I know the query works, because I have tested it here: http://sqlfiddle.com/#!6/ede97/2
Now I know my example on sqlfiddle is using MS Server 2014, but I assumed the syntax should be pretty much the same since its just standard SELECT with inner joins, but I could be wrong?
Anyone know what I'm doing wrong? Any help is greatly appreciated
EDIT - Fixed the SQLFIDDLE, I put the wrong statement in the example
Aren't you missing your filter on WBLinkCategoryID in the combined query. Shouldn't you have this:
...
+ "INNER JOIN WEBSITECATEGORY_TABLE "
+ "ON WEBSITECATEGORY_TABLE.WBLinkWebsiteID = PREF_TABLE.Pref_WebsiteID "
+ "WHERE "
+ "WEBSITECATEGORY_TABLE.WBLinkCategoryID IN (<value1>,...,<valueN>) AND "
+ "PREF_TABLE.Pref_SavedTitle = '" + theSavedPref + "'";
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].