I have an employee class.
case class Employee(name: String, age: Int, company: String)
and I write a Group-By query in Quill.
val q = quote {
query[Employee]
.filter(_.age == lift(100))
.groupBy(_.company)
.map { a =>
val tot = a._2.size
val totCond = (a._2.map { i =>
if (i.name == "software") 1 else 0
}.sum)
(tot, totCond)
}
}
ctx.run(q)
This query translates to
SELECT COUNT(*), SUM(CASE WHEN x36.name = 'software' THEN 1 ELSE 0 END) FROM employee x36
WHERE x36.age = ? GROUP BY x36.company
which is fine.
Now I want to sort the query like
quote {
query[Employee]
.filter(_.age == lift(100))
.groupBy(_.company)
.map { a =>
val tot = a._2.size
val totCond = (a._2.map { i =>
if (i.name == "software") 1 else 0
}.sum)
(tot, totCond)
}.sortBy(_._1)(Ord.desc)
}
But this translates to
SELECT x36._1, x36._2 FROM (SELECT COUNT(*) AS _1, SUM(CASE WHEN x36.name = 'software' THEN 1
ELSE 0 END) AS _2 FROM employee x36 WHERE x36.age = ? GROUP BY x36.company) AS x36 ORDER BY
x36._1 DESC
this appears as a subquery which does all logic and the main query which just selects the record from main query.
I want to get rid of this and do everything in only one query. Is there a way to do it ??
Related
I'm currently working on a Spring Boot Webapp where I want to retreive tasks with JPA.
A Task can have multiple requirements and my customer creates requirement_answers which are connected to his wedding. I now want to select all tasks where all the requirement.answer_value are answered with 'true'.
My relevant Database Schema is:
My current query is this:
I now want to check that the task with the same uuid has all requirement_answer with true?
How can I achieve this?
Greetings
EDIT:
My Solution, filtered in Code instead of jpql as I could not get it working
#Query("""
select t, ra
from
Task t,
RequirementAnswer ra,
Requirement r,
Wedding w
where
ra.requirement = r and
w.id = :weddingId and
t member of r.tasks"
""")
fun findByWedding(weddingId: Long): List<Tuple>?
}
Here is the filtering:
fun getTasksByWedding(wedding: Wedding?): List<Task> {
val tasks: MutableMap<Task,String> = mutableMapOf()
wedding?.id?.let { taskRepository.findByWedding(it) } ?.map {
val task = it.get(0) as Task
val requirementAnswer = it.get(1) as RequirementAnswer
tasks[task]?.let { taskAnswer ->
if(taskAnswer != requirementAnswer.answerValue){
tasks.remove(task)
}
}?: let {
if(requirementAnswer.answerValue == "true"){
tasks[task] = requirementAnswer.answerValue
}
}
} ?: throw ResponseStatusException(HttpStatus.BAD_REQUEST, "Wedding doesn't exist")
return tasks.map { it.key }
}
With SQL you can do use subselects to compare the counts:
select t.*
from task t
join task_requirement tr on t.uuid = tr.task_id
join requirement r on tr.requirement_id = r.id
join requirement_answer ra1 on r.id = ra1.requirement_id
join wedding_requirement_answer wra1 on ra1.id = wra1.requirement_answer_id
where wra1.wedding_id = 1
and ( (select ra2.requirement_id
from requirement_answer ra2
join wedding_requirement_answer wra2 on ra2.id = wra2.requirement_answer_id
where wra2.wedding_id = wra1.wedding_id
and ra2.requirement_id = ra1.requirement_id))
=
(select ra3.requirement_id
from requirement_answer ra3
join wedding_requirement_answer wra3 on ra3.id = wra3.requirement_answer_id
where wra3.wedding_id = wra1.wedding_id
and ra3.requirement_id = ra1.requirement_id
and ra3.answer_value = 'true');
I'm doing a sample project where it counts the total issues per month that is to be displayed in a bar graph..
Here is my working SQL query
SELECT
SUM(CASE datepart(month,D_ISSUE) WHEN 1 THEN 1 ELSE 0 END) AS 'Jan',
SUM(CASE datepart(month,D_ISSUE) WHEN 2 THEN 1 ELSE 0 END) AS 'Feb',
so on...
FROM EMP_MEMOS
Can someone help me transpose this SQL Query into a LinQ code. i'm still trying to understand how it works
Here is my code so far, but i still can't get it to work.
public ActionResult MonthCount()
{
var Monthly = (from f in db.EMP_MEMOS
group f by new { month = f.D_ISSUE, year = f.D_ISSUE } into g
orderby g.Key.year
select new
{
dt = string.Format("{0}/{1}", g.Key.month, g.Key.year),
}).ToList();
return Json(new { result = Monthly }, JsonRequestBehavior.AllowGet);
}
I already got the answer, gonna share it here:
public ActionResult CountMonth()
{
var memo = from t in db.EMP_MEMOS
select new
{
t.D_ISSUE.Value.Month,
t.D_ISSUE.Value.Year
};
var res = from s in memo
group s by new { s.Year, s.Month } into g
select new
{
Period = g.Key,
MemoCount = g.Count(),
};
return Json(new { result = res }, JsonRequestBehavior.AllowGet);
}
How can I do the following sql statement in Slick. The issue is that in the select statement there is filter and I don't know how to do that in Slick.
SELECT Sellers.ID,
COALESCE(count(DISTINCT Produce.IMPORTERID) FILTER (WHERE Produce.CREATED > '2019-04-30 16:38:00'), 0::int) AS AFTERDATE,
COALESCE(count(DISTINCT Produce.IMPORTERID) FILTER (WHERE Produce.NAME::text = 'Apple'::text AND Produce.CREATED > '2018-01-30 16:38:00'), 0::bigint) AS APPLES
FROM Sellers
JOIN Produce ON Produce.SellersID = Sellers.ID
WHERE Sellers.ID = 276
GROUP BY Sellers.ID;
Try
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
import slick.jdbc.PostgresProfile.api._
case class Seller(id: Long)
case class Produce(name: String, sellerId: Long, importerId: Long, created: LocalDateTime)
class Sellers(tag: Tag) extends Table[Seller](tag, "Sellers") {
def id = column[Long]("ID", O.PrimaryKey)
def * = id <> (Seller.apply, Seller.unapply)
}
class Produces(tag: Tag) extends Table[Produce](tag, "Produce") {
def name = column[String]("NAME", O.PrimaryKey)
def sellerId = column[Long]("SellersID")
def importerId = column[Long]("IMPORTERID")
def created = column[LocalDateTime]("CREATED")
def * = (name, sellerId, importerId, created) <> (Produce.tupled, Produce.unapply)
}
val sellers = TableQuery[Sellers]
val produces = TableQuery[Produces]
val dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
val ldt2019 = LocalDateTime.parse("2019-04-30 16:38:00", dtf)
val ldt2018 = LocalDateTime.parse("2018-01-30 16:38:00", dtf)
sellers.join(produces).on(_.id === _.sellerId)
.filter { case (s, p) => p.sellerId === 276L }
.groupBy { case (s, p) => s.id }
.map { case (sid, group) =>
(
sid,
group
.filter { case (s, p) => p.created > ldt2019 }
.map { case (s, p) => p.importerId }
.distinct.length,
group
.filter { case (s, p) => p.name === "Apple" && p.created > ldt2018 }
.map { case (s, p) => p.importerId }
.distinct.length
)
}
libraryDependencies += "com.github.tminglei" %% "slick-pg" % "0.18.0"
I really hope something like #Dymytro's answer can work, but from my testing it all comes down to limitations with the GROUP BY, and here are the issues you will run into:
Trying to use just Slick with a Postgres driver won't work because Slick doesn't support aggregate functions with a FILTER clause. Postgres is one of the few databases that supports FILTER! So you won't get far:
someQuery
.groupBy { a => a.pivot }
.map{ case (pivot, query) =>
(
pivot,
query
.filter(_.condition === "stuff")
.map(_.column).distinct.length
)
}
Although it compiles, you'll get some kind of runtime error like:
[ERROR] slick.SlickTreeException: Cannot convert node to SQL Comprehension
Then, if you check out slick-pg you'll notice it has support for Postgres aggregate functions! Including the FILTER clause! But... there's an open issue for aggregate functions with GROUP BY so this sort of attempt will fail too:
import com.github.tminglei.slickpg.agg.PgAggFuncSupport.GeneralAggFunctions._
...
someQuery
.groupBy { a => a.pivot }
.map{ case (pivot, query) =>
(
pivot,
query
.map(a => count(a.column.distinct).filter(a.condition === "stuff"))
)
}
No matching Shape found.
[error] Slick does not know how to map the given types.
So until those issues are resolved or someone posts a work around, luckily simple single column FILTER expressions can be equivalently implemented with the more primitive CASE statements. Though not as pretty, it will work!
val caseApproach = someQuery
.groupBy { a => a.pivot }
.map{ case (pivot, query) =>
(
pivot,
query
.map{ a =>
Case If a.condition === "stuff" Then a.column
}.min //here's where you add the aggregate, e.g. "min"
)
}
println(caseApproach.result.statements.headOption)
select pivot, min((case when ("condition" = 'stuff') then "column" end)) from table group by pivot;
EF6, asp mvc core and SQL Server are used on the background.
I have to do many queries to the same table with different conditions, f.e.
SELECT COUNT(*) FROM Table1 WHERE a = true
SELECT COUNT(*) FROM Table1 WHERE b = true
SELECT COUNT(*) FROM Table1 WHERE a = true || b = true
SELECT a FROM Table1 WHERE b = true
So 4 queries to Table1 with different conditions. I think that as result I have to read the entire Table1 four times. In pseudo code it might be looking like this.
var res1 = new list();
foreach(var rec in Table1)
{
// read Table1 first time
if(rec.a == true)
{
res1.push(rec);
}
}
var res2 = new list();
foreach(var rec in Table1)
{
// read Table1 second time
if(rec.b == true)
{
res2.push(rec);
}
}
var res3 = new list();
foreach(var rec in Table1)
{
// read Table1 third time
if(rec.a == true || rec.b == true)
{
res3.push(rec);
}
}
var res4 = new list();
foreach(var rec in Table1)
{
// read Table1 fourth time
if(rec.b == true)
{
res4.push(rec);
}
}
I want to know how to read the Table1 only one time and get four different results, like this:
var res1 = new List();
var res2 = new List();
var res3 = new List();
var res4 = new list();
foreach(rec in Table1)
{
// read Table1 first time
if(a == true)
{
res1.push(rec);
}
if(b == true)
{
res2.push(rec);
}
if(a == true || b == true)
{
res3.push(rec);
}
if(b == true)
{
res4.push(rec);
}
}
Also the challenge, that those queries are dynamic sql, I mean that a = true, b = true, a = true || b = true are stored in database. And queries are running in this way:
string query = "SELECT Count(*) FROM Table1 WHERE" + condition;
var count = ExecuteSql(query);
The sample above is simplified, but in reality all the query is split and stored in database.
PS. Actually I want to speed up the page, which makes 30-40 requests to the server and each request is the query to the same table. I think if I can replace them with one request instead of 40 requests.
You may use conditional aggregation with just a single query:
SELECT
COUNT(CASE WHEN a = true THEN 1 END) AS cnt_a,
COUNT(CASE WHEN b = true THEN 1 END) AS cnt_b,
COUNT(CASE WHEN a = true OR b = true THEN 1 END) AS cnt_a_b
FROM Table1;
This would reduce the number of full table scans from 3 to just 1. Also, it would also potentially reduce the number of round trips to/from the database from 3 to 1.
I have this SQL query that I want to translate into Linq-to-SQL:
Now here's the beginning of the Linq-to-SQL code but I'm stuck on how to group fields and get SUM :
private void GetDatas()
{
DateTime d = DateTime.Now;
using (DataClasses1DataContext dc = new DataClasses1DataContext())
{
var query = from ent in dc.STK_ABC_ENT
join det in dc.STK_ABC_DET on ent.ENT_ID equals det.ENT_ID
join art in dc.FICHES_ARTICLES on ent.ART_CODE equals art.ART_CODE
where !ent.ENT_NUM_PAL.Contains("FDR_")
&& ent.ENT_OUTDATE == null
&& ent.ENT_PICKING == null
&& ent.ENT_DATE_ENT != d
// How to group here ?
// How to get SUM ??
}
}
You can use group x by ColumnName into z to group a column.
When you want to group multiple columns you can use group x by new { x.Column1, x.Column2 } into z.
When you want to group multiple columns in multiple tables you can use group new { x, y } by new { x.Column, y.Column } into z.
With Sum, just call it in select with lamda expression.
Example:
var query = from ent in dc.STK_ABC_ENT
join det in dc.STK_ABC_DET on ent.ENT_ID equals det.ENT_ID
join art in dc.FICHES_ARTICLES on ent.ART_CODE equals art.ART_CODE
where !ent.ENT_NUM_PAL.Contains("FDR_") && ent.ENT_OUTDATE == null
&& ent.ENT_PICKING == null && ent.ENT_DATE_ENT != d
group new { art, ent } by new {
art.ART_CODE,
...,
ent.ENT_DATE_ENT,
...
} into grouped
select new {
ArtCode = grouped.Key.ART_CODE,
SumPdsNet = grouped.Sum(x => x.DET_PNET),
...
}
I hope it can work for you.