I have a model called Entry that has_many :entry_states. EntryState has an attribute called :read.
I want to get all entries whether or not there is an entry_state so I think a LEFT OUTER JOIN is the best way to go see Left outer join on Coding Horror
However I also want to include an attribute of EntryState in the result. So I have a query that looks like:
entries = Entry.select('entries.*, entry_states.read').
joins("LEFT OUTER JOIN entry_states ON entries.id = entry_states.entry_id AND entry_states.user_id = #{user_id}")
The problem with this is the entry_states object is not available after this.
I can get the read state like:
entries.first.read
>> t
Which gives me the raw column value, but this is a boolean column so it would be great to get back true or false
What would be even better is if I could get the whole EntryState object. I've tried using .includes(:entry_state), but I have not found a way to include the outer join condition of AND entry_states.user_id = #{user_id}
Related
I hope what I'm asking for makes sense. I would like to have a query that chooses a specific type of Inner Join based on a user input.
This is what I have.
Queries:
qryFiltered (Main Query)
qryLand (sub)
qrySea (sub)
qryAllOrder (sub)
Tables:
tblLowPriority
Forms:
EnterWork (which has a option group called ogLandSea)
In my main query "qryFiltered", I have the following SQL code:
SELECT qryAllOrder.*
FROM
SWITCH
(Forms!EnterWork!ogLandSea = 1, (qryAllOrder LEFT JOIN tblLowPriority ON qryAllOrder.[WORK_ORDER_NBR] = tblLowPriority.[WO]) INNER JOIN qrySea ON qryAllOrder.WORK_ORDER_NBR = qrySea.WORK_ORDER_NBR,
Forms!EnterWork!ogLandSea = 2, (qryAllOrder LEFT JOIN tblLowPriority ON qryAllOrder.[WORK_ORDER_NBR] = tblLowPriority.[WO]) INNER JOIN qryLand ON qryAllOrder.WORK_ORDER_NBR = qryLand.WORK_ORDER_NBR)
WHERE tblLowPriority.WO Is Null
Basically, what I'm looking for is to choose the join based on what the user selects on the form. The inner join would choose either qrySea or qryLand based on this input. The error I'm getting is: "Syntax error in FROM clause."
What am I doing wrong here? It's the Switch function that's not working. I tried the two Land and Sea options separately without the Switch function and it works. I just can't seem to figure out a way to have the Inner Join change based on user input.
Appreciate all your responses!
I was able to get this to work by doing a Left Join from the main query to the two subqueries. And then I put a Switch criteria in the WHERE Clause:
WHERE (Switch([Forms]![EnterWork]![ogLandSea]=1,[qrySea]![WORK_ORDER_NBR] Is Not Null Or [qrySea]![WORK_ORDER_NBR] Is Null Or [qryLand]![WORK_ORDER_NBR] Is Not Null,[Forms]![EnterWork]![ogLandSea]=2,[qrySea]![WORK_ORDER_NBR] Is Not Null,[Forms]![EnterWork]![ogLandSea]=3,[qryLand]![WORK_ORDER_NBR] Is Not Null,[Forms]![EnterWork]![ogFN]=4,[qrySea]![WORK_ORDER_NBR] Is Null And [qryLand]![WORK_ORDER_NBR] Is Null))<>False)
Basically, this is what it does:
Option 1: Display all records from the Main Query
Option 2: Display only records that are in qrySea
Option 3: Display only records that are in qryLand
Option 4: Display only records that are NOT in qrySea nor qryLand
Not sure why my field in my query is getting truncated upon the return of the result. The value is being stored in the field, but gets truncated by access to help with "performance". I have reviewed multiple forums and SO posts to no avail.
Problems listed at link do not apply, Aggregation, Uniqueness, Union, Format Property, Row Source
What is wrong with my query? Instructions field in the Customer table is the one that is getting truncated.
Here is the raw query generated by access:
SELECT Task.ID, Task.TaskID, Task.TaskName, Task.TypeID, TaskType.TaskTypeName, Task.CustomerID, Customer.CustomerName, Customer.OnHold, Customer.Blacklisted, Customer.CustomerEngagementRecieved, Customer.AutoEmail, Customer.SpecialInstructions, Customer.Instructions, Task.QuoteRequired, Task.PriorityID, Priority.Priority, Task.Min, Task.Max, Task.Projected, Task.DeadlineDate, Task.ResourceID, Resource.ResourceName, Resource.Email, Resource.Extension, Task.Description, Task.StatusID, Status.Status, Task.DeveloperLog, Task.TaskPOCID, POC.Phone, POC.Email, Task.OtherPOC, Task.OtherPOCPhone, Task.OtherPOCEmail, Task.FolderPath, Task.StopBilling, Task.Premium, Task.EntryDate, Task.CompleteDate, Task.AssignedBy, Task.SettingsID, Settings.AutoEmail
FROM TaskType
INNER JOIN (Status
INNER JOIN (Settings
INNER JOIN (Resource
INNER JOIN (Priority
INNER JOIN (Customer
INNER JOIN (Task
INNER JOIN POC ON Task.TaskPOCID = POC.POCID)
ON Customer.CustID = Task.CustomerID)
ON Priority.PriorityID = Task.PriorityID)
ON Resource.ResourceID = Task.ResourceID)
ON Settings.SettingsID = Task.SettingsID)
ON Status.StatusID = Task.StatusID)
ON TaskType.TTID = Task.TypeID;
`
Have a close read of this - http://allenbrowne.com/ser-63.html something in your set up will causing the truncation.
If it's when you cut and paste the query results that can also be mis-leading. When you say a Long Text are these linked tables?
I'd also rename your Min and Max fields as they are reserved words and may cause access to think you are aggregating your data.
So from the sounds of it, Access just sometimes will ALWAYS truncate the field no matter what the settings. There is a way to force access to show the entire field though, by using the DLOOKUP() function instead of using a Control Source.
Here is the Answer to my current Issue for reference,
=DLOOKUP("Instructions", "Customer", "CustID=" & [CustomerID])
I am working on a open source ORM, and have ended up with the following generated Statement
SELECT "Task"."id"
,"Task"."title"
,"Task"."projectId"
,"Project"."id" AS "Project.id"
,"Project"."title" AS "Project.title"
,"Project"."userId" AS "Project.UserId"
,"Project.User"."id" AS "Project.User.id"
,"Project.User"."username" AS "Project.User.username"
FROM "Task" AS "Task"
LEFT OUTER JOIN ("Project" AS "Project"
INNER JOIN "User" AS "Project.User" ON "Project"."userId" = "Project.User"."id"
AND "Project.User"."username" = 'test01' ) ON "Task"."projectId" = "project"."id";
which produces the following error
no such column: Project.User.id
Thanks to help from a previous question here I was able to solve it by removing the periods from the alias inside the parentheses
SELECT "Task"."id"
,"Task"."title"
,"Task"."projectId"
,"Project"."id" AS "Project.id"
,"Project"."title" AS "Project.title"
,"Project"."userId" AS "Project.UserId"
,"Project_User"."id" AS "Project.User.id"
,"Project_User"."username" AS "Project.User.username"
FROM "Task" AS "Task"
LEFT OUTER JOIN ("Project" AS "Project"
INNER JOIN "User" AS "Project_User" ON "Project"."userId" = "Project_User"."id"
AND "Project_User"."username" = 'test01' ) ON "Task"."projectId" = "project"."id";
However, since I am working on an ORM the format of the the SQL statement is important and having the periods in those inner aliases would help a lot. I have discovered the following SQL works.
SELECT "Task"."id"
,"Task"."title"
,"Task"."projectId"
,"Project"."id" AS "Project.id"
,"Project"."title" AS "Project.title"
,"Project"."userId" AS "Project.UserId"
,"Project.User"."id" AS "Project.User.id"
,"Project.User"."username" AS "Project.User.username"
FROM "Task" AS "Task"
LEFT OUTER JOIN ("Project" AS "Project"
INNER JOIN "User" AS "Project.User" ON "Project"."userId" = "Project.User"."id"
AND "Project.User"."username" = 'test01' ) "Project.User" ON "Task"."projectId" = "project"."id";
But I do not understand why, or how to scale this to a solution that has many nested joins of a similar nature.
All columns and tables exist with the names given, and no other problems are getting in the way (I assume this mainly based on the failure of the first example and the success of the second.) The initial error only happens in sqlite, all over dialects I have tried seem to not have an issue.
My question is, a, why does the third example work and, b, would it scale to even deeper joins with aliases with periods?
I am using Rails 3 with ActiveRecord and I cannot get it to generate the query I want without inserting almost plain SQL in it.
I simply want to execute this SQL query (actually the query is a little more complex but it's really this part that I cannot get right).
SELECT DISTINCT users.*, possible_dates.*
FROM users
LEFT OUTER JOIN possible_dates
ON possible_dates.user_id = users.id
AND possible_dates.event_id = MY_EVENT_ID;
which I managed to using
User.includes(:possible_dates)
.joins("LEFT OUTER JOIN possible_dates
ON possible_dates.user_id = users.id
AND possible_dates.event_id = #{ActiveRecord::Base.sanitize(self.id)}"
)
.uniq
but I feel that I am missing something to simply add the AND condition of the left join using ActiveRecord query methods.
I also tried to do something like this
User.includes(:possible_dates)
.where('possible_dates.event_id' => self.id)
.uniq
but this yields (as expected), a query with a WHERE clause at the end and not the AND clause I want on the join.
By the way, self in the two snippets above is an instance of my Event class.
Thank you for your help.
(1 year later...)
After looking around, I found out that the best was probably to use arel tables.
For the above example, the following code would work.
ut = User.arel_table
pt = PossibleDate.arel_table
et = Event.arel_table
User.joins(
ut.join(pt, Arel::Nodes::OuterJoin)
.on(pt[:user_id].eq(u[:id])
.and(pt[:event_id].eq(1))
).join_sql
).includes(:possible_dates).uniq
the 1 in eq(1) should be replaced by the correct event id.
I have a question about joins in NHIBERNATE. We had an issue with our sql query that was generated but nhibernate. Our db developer optimized the raw sql so it works as we need, but we need to change the nhibernate code to make generated sql look like optimized.
the part of the original part of the query is:
FROM PERSON_VISIT this_
inner join PERSON_Basic per2_
on this_.PERSON_ID = per2_.PERSON_ID
left outer join PERSONC_QUESTIONS perint10_
on per2_.PERSON_ID = perint10_.PERSON_ID
left outer join TELEPHONE_QUESTIONS intaudit13_
on perint10_.PP_QUESTIONS_ID = intaudit13_.PP_QUESTIONS_ID
inner join C_QUESTIONS intdef14_
on perint10_.QUESTION_ID = intdef14_.QUESTION_ID
and perint10_.QUESTIONS_CODE = intdef14_.QUESTIONS_CODE
and perint10_.QUESTION_ID = intdef14_.QUESTION_ID
The optimized one is :
FROM PERSON_VISIT this_
inner join PERSON_Basic per2_
on this_.PERSON_ID = per2_.PERSON_ID
left outer join PERSONC_QUESTIONS perint10_
on per2_.PERSON_ID = perint10_.PERSON_ID
left outer join TELEPHONE_QUESTIONS intaudit13_
on perint10_.PP_QUESTIONS_ID = intaudit13_.PP_QUESTIONS_ID
left outer join C_QUESTIONS intdef14_
on perint10_.QUESTION_ID = intdef14_.QUESTION_ID
and perint10_.QUESTIONS_CODE = intdef14_.QUESTIONS_CODE
and perint10_.QUESTION_ID = intdef14_.QUESTION_ID
and intdef14_.DISCIPLINE_CODE = this_.DISCIPLINE_CODE
To change query from inner join to left outer join is easy, i changed only one line of code:
.CreateAlias("PersonInt.QuestionEntity", "IntDef", JoinType.LeftOuterJoin)
But how I can add
and intdef14_.DISCIPLINE_CODE = this_.DISCIPLINE_CODE
using nhibernate code?
There is an option to add reference from PERSON_VISIT definition to C_QUESTIONS, but the problem is that
PERSON_VISIT is used everywhere and I don't want this change to possibly break other queries, I just wnat to add only one line of code to add, how I can do that? Is there any way to have access to the raw join to change it? Or some other way to add this
and intdef14_.DISCIPLINE_CODE = this_.DISCIPLINE_CODE
To the query?
I know that somebody will say that we can add a restriction to the query through criteria.Add, but it is not an option cause db developer optimized our query taking this restriction from WHERE clause to the join.
How I can do that quickly without changing the models definitions? Just changing only this one query without changing the whole model?
It is possible using HQL and the Criteria API's.
This question gives you the answer: Adding conditionals to outer joins with nhibernate
Something like this may solve your issue.
.CreateAlias("PersonInt.QuestionEntity", "IntDef", JoinType.LeftOuterJoin,
Restrictions.EqProperty("DISCIPLINE_CODE", "IntDef.DISCIPLINE_CODE"))
Thanks for answers. We use 2.0 version of NHibernate in our project so we didn't have a chance to use new methods of .CreateAlias with restrictions.
I have fixed an issue using Interceptors:
public class SqlInterceptor : EmptyInterceptor, IInterceptor
{
SqlString IInterceptor.OnPrepareStatement(SqlString sql)
{
//manipulating with the sql
return sql;
}
}
than
var factory = Session.SessionFactory;
var session = factory.OpenSession(new SqlInterceptor());
And use my query without a change.