Room Dao Left Outer Join sets parentId to 0 when join does not find children - sqlite

I have a standard outer join in Room Dao which strangely ends up setting parent joinId (bakeId) to 0 whenever join didn't find any child rows.
Is this a room bug or a normal behavior? Any idea how to get proper joinId (bakeId), without changing the schema?
#Query(
"SELECT * FROM ${Bake.tableName} " +
"left outer JOIN ${Ingredient.tableName} ON ${Bake.tableName}.${Bake.Columns.bakeId} " +
"= ${Ingredient.tableName}.${Ingredient.Columns.bakeId}" +
" ORDER BY ${Bake.Columns.bakeId} DESC , ${Bake.Columns.startTime} DESC"
)
fun getBakesFlow(): Flow<Map<Bake, List<Ingredient>>>
The problem I think is that both parent and child tables have the join column named exactly the same

Looks like this is a bug with version "2.4.3", I updated room to "2.5.0-beta02" and it's no longer happening.

Related

How to select specific properties of properties through DQL

I have the following Entities. First and foremost: the design derived from a legacy DB and it has been semplified here for clarity shake.
What I would like to do is selecting all the widgets along with their varsSelection populated (in this very moment Widget only contains PhyVarSelection instances so we can focus on them) which should have teir phyVar hydrated. Phyvar must not be populated with its ewCfgVar property.
I'm trying to do it by using DQL. I succeeded to select all the widgets and their relative varsSelection, but I'm not able to populate their relative phyVar. Is that possible? Here is the DQL I'm using:
$sql = <<<EOS
SELECT wid, partial phyvarsel.{id, phyVar, start}
FROM Belka\\TsBundle\\Entity\\Widget wid
LEFT JOIN Belka\\TsBundle\\Entity\\PhyVarSelection phyvarsel WITH wid.id = phyvarsel.widget
LEFT JOIN Belka\\TsBundle\\Entity\\PhyVar phyvar WITH phyvarsel.phyVar = phyvar.id
EOS;
$query = $this->getEntityManager()->createQuery($sql);
If I dump the result, Widget and varsSelection are correctly populated, but PhyVarSelection::phyVar is set to NULL.
The query DQL it generates is actually correct, and if I query it I get all the PhyVar's properties:
die(var_dump($query->getSQL()));
It generates:
SELECT a0_.id AS id0, a0_.title AS title1, a0_."order" AS order2, a0_.span_cols AS span_cols3, a0_.description AS description4, a1_.id AS id5, a1_.start AS start6, a2_.id AS id7, a0_.part_of_section AS part_of_section8, a1_.vartype AS vartype9, a1_.part_of_widget AS part_of_widget10, a1_.phy_var_sel AS phy_var_sel11, a2_.vartype AS vartype12, a2_.part_of_phy_meter AS part_of_phy_meter13, a2_.varname AS varname14, a2_.id_device AS id_device15 FROM app_t.widget a0_ LEFT JOIN app_t.var_selection a1_ ON (a0_.id = a1_.part_of_widget) AND a1_.vartype IN ('phy') LEFT JOIN app_t.variable a2_ ON (a1_.phy_var_sel = a2_.id) AND a2_.vartype IN ('phy');
I don't paste here the Entities' code but if you need it let me know with a comment below and I will update the question.
Update
I've also tried the following:
$sql2 = <<<EOS
SELECT phyvarsel, phyvar
FROM Belka\\TsBundle\\Entity\\PhyVarSelection phyvarsel
LEFT JOIN Belka\\TsBundle\\Entity\\PhyVar phyvar
WHERE phyvarsel.id = :phyvarselid
EOS;
$query2 = $this->getEntityManager()->createQuery($sql2);
$query2->setParameter('phyvarselid', '0');
$query2->setHint(Query::HINT_FORCE_PARTIAL_LOAD, true);
$varSel = $query2->getResult();
again, in this case I get PhyVarSelection but its attribute phyVar is still set to NULL.

Allowing additional data to be added after a join with blank rows

I've got a left join query that returns the correct number of rows (equal to the left table), but because the right table is empty, there are no corresponding rowid's. How can I add the rowid (RecNum should be the same value) to the empty table so that I can add data to the resulting dataset? Here's the query:
select
Week01.RecNum as RecNum,
Week01.UserName as UserName,
Week01.Day1Reg as Day1Reg,
Week01.Day1OT as Day1OT,
Week01.Day2Reg as Day2Reg,
Week01.Day2OT as Day2OT,
Week01.Day3Reg as Day3Reg,
Week01.Day3OT as Day3OT,
Week01.Day4Reg as Day4Reg,
Week01.Day4OT as Day4OT,
Week01.Day5Reg as Day5Reg,
Week01.Day5OT as Day5OT,
Week01.Day6Reg as Day6Reg,
Week01.Day6OT as Day6OT,
Week01.Day7Reg as Day7Reg,
Week01.Day7OT as Day7OT
from
"Project List"
left join
Week01
on
"Project List".RecNum = Week01.RecNum
and
Week01.UserName = "JustMe"
or, if that can't really be done, how about a query that will pre-create row entries in Week01 (if they don't already exist) to match the same RecNum column and fill in the username with "JustMe"? But, the thing is that if the RecNum already exists in Week01, it can't be overwritten. Any ideas on that? If that could be done, then the query above should work out just fine.
you can actually select it using IFNULL
SELECT
IFNULL(Week01.RecNum, "Project List".RecNum
..
To answer your question of getting all recNums that exists in project List but not in Week01
you can do use left join
select p.RecNum
from "Project List" p
left join Week01 w
on p.RecNum = w.RecNum
where w.RecNum is NULL
or you can use not exists clause
select p.RecNum
from "Project List" p
where not exists
( select 1 from Week01 w
where p.RecNum = w.RecNum
)

SQLite/JDBC inner join

I have found what appears to be a bug in the SQLite JDBC driver, but I thought I'd see if someone could spot any boneheaded errors on my part. I have the following query:
SELECT
SKU_ATTR_VALUE.*,
Product.ProductID
FROM
SKU_ATTR_VALUE
INNER JOIN SKU
ON SKU_ATTR_VALUE.SkuID=SKU.SkuID
INNER JOIN Product
ON SKU.ProductID=Product.ProductID
WHERE Product.ProductID=?
Pretty simple. I can run this in the SQLite database browser, replacing the ? with 1, and it returns 18 rows, which is just what it should do. Only 18 rows match the condition. But when I run this in Java, and pass in the value 1, I get 817 values back. And that's not a Cartesian join; there are 864 possible values in SKU_ATTR_VALUE. The results I get back have at least one value for each record in Product too...so I really can't imagine what is happening.
I've been looking at this a while and I'm completely stumped. Googling it doesn't seem to turn anything up. Yes, I'm sure that I'm running the Java query against the same SQLite database as in the SQLite browser.
The name of the SQLite jar is sqlitejdbc-v056.jar. It is based on SQLite 3.6.14.2.
Here is the Java code that sets up the query:
String sql = "SELECT SKU_ATTR_VALUE.*, Product.ProductID " +
"FROM SKU_ATTR_VALUE " +
" INNER JOIN SKU ON SKU_ATTR_VALUE.SkuID=SKU.SkuID " +
" INNER JOIN Product ON SKU.ProductID=Product.ProductID " +
"WHERE Product.ProductID=?";
ps = conn.prepareStatement(sql);
ps.setInt(1, productID);
ResultSet rs = ps.executeQuery();
According to this document section "5.0 Joins" : you can try to rewrite your query like this :
SELECT
SKU_ATTR_VALUE.*,
Product.ProductID
FROM
Product, SKU, SKU_ATTR_VALUE
WHERE
Product.ProductID=?
AND SKU.ProductID=Product.ProductID
AND SKU_ATTR_VALUE.SkuID=SKU.SkuID

JOIN for Membership table

Why Microsoft uses stupid examples with GroupBy clause only for one table?
http://msdn.microsoft.com/en-us/library/bb896341.aspx
And why error is causing when i tried to Select some field that is not in GroupBy?
Error says that fields that is not in GroupBy clause doesn't exist in current context.
using (Entities db = new Entities())
{
var query = "SELECT RLS.RoleId, UIR.UserId, UIR.RoleId, USR.UserName FROM Entities.aspnet_Roles AS RLS " +
"INNER JOIN Entities.vw_aspnet_UsersInRoles AS UIR " +
"ON RLS.RoleId = UIR.RoleId " +
"INNER JOIN Entities.aspnet_Users AS USR " +
"ON UIR.UserId = USR.UserId " +
//"GROUP BY RLS.RoleId" // is uncomment - it says UserId doesn't exist in context
;
var x = new ObjectQuery<DbDataRecord>(query, db);
var y = x.ToTraceString().Replace("\n", " ").Replace("\t", " ").Replace("\r", " ");
}
The same for LINQ:
var x = from RLS in db.aspnet_Roles
join URS in db.vw_aspnet_UsersInRoles
on RLS.RoleId equals URS.RoleId
join USR in db.aspnet_Users
on URS.UserId equals USR.UserId
group RLS by RLS.RoleId into GRP
select new
{
GRP.Key
};
The question is: how to select all fields and GroupBy selection by only one field?
Thanks in advance.
All fields that are not in the GROUP BY clause have to be aggregate expressions such as AVG(...) or SUM(...) or MAX(...) etc.
So this is wrong:
SELECT RLS.RoleId, UIR.UserId, UIR.RoleId, USR.UserName
(...)
GROUP BY RLS.RoleId
because the last three expressions in the SELECT should either be aggregated somehow or added to the GROUP BY clause.
This is logical because by specifying GROUP BY RLS.RoleId you say that RLS.RoleId should be unique in the end result, so only one row per value. But for each RLS.RoleID there could be multiple values for UIR.UserId, UIR.RoleId or USR.UserName. So you need to tell the DBMS what to do to make them into one row: average, maximum, minimum... OR you add fields to the GROUP BY clause, so that not RLS.RoleID but the combination of column values should be unique. See also this example.
I don't see any aggregate functions (COUNT, SUM, AVG, etc) being used in the query you posted. Thus, there's no need to include a GROUP BY expression, and there's really no reason to do so. Could you please explain what you're trying to accomplish?
I think i didn't understand littlegreen.
His advice really helped ... though i was upset by SQL Server comparing to MySQL (there is enough to set one grouping field).
Works!
SELECT DISTINCT
RLS.RoleId,
COUNT(RLS.RoleId) AS RID_CNT,
MAX(CAST(UIR.UserId AS VARCHAR(36))) AS EXPR1,
MAX(CAST(USR.UserName AS VARCHAR(36))) AS EXPR2
FROM aspnet_Roles AS RLS
INNER JOIN vw_aspnet_UsersInRoles AS UIR ON RLS.RoleId = UIR.RoleId
INNER JOIN aspnet_Users AS USR ON UIR.UserId = USR.UserId
GROUP BY RLS.RoleId

NHibernate Collection Left Outer Join Where Clause Issue

It seems that when using the following NHibernate query, I do not get a root entity when the left outer join has no records.
ICriteria critera = session.CreateCriteria(typeof(Entity));
criteria.CreateCriteria("SubTable.Property", "Property", NHibernate.SqlCommand.JoinType.LeftOuterJoin);
criteria.Add(Expression.Not(Expression.Eq("Property", value)));
The SQL that I am trying to generate is:
SELECT * FROM BaseTable
LEFT JOIN (
SELECT * FROM SubTable
WHERE Property <> value
)Sub ON Sub.ForeignKey = BaseTable.PrimaryKey
Notice that the where clause is inside the left join's select statement. That way if there arent any maching sub records, we still get a top level record. It seems like NHibernate is producing the following SQL.
SELECT * FROM BaseTable
LEFT JOIN (
SELECT * FROM SubTable
)Sub ON Sub.ForeignKey = BaseTable.PrimaryKey
WHERE Sub.Property <> value
Is there anyway to achieve that first piece of SQL? I have already tried:
ICriteria critera = session.CreateCriteria(typeof(Entity));
criteria.CreateCriteria("SubTable.Property", "Property", NHibernate.SqlCommand.JoinType.LeftOuterJoin);
criteria.Add(
Restrictions.Disjunction()
.Add(Expression.IsNull("Property"))
.Add(Expression.Not(Expression.Eq("Property", value)));
I am looking for a solution using the Criteria API.
Try this:
var hql = #"select bt
from BaseTable bt
left join bt.SubTable subt
with subt.Property <> :property";
Or perhaps:
var hql = #"select bt
from BaseTable bt
left join bt.SubTable subt
where subt.ForeignKey = bt.PrimaryKey
and subt.Property <> :property";
Finally:
var result = session.CreateQuery(hql)
.SetParameter("property", "whateverValue")
.List<BaseTable>();
I don't use nHibernate but I think this is the SQL you need to generate:
SELECT *
FROM BaseTable
LEFT JOIN SubTable sub
ON Sub.ForeignKey = BaseTable.PrimaryKey and sub.Property <> value
What you want isn;t a where clasue but an additional condition on the join. Hope that helps.

Resources