How to add AND to the join SLICK - scala

I have the problem writing query in SLICK
Here is my request to MySql database:
SELECT * FROM readings AS r
JOIN parameters AS p
LEFT JOIN sensorvalues AS sv ON sv.parameter_id=p.id AND sv.reading_id=r.id
How can I write it using SLICK ? It is really lack of info on joins in docs.
UPDATE 1
I tried all the combinations even one like this
val q = for{
Join(p,sv) <- Parameters leftJoin SensorValues on (_.id is sv.parameter_id)
r <- Readings if sv.reading_id is r.id
} yield(r,p,sv)
In this case compiler gives me an error wrong number of parameters; expected = 2
So I replaced sv.parameter_id to _.parameter_id and now it argues about Join
It said that:
constructor cannot be instantiated to expected type; found : models.Join required: (models.Parameters.type, models.SensorValues.type)
I'm using last version of SLICK and it's deprecated. I manually imported
import scala.slick.lifted.Join and now it looks like it's regular innerJoin because it uses WHERE instead of putting and after ON.
SQL generated buy query:
select x2.id, x2.platform_id, x2.date, x3.x4, x3.x5, x3.x6, x7.x8, x7.x9, x7.x10, x7.x11 from (select x12.id as x4, x12.name as x5, x12.units as x6 from parameters x12) x3 left outer join (select x13.id as x8, x13.reading_id as x9, x13.parameter_id as x10, x13.value as x11 from sensorValues x13) x7 on x3.x4 = x7.x10, readings x2 where true and (x7.x9 = x2.id)
Even this one would be good with added condition to the ON section
val readings = for {
all <-Readings join Parameters leftJoin SensorValues on (_._2.id is _.parameter_id) if(all._1._1.id === all._2.reading_id)
} yield (all._1._1,all._1._2,all._2)
But this never hapens.
SELECT
x2.x3,
x2.x4,
x2.x5,
x2.x6,
x2.x7,
x2.x8,
x9.x10,
x9.x11,
x9.x12,
x9.x13
FROM
(
SELECT
x14.x15 AS x3,
x14.x16 AS x4,
x14.x17 AS x5,
x18.x19 AS x6,
x18.x20 AS x7,
x18.x21 AS x8
FROM
(
SELECT
x22.`id` AS x15,
x22.`platform_id` AS x16,
x22.`date` AS x17
FROM
`readings` x22
)x14
INNER JOIN(
SELECT
x23.`id` AS x19,
x23.`name` AS x20,
x23.`units` AS x21
FROM
`parameters` x23
)x18
)x2
LEFT OUTER JOIN(
SELECT
x24.`id` AS x10,
x24.`reading_id` AS x11,
x24.`parameter_id` AS x12,
x24.`value` AS x13
FROM
`sensorValues` x24
)x9 ON x2.x6 = x9.x12
WHERE
x2.x3 = x9.x11
instead WHERE I need AND. What Should I use to mention this condition or there is no functionality in slick for that?

EDIT
The OP is looking for chained on conditions in a join of 2 table objects.
This should compile:
val q = for{
r <- Readings
Join(p,s) <- Params leftJoin Sensors on (
(a,b)=> (a.id is b.parameter_id) && (b.reading_id is r.id)
)
} yield(r,p,s)
ORIGINAL
Try something like this:
val q = for{
Join(p,s) <- Params leftJoin Sensors on (_.id is s.parameter_id)
r <- Readings if s.reading_id is r.id
} yield(r,p,s)
val result =
q.list.map{ case(r,p,s)=>
SomeCaseClass(r,p,s)
}

Related

Scala Slick combining Rep sub queries into one re

I sum up totals in two different database tables:
val sum1Query: Rep[Int] = tableQuery1.map(_.amount).sum.ifNull(0)
val sum2Query: Rep[Int] = tableQuery2.map(_.amount).sum.ifNull(0)
for {
sum1 <- sum1Query.result
sum2 <- sum2Query.result
} yield {
sum1 + sum2
}
This runs 2 SQL queries to the database each time .result is called. I am looking for a way to make it use only one SQL query.
Something like this doesn't work:
for {
sum1 <- sum1Query
sum2 <- sum2Query
} yield {
sum1 + sum2
}.result
Any ideas on how to do it in Slick other than using plain SQL query?
Each call to .result creates a DBIO action which is a SQL statement. The trick to reducing the number of actions is to find a way to combine two queries (or two Reps) together into one action.
In your case you could zip the two queries:
val sum1 = table1.map(_.amount).sum.ifNull(0)
val sum2 = table2.map(_.amount).sum.ifNull(0)
val query = sum1.zip(sum2)
When run the query.result you'll execute a single query something like:
select ifnull(x2.x3,0), ifnull(x4.x5,0)
from
(select sum("amount") as x3 from "table_1") x2,
(select sum("amount") as x5 from "table_2") x4
...which will result in a tuple of the two values.
However, as you've already just got a Rep[Int] you can use + in the database:
val query = sum1 + sum2
...which will be a query along the lines of:
select ifnull(x2.x3,0) + ifnull(x4.x5,0)
from
(select sum("amount") as x3 from "table_1") x2,
(select sum("amount") as x5 from "table_2") x4
Just put it like this:
(sum1 + sum2).result

Left outer join of three tables with two conditions each not working in Slick

I have the following left outer join that I'd like to represent in Slick:
select * from tableD d
left outer join tableE e on e.ds_sk=d.ds_sk and e.ds_type = d.ds_type
left outer join tableV v on v.es_sk=e.sk and v.var_name=d.var_name
where e.sk=30;
Note that each relationship contains two join conditions. The first join conditions are between tableD and tableE, and the second join conditions are between tableV, tableE and tableD. Finally, there's also a where condition.
This is my attempt (that throws a compilation error):
val query = for {
((d, e), v) <- tableE joinLeft tableD on ((x,y) => x.dsType === y.dsType && x.dsSk === y.dsSk)
joinLeft tableV on ((x,y) => x._1.sk === y.esSk && x._2.varName === y.varName)
if (e.sk === 30)
} yield (d,e,v)
The error I get is:
â—¾value varName is not a member of slick.lifted.Rep[Option[tableD]]
What is the error and how to fix this code?
There seems to be discrepancies between your SQL query (D leftjoin E leftjoin V) and Slick query (E leftjoin D leftjoin V).
Assuming your SQL query is the correct version, the Slick query should look like the following:
val query = for {
((d, e), v) <- tableD joinLeft tableE on ( (x, y) =>
x.dsType === y.dsType && x.dsSk === y.dsSk )
joinLeft tableV on ( (x, y) =>
x._2.map(_.sk) === y.esSk && x._1.varName) === y.varName )
if (e.sk === 30)
} yield (d,e,v)
Note that in the second joinLeft, you need to use map to access fields in tableE which is wrapped in Option after the first joinLeft. That also explains why you're getting the error message about Option[tableD] in your Slick query.
You can compare doing map by varName, example:
... && x._2.map(_.varName) === y.varName ...
Here I leave a similar example:
val withAddressesQuery = for {
(((person, phone), _), address) <- withPhonesQuery.
joinLeft(PersonAddress).on(_._1.personId === _.personId).
joinLeft(Address).on(_._2.map(_.addressId) === _.addressId)
} yield (person, phone, address)
Notice: ... on(_._2.map( _.addressId) === ...see complete
There's some more information about joinLeft in Chapter 6.4.3 of Essential Slick.

Slick join with subqueries

I build following SQL Query
SELECT
v.uuid, d.start_time, d.end_time
FROM
visits v
INNER JOIN
visit_dates d ON v.uuid = d.visit_uuid
WHERE
v.study_environment_site_uuid = (SELECT
study_environment_site_uuid
FROM
visits
WHERE
uuid = 'e4663612-39f9-4c43-bd86-c4c5a9235b03')
AND v.uuid != 'e4663612-39f9-4c43-bd86-c4c5a9235b03'
AND d.start_time < (SELECT start_time FROM visit_dates WHERE visit_uuid = 'e4663612-39f9-4c43-bd86-c4c5a9235b03' ORDER BY start_time LIMIT 1)
ORDER BY d.start_time;
now trying to reflect that into Slick
(for {
vSes <- visits.filter(_.uuid === uuid)
vDate <- visitDates.filter(_.visitUuid === uuid).sortBy(_.startTime).take(1)
(v, d) <- visits join visitDates on (_.uuid === _.visitUuid)
if (v.uuid =!= uuid && v.studyEnvironmentSiteUuid === vSes.studyEnvironmentSiteUuid && d.startTime < vDate.startTime)
} yield (v.uuid)).result.map(_.headOption)
But this produced wrong result. I am using Slick 3.2.1
Following SQL is generated
SELECT
x2.`uuid`,
x7.start_time
FROM
`visits` x3,
(SELECT
`visit_uuid` AS x4, `start_time` AS x5
FROM
`visit_dates`
WHERE
`visit_uuid` = ?
ORDER BY `start_time`
LIMIT 1) x6,
`visits` x2,
`visit_dates` x7
WHERE
((x2.`uuid` = x7.`visit_uuid`)
AND ((NOT (x2.`uuid` = ?))
AND (x7.`start_time` < x6.x5)))
AND ((x3.`uuid` = ?)
AND (x2.`study_environment_site_uuid` = x3.`study_environment_site_uuid`));
Generated query is not a join and returns multiple rows instead of one that the manual query produces.
Any ideas/pointers?
As there isn't an answer yet, I will post my workaround for the problem
for {
(v, d) <- visits join visitDates on (_.uuid === _.visitUuid)
vSes <- visits.filter(_.uuid === uuid)
vDate <- visitDates.filter(_.visitUuid === uuid).sortBy(_.startTime).take(1)
if v.uuid =!= uuid && v.studyEnvironmentSiteUuid === vSes.studyEnvironmentSiteUuid && d.startTime < vDate.startTime
} yield (v.uuid, d.startTime)).result.map {
case results#(_ +: _) => Some(results.maxBy(_._2)._1)
case _ => None
}

zend_db obtain "select ..., (select ...) as x from ... "

I have a query that i can't build with Zend_Db_Select
SELECT `f`.*,
(SELECT Sum(x) AS `y`
FROM z AS pf
WHERE pf.q_id = f.id) AS w
FROM f ...
WHERE ...
GROUP BY `f`.`id`
so at the moment i'm running it manually $db->fetchAll($sql).
How do i obtain
select f.* , (select ...) as `something` from ...
I was thinking using ->column('f.*, (select...)') but it didn't work,
it could work maybe with a left join if i do (select ..., id) and then join on that id, but i wanted to obtain THIS very sql query. Is it possible?
thanks
I would recommend the JOIN. You might get better performance as sub selects are usually hard for the database to optimize. It is also easy to write this with Zend_Db_Select. Alternately new Zend_Db_Expr might work for this.
$select = $db->select()
->from('f', array('f.foo', 'f.bar', new Zend_Db_Expr('SELECT Sum(x) AS `y`
FROM z AS pf
WHERE pf.q_id = f.id') => 'f'))
->where(...);

scalaquery retrieve values

I have few tables, lets say 2 for simplicity. I can create them in this way,
...
val tableA = new Table[(Int,Int)]("tableA"){
def a = column[Int]("a")
def b = column[Int]("b")
}
val tableB = new Table[(Int,Int)]("tableB"){
def a = column[Int]("a")
def b = column[Int]("b")
}
Im going to have a query to retrieve value 'a' from tableA and value 'a' from tableB as a list inside the results from 'a'
my result should be:
List[(a,List(b))]
so far i came upto this point in query,
def createSecondItr(b1:NamedColumn[Int]) = for(
b2 <- tableB if b1 === b1.b
) yield b2.a
val q1 = for (
a1 <- tableA
listB = createSecondItr(a1.b)
) yield (a1.a , listB)
i didn't test the code so there might be errors in the code. My problem is I cannot retrieve data from the results.
to understand the question, take trains and classes of it. you search the trains after 12pm and you need to have a result set where the train name and the classes which the train have as a list inside the train's result.
I don't think you can do this directly in ScalaQuery. What I would do is to do a normal join and then manipulate the result accordingly:
import scala.collection.mutable.{HashMap, Set, MultiMap}
def list2multimap[A, B](list: List[(A, B)]) =
list.foldLeft(new HashMap[A, Set[B]] with MultiMap[A, B]){(acc, pair) => acc.addBinding(pair._1, pair._2)}
val q = for (
a <- tableA
b <- tableB
if (a.b === b.b)
) yield (a.a, b.a)
list2multimap(q.list)
The list2multimap is taken from https://stackoverflow.com/a/7210191/66686
The code is written without assistance of an IDE, compiler or similar. Consider the debugging free training :-)