How to Create a Criteria Query with AVG and GROUP using JPA - sql

I wanted to get the list of products based on average customer rating using Criteria query. I wrote a piece of code using criteria query aggregate functions AVG and kept GROUP BY clause. But somehow i am getting "Not a Group BY Expression" exception. I tried and searched in Google in order to resolve the but nothing was helpful. Posting the code below
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<Object[]> cq = builder.createQuery(Object[].class);
Root<GdbCustomerProductReviewImpl> productReview = cq.from(GdbCustomerProductReviewImpl.class);
Path productPath = productReview.get("product");
Path documentPath = productPath.get("photo");
Path categoryPath = productPath.get("defaultCategory");
Path productCategoryPath = categoryPath.get("prdCategory");
cq.multiselect(productReview.get("id"),productPath.get("url"),productPath.get("skuName"),documentPath.get("id"));
cq.where(builder.isNotNull(productPath.get("id")));
cq.where(builder.equal(productReview.get("status"),"ACTIVE"));
cq.where(builder.equal(productReview.get("reviewType"),"PRODUCT"));
cq.where(builder.equal(productPath.get("isEnable"),Boolean.TRUE));
cq.where(builder.equal(productPath.get("status"),StatusType.APPROVED.getType()));
cq.where(builder.isNotNull(productPath.get("defSkuMap")));
cq.where(builder.equal(productCategoryPath.get("isEnabled"),Boolean.TRUE));
cq.groupBy(productReview.get("id"));
Expression event_count = builder.avg(productReview.get("rating"));
cq.orderBy(builder.desc(event_count));
List<Object[]> resultList = em.createQuery(cq).setMaxResults(size).getResultList();

On further Research found out a solution and also thanks to crizzis. Posting the solution which had worked for me.
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<Object[]> cq = builder.createQuery(Object[].class);
Root<GdbCustomerProductReviewImpl> productReview = cq.from(GdbCustomerProductReviewImpl.class);
Path productPath = productReview.get("product");
Path documentPath = productPath.get("photo");
Path categoryPath = productPath.get("defaultCategory");
Path productCategoryPath = categoryPath.get("prdCategory");
Expression event_count = builder.avg(productReview.get("rating"));
cq.multiselect(productPath.get("id"),productPath.get("url"),productPath.get("skuName"),documentPath.get("id"));
cq.where(builder.isNotNull(productPath.get("id")),builder.equal(productReview.get("status"),"ACTIVE"),builder.equal(productReview.get("reviewType"),"PRODUCT"),builder.equal(productPath.get("isEnable"),Boolean.TRUE),builder.equal(productPath.get("status"),StatusType.APPROVED.getType()),builder.isNotNull(productPath.get("defSkuMap")),builder.equal(productCategoryPath.get("isEnabled"),Boolean.TRUE));
cq.groupBy(productPath.get("id"),productPath.get("url"),productPath.get("skuName"),documentPath.get("id"));
cq.orderBy(builder.desc(event_count));
List<Object[]> resultList = em.createQuery(cq).setMaxResults(size).getResultList();

Related

API return breaks when using inner join?

I've been building an API which was working absolutely fine until I tried to add an inner join
The SQL I'm passing returns as I'd expect when I run it in Beaver (am using Mac)
However, when I try and access it via my API end point, instead of the combined results, I get only results from the table I added in the join
I presume am doing something really stupid ...
From my controller:
result = dbtest.FromDatabase(
"SELECT A.FAMILY_ID
,A.START_TIME
,A.END_TIME
,A.WORKER_ID
,A.WEEK_NO
,A.ID
,A.SHIFT_NO
,A.DAY_OF_WEEK
,A.HOLIDAY_OR_TERM
,B.WORKER_NAME
FROM SHIFT_REQ_TBL A
INNER JOIN WORKER_TBL B ON A.WORKER_ID = B.WORKER_ID
WHERE A.FAMILY_ID = '" + FAMILY.FAMILY_ID + "'");
From my model
if (query.Contains("SHIFT_REQ_TBL"))
{
var tbl_type = new TimekeeperTables.SHIFT_REQ_TBL();
tbl_type.FAMILY_ID = Convert.ToInt32(reader["FAMILY_ID"]);
tbl_type.ID = Convert.ToInt32(reader["ID"]);
tbl_type.WEEK_NO = Convert.ToInt32(reader["WEEK_NO"]);
tbl_type.WORKER_ID = Convert.ToInt32(reader["WORKER_ID"]);
tbl_type.SHIFT_NO = reader["SHIFT_NO"].ToString();
tbl_type.START_TIME = reader["START_TIME"].ToString();
tbl_type.END_TIME = reader["END_TIME"].ToString();
tbl_type.DAY_OF_WEEK = reader["DAY_OF_WEEK"].ToString();
tbl_type.HOLIDAY_OR_TERM = reader["HOLIDAY_OR_TERM"].ToString();
tbl_type.WORKER_NAME = reader["WORKER_NAME"].ToString();
db_results.Add(tbl_type);
jsonDoc = JsonConvert.SerializeObject(db_results);
}
Results (from postman)
"[{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"},{\"WORKER_ID\":1,\"WORKER_NAME\":\"UNASSIGNED\"}]"
I'm sorry, I was being stupid and figured it out
I was using a really dumb way of identifying the calling URL which already was checking for SHIFT_TBL in an earlier case

How to write the HAVING clause with SQL COUNT(DISTINCT column_name) function in Codeigniter Active Record?

I am using Codeigniter and am trying to use the Active Record Class for all my database operations.
However, I did run into problems when trying to convert the last bid of the following (working) query into Active Record code.
$sql = ("SELECT ac.cou_id
FROM authorcourse ac
INNER JOIN course c
ON ac.cou_id = c.cou_id
WHERE cou_name = ? // ? = $cou_name
AND cou_number = ? // ? = $cou_number
AND cou_term = ? // ? = $cou_term
AND cou_year = ? // ? = $cou_year
AND FIND_IN_SET (ac.aut_id, ?) //? = $aut_ids_string
GROUP BY ac.cou_id
HAVING COUNT(DISTINCT ac.aut_id) = ? //? = $aut_quantity
AND COUNT(DISTINCT ac.aut_id) = (SELECT COUNT(DISTINCT ac2.aut_id)
FROM authorcourse ac2
WHERE ac2.cou_id = ac.cou_id)");
$query = $this->db->query($sql, array($cou_name, $cou_number, $cou_term, $cou_year, $aut_ids_string, $aut_quantity));
Question: How do I convert the HAVING clause into valid Active Record code?
I tried using $this->db->having(); and $this->db->distinct(); but failed to combine the functions to achieve the desired result.
My (working) code so far:
$this->db->select('ac.cou_id');
$this->db->from('authorcourse ac');
$this->db->join('course c', 'c.cou_id = ac.cou_id');
$this->db->where('cou_name', $cou_name);
$this->db->where('cou_number', $cou_number);
$this->db->where('cou_term', $cou_term);
$this->db->where('cou_year', $cou_year);
$this->db->where_in('ac.aut_id',$aut_ids);
$this->db->group_by('ac.cou_id');
// missing code
Thanks a lot!
You code seems quite clumsy. But, you can use having clause in the following way:
$this->db->having("ac.aut_id = '".$aut_qty."'", null, false)

Linq-to-sql query error with StartsWith

I am using LINQPad to run the following query:
var pds = (from p in Projects
group p by p.FiscalYearVariables.FiscalYear into grouped
where grouped.Count() > 0
select new {
fiscalYear = grouped.Key,
projectDetails = grouped.SelectMany(a=>a.ProjectDetails),
Programs = (from pwbs in Programs.SelectMany(a =>a.ProgramWbsNumbers)
let ds = pwbs.WbsNumbers.DisplayString
where pwbs.Programs.IsActive
&& (from w in WbsNumbers
where w.DisplayString.StartsWith(ds)
select w).Any()
select pwbs.Programs)
});
pds.Dump();
And I get the error:
NotSupportedException: Only arguments that can be evaluated on the client are supported for the String.StartsWith method.
I'm not sure how to go about correcting this error. I need to get each Program where the WbsNumber starts with the WbsNumber contained within ProgramWbsNumbers if that helps.
Try this:
where SqlMethods.Like(w.DisplayString, ds + "%")

C# RegEx.Matches doesn't return all submatches inside expression unlike RegEx.Replace

Could you help me to understand what is wrong. I have usual SQL query :
var SQL = "SELECT [Extent1].[RouteID] AS [RouteID]
FROM [RoutesEntities].[Routes] AS [Extent1]\r\n
INNER JOIN [dbo].[Locales] AS [Extent2]
ON [Extent2].[LocaleID] = [Extent1].[LocaleID]";
And i need to define location of FROM part till its AS alias.
I did the following with RegEx.Replace :
var pattern = #"(FROM[^(SELECT)]+?Routes.+?AS.+?\[?([^\]\s]+)\]?)";
var result = Regex.Replace(SQL, pattern, "$1 $2", RegexOptions.Singleline | RegexOptions.IgnoreCase);
And it works fine - this will return :
match_$1 = "FROM [RoutesEntities].[Routes] AS [Extent1]";
match_$2 = "Extent1";
BUT! If i try to use Regex.Matches with the same options and same input string ... it finds only one match.
MatchCollection queryPlace = Regex.Matches(
SQL,
#"(FROM[^(SELECT)]+?Routes.+?AS.+?\[?([^\]\s]+)\]?)",
RegexOptions.IgnoreCase | RegexOptions.Singleline
);
match_$1 = "FROM [RoutesEntities].[Routes] AS [Extent1]";
WHY??? Is this a bug, or i should create a separate named group for each sub expression? Does anybody know why this happens, why only first match was found?
P.S. Regex is correct - i'm sure, you can check it here - http://www.gskinner.com/RegExr/
Thank, Artem
Sorry, I think i was so stupid because i did not know how exactly Regex.Matches method works and now i understood that in any case i should use Regex.Match and its "Groups" property :
Match queryPlace = Regex.Match(
_queryData.CommandText,
#"(FROM[^(SELECT)]+?" + tableInner + #".+?AS.+?\[?([^\]\s]+)\]?)",
RegexOptions.IgnoreCase | RegexOptions.Singleline
);
String innerAlias = "";
var d = queryPlace.Groups[0]; // FROM [RoutesEntities].[Routes] AS [Extent1]
var d1 = queryPlace.Groups[1]; // FROM [RoutesEntities].[Routes] AS [Extent1]
var d2 = queryPlace.Groups[2]; // Extent1
Sorry for disturbing, answer is found.

nhibernate hql with named parameter

I have implemented a search function using Castel Active Record. I thought the code is simple enough but I kept getting
NHibernate.QueryParameterException : could not locate named parameter [searchKeyWords]
errors. Can someone tell me what went wrong? Thanks a million.
public List<Seller> GetSellersWithEmail(string searchKeyWords)
{
if (string.IsNullOrEmpty(searchKeyWords))
{
return new List<Seller>();
}
string hql = #"select distinct s
from Seller s
where s.Deleted = false
and ( s.Email like '%:searchKeyWords%')";
SimpleQuery<Seller> q = new SimpleQuery<Seller>(hql);
q.SetParameter("searchKeyWords", searchKeyWords);
return q.Execute().ToList();
}
Why do not u pass the % character with parameter?
string hql = #"select distinct s
from Seller s
where s.Deleted = false
and ( s.Email like :searchKeyWords)";
SimpleQuery<Seller> q = new SimpleQuery<Seller>(hql);
q.SetParameter("searchKeyWords", "%"+searchKeyWords+"%");
return q.Execute().ToList();