Problem with two Doctrine request - symfony1

Hello I have a little problem with requests : In an action executeFiche, I have three requests
public function executeFiche(sfWebRequest $request){
// Récupération du logement correspondant à l'ID passé dans l'URL
$this->forward404Unless($this->logement = Doctrine::getTable('Logement')->find(array($request->getParameter('id'))), sprintf('Object logement does not exist (%s).', $request->getParameter('id')));
// Récupération du (ou des) locataire(s) actuel(s) du logement
$locataires = Doctrine::getTable('Logement')->createQuery('l')
->leftJoin('l.Bail b')
->leftJoin('b.Locataire')
->where('l.id = ?', $request->getParameter('id'))
->andWhere('(b.datefin >= ?', date('Y-m-d', time()))
->orWhere('b.datefin = 0000-00-00)')
->execute();
// Récupération du (ou des) locataire(s) précédent(s) du logement
$locatairesprec = Doctrine::getTable('Logement')->createQuery('l')
->leftJoin('l.Bail b')
->leftJoin('b.Locataire')
->where('l.id = ?', $request->getParameter('id'))
->andWhere('b.datefin < ?', date('Y-m-d', time()))
->andWhere('b.datefin != 0000-00-00')
->orderBy('datedeb')
->execute();
$this->locataires = $locataires;
$this->locatairesprec = $locatairesprec;
}
The problem is my two requests (the first is alright) hinder themselves and the result returned is wrong.
Edit : SQL request
SELECT l.id AS l__id, l.adresse AS l__adresse, l.montee AS l__montee, l.etage AS
l__etage, l.numetage AS l__numetage, l.numlogt AS l__numlogt, l.taille AS l__taille,
l.surfacehab AS l__surfacehab, l.typelog AS l__typelog, l.intergen AS l__intergen,
l.ascenseur AS l__ascenseur, l.ascenseuracc AS l__ascenseuracc, l.accessibl AS
l__accessibl, l.adaptable AS l__adaptable, l.adapte AS l__adapte, l.chauffage AS
l__chauffage, l.chargeschauf AS l__chargeschauf, l.chargeseauch AS l__chargeseauch,
l.chargeseaufr AS l__chargeseaufr, l.reservataire AS l__reservataire, l.loyer AS
l__loyer, l.loyercc AS l__loyercc, l.commentaires AS l__commentaires, l.created_at AS
l__created_at, l.updated_at AS l__updated_at, b.id AS b__id, b.locataire AS b__locataire,
b.logement AS b__logement, b.datedeb AS b__datedeb, b.datefin AS b__datefin, b.colloc AS
b__colloc, b.bailglissant AS b__bailglissant, l2.nud AS l2__nud, l2.titre AS l2__titre,
l2.nom AS l2__nom, l2.prenom AS l2__prenom, l2.nationalite AS l2__nationalite,
l2.datenaissance AS l2__datenaissance, l2.statutmatri AS l2__statutmatri, l2.statutpro AS
l2__statutpro, l2.nbenfants AS l2__nbenfants, l2.monoparental AS l2__monoparental,
l2.numprec AS l2__numprec, l2.rueprec AS l2__rueprec, l2.quartierprec AS l2__quartierprec,
l2.codepostalprec AS l2__codepostalprec, l2.villeprec AS l2__villeprec, l2.statutlogprec
AS l2__statutlogprec FROM logement l LEFT JOIN bail b ON l.id = b.logement LEFT JOIN
locataire l2 ON b.locataire = l2.nud WHERE (l.id = '1' AND (b.datefin >= '2010-07-01' OR
b.datefin = '0000-00-00'))
0.03s, "doctrine" connection
#
SELECT l.id AS l__id, l.adresse AS l__adresse, l.montee AS l__montee, l.etage AS
l__etage, l.numetage AS l__numetage, l.numlogt AS l__numlogt, l.taille AS l__taille,
l.surfacehab AS l__surfacehab, l.typelog AS l__typelog, l.intergen AS l__intergen,
l.ascenseur AS l__ascenseur, l.ascenseuracc AS l__ascenseuracc, l.accessibl AS
l__accessibl, l.adaptable AS l__adaptable, l.adapte AS l__adapte, l.chauffage AS
l__chauffage, l.chargeschauf AS l__chargeschauf, l.chargeseauch AS l__chargeseauch,
l.chargeseaufr AS l__chargeseaufr, l.reservataire AS l__reservataire, l.loyer AS l__loyer,
l.loyercc AS l__loyercc, l.commentaires AS l__commentaires, l.created_at AS l__created_at, l.updated_at AS l__updated_at, b.id AS b__id, b.locataire AS b__locataire,
b.logement AS b__logement, b.datedeb AS b__datedeb, b.datefin AS b__datefin, b.colloc AS
b__colloc, b.bailglissant AS b__bailglissant, l2.nud AS l2__nud, l2.titre AS l2__titre,
l2.nom AS l2__nom, l2.prenom AS l2__prenom, l2.nationalite AS l2__nationalite,
l2.datenaissance AS l2__datenaissance, l2.statutmatri AS l2__statutmatri, l2.statutpro AS
l2__statutpro, l2.nbenfants AS l2__nbenfants, l2.monoparental AS l2__monoparental,
l2.numprec AS l2__numprec, l2.rueprec AS l2__rueprec, l2.quartierprec AS l2__quartierprec,
l2.codepostalprec AS l2__codepostalprec, l2.villeprec AS l2__villeprec, l2.statutlogprec
AS l2__statutlogprec FROM logement l LEFT JOIN bail b ON l.id = b.logement LEFT JOIN
locataire l2 ON b.locataire = l2.nud WHERE (l.id = '1' AND b.datefin < '2010-07-01' AND
b.datefin != '0000-00-00') ORDER BY datedeb
EDIT
Thanks for this answer,
But when I want to put the queries in my model, I have others problems : I have an error, with '$request->getParameter('id')'. I exchange it to '$this->getId()' and Doctrine tell me I have an error.
For the parenthesis, I close them in the next. I don't know another way to generate SQL with order in the where. It is to have :
WHERE l.id = $request->getParameter('id') AND ( b.datefin >= date('Y-m-d', time()) OR b.datefin = 0000-00-00 )
Edit : I still have my problem. When the second request have something to return, the first doesn't return all the entries

As a good symfony practice, you could start by putting the queries in a model (something like LogementTable.class.php).
You have also some syntax problems in your queries.
Update : I didn't notice the parenthesis is closed on the line after
In the following line, you open the parenthesis but it isn't closed after :
->andWhere('(b.datefin >= ?', date('Y-m-d', time()))
Second error, in SQL dates must be surrounded by quotes :
->orWhere("b.datefin = '0000-00-00')")
// ...
->andWhere("b.datefin != '0000-00-00'")
Update 2 :
Try this as your second request :
$locatairesprec = Doctrine::getTable('Logement')->createQuery('l')
->leftJoin('l.Bail b')
->leftJoin('b.Locataire')
->where('l.id = ?', $request->getParameter('id'))
->andWhere('(b.datefin < ?', date('Y-m-d', time()))
->andWhere("b.datefin != '0000-00-00')")
->orderBy('datedeb')
->execute();

Related

Dynamically create query - Rails 5

If I manually write a query, it will be like
User.where("name LIKE(?) OR desc LIKE(?)",'abc','abc')
.where("name LIKE(?) OR desc LIKE(?)",'123','123')
However, I need to dynamically generate that query.
I am getting data like
def generate_query(the_query)
query,keywords = the_query
# Here
# query = "name LIKE(?) OR desc LIKE(?)"
# keywords = [['abc','abc'],['123','123']]
keywords.each do |keyword|
users = User.where(query,*keyword) <-- not sure how to dynamically add more 'where' conditions.
end
end
I am using Rails 5. Hope it is clear. Any help appreciated :)
Something like this:
q = User.where(a)
.where(b)
.where(c)
is equivalent to:
q = User
q = q.where(a)
q = q.where(b)
q = q.where(c)
So you could write:
users = User
keywords.each do |keyword|
users = users.where(query, *keyword)
end
But any time you see that sort of feedback pattern (i.e. apply an operation to the operation's result or f(f( ... f(x)))) you should start thinking about Enumerable#inject (AKA Enumerable#reduce):
users = keywords.inject(User) { |users, k| users.where(query, *k) }
That said, your query has two placeholders but keywords is just a flat array so you won't have enough values in:
users.where(query, *k)
to replace the placeholders. I think you'd be better off using a named placeholder here:
query = 'name like :k or desc like :k'
keywords = %w[abc 123]
users = keywords.inject(User) { |users, k| users.where(query, k: k) }
You'd probably also want to include some pattern matching for your LIKE so:
query = "name like '%' || :k || '%' or desc like '%' || :k || '%'"
users = keywords.inject(User) { |users, k| users.where(query, k: k)
where || is the standard SQL string concatenation operator (which AFAIK not all databases understand) and % in a LIKE pattern matches any sequence of characters. Or you could add the pattern matching in Ruby and avoid having to worry about the different ways that databases handle string concatenation:
query = 'name like :k or desc like :k'
users = keywords.inject(User) { |users, k| users.where(query, k: "%#{k}%")
Furthermore, this:
User.where("name LIKE(?) OR desc LIKE(?)",'abc','abc')
.where("name LIKE(?) OR desc LIKE(?)",'123','123')
produces a WHERE clause like:
where (name like 'abc' or desc like 'abc')
and (name like '123' or desc like '123')
so you're matching all the keywords, not any of them. This may or may not be your intent.

how to pass string value in `find_by_sql`

Using query like:
Campaign.find_by_sql("select c.*,sc.*,org.name as org_name from campaigns as c left join super_campaigns as sc ON sc.id= c.super_campaign_id left join organisations as org ON org.id= c.organisation_id where c.status=0 AND sc.status='active'")
Getting error after using sc.status='active'.Thanks.
You can achieve that by using interpolation, here is an example from a project i made for doing something similar
self.find_by_sql("SELECT s.subject_title AS subject_name,s.subject_code AS subject_code,
COUNT(*) AS total_complaints,
COUNT(CASE p.priority_name WHEN '#{Ticket::HIGH}' THEN 1 END) AS high_complaints,
COUNT(CASE p.priority_name WHEN '#{Ticket::NORMAL}' THEN 1 END) AS normal_complaints,
COUNT(CASE p.priority_name WHEN '#{Ticket::LOW}' THEN 1 END) AS low_complaints
FROM
tickets AS t
JOIN
subjects AS s
ON t.subject_id = s.id
JOIN
priorities AS p
ON t.priority_id = p.id
WHERE
p.priority_name IN ('#{Ticket::HIGH}', '#{Ticket::NORMAL}', '#{Ticket::LOW}')
GROUP BY s.subject_title, s.subject_code
ORDER BY total_complaints ASC")
As you can see #{Ticket::HIGH} is coming from PRIORITY = [HIGH = 'high', NORMAL = 'normal', LOW = 'low'] same goes for the others
Note: this is a part of the original code.

Lambda not in like SQL

I’m working with MVC 5 and I’m quite new on it. How can convert the below sql code to lambda expression. Basically what I’m trying to do is to show all records that ProductID not exist in Scrap Table
Select * from Product
where ProductID not in (Select ProductID from Scrap where ref = '123')
and active = 1
I believe your query is equivalent to:
select p.*
from Product p
join Scrap s on p.ProductID = s.ProductID
where
s.ref <> '123'
and p.active = 1
If so, try this:
from p in db.Products
join s in db.Scraps on p.ProductID equals s.ProductID
where s.Ref != "123" && p.Active == 1
select p
Try
var query =
from p in db.Products
where !(from s in db.Scrap
where s.ref == '123'
select s.ProductId)
.Contains(p.ProductId)
&& p.Active = 1
Try something like:
var products = (from p in db.Products //db is an instance of my datacontext
where !db.Scrap.Any(s => s.ProductId == p.ProductId && s.ref == "123")
&& p.active == 1 // p.active == true if active is of type bit in sql
select p);

Rails: Search for person with language skills - e,g, speaks "German AND English" on one-to-many table

This must be a basic thing in rails, but I don't know how to do it.
I would like to filter participants based on the languages they speak. People can speak multiple languages, and languages are stored in their own table with a one-to-many relationship.
Now my search looks really clunky and doesn't seem to work:
if #cvsearch.language.present? == true and #cvsearch.language != 0
#p = #p.joins(:languages).where('languages.name = ?', #cvsearch.language)
else
#cvsearch.language = 0
end
if #cvsearch.language1.present? == true and #cvsearch.language1 != 0
#p = #p.joins(:languages).where('languages.name = ?', #cvsearch.language1)
end
if #cvsearch.language2.present? == true and #cvsearch.language2 != 0
#p = #p.joins(:languages).where('languages.name = ?', #cvsearch.language2)
end
if #cvsearch.language3.present? == true and #cvsearch.language3 != 0
#p = #p.joins(:languages).where('languages.name = ?', #cvsearch.language3)
end
The resulting SQL, slightly shortened:
SELECT COUNT(*) FROM "participants" INNER JOIN "languages" ON "languages"."participant_id" = "participants"."id" WHERE (participants.id >= 2) AND (languages.name = 11) AND (languages.name = 10)[0m
It would be great to get a specific solution, but even better is a pointer as to where I can read up on this - what's the key word I am missing to describe this problem?
So this is the solution I am using for now:
if #cvsearch.language1.present? == true and #cvsearch.language1 != 0
safe_lang = ActiveRecord::Base::sanitize(#cvsearch.language1)
qry = "INNER JOIN languages l1 ON l1.participant_id = participants.id AND l1.name = " + safe_lang.to_s
#p = #p.joins(qry)
end
Works wonderfully, just need to get some feedback regarding the safety of this approach.
I'm not sure of a general reference to refer you to, but this is basic SQL stuff. Basically, the JOIN is performed first resulting in a number of rows and then the WHERE is applied, filtering the rows. The conceptual mistake here is thinking that the WHERE clause will somehow apply to the full set of matched languages, but it doesn't work that way, each row of the result is considered in isolation, therefore a clause like (languages.name = 11) AND (languages.name = 10) will never return anything, because languages.name only has a single value in each row. The query as constructed could only work for an OR clause, so you could say something like WHERE (languages.name = 11) OR (languages.name = 12).
In order to filter down the participants you need one join for each language, so you want something like this:
SELECT COUNT(*) FROM participants
INNER JOIN languages l1 ON l1.participant_id = participants.id AND (languages.name = 10)
INNER JOIN languages l2 ON l2.participant_id = participants.id AND (languages.name = 11)
WHERE participants.id >= 2
Offhand I'm not sure of the easiest way to do this in ActiveRecord, it's not a super common query. Your general structure should work, but with something like:
if #cvsearch.language1.present? == true and #cvsearch.language1 != 0
safe_language = ActiveRecord::Base.sanitize(#cvssearch.language1)
join_clause = "INNER JOIN languages l1 ON l1.participant_id = participants.id AND language.name = #{safe_language}"
#p = #p.joins(join_clause)
end

Speeding up a linq query

I am trying to replicate the following SQL query with linq. On SQL Server it takes a fraction of a second to run:
select g.reference, count(*)
from isis.dbo.[group] as g inner join
isis.dbo.enrolment as e on g.groupid = e.groupid inner join
isis.dbo.student as s on e.studentid = s.studentid inner join
isis.dbo.progression as p on s.studentid = p.studentid
where p.academicyear = '12/13' and g.istutorgroup = 1
group by reference
In my MVC application I am passing a listing of "TutorGroups" to the view. For each tutor group in the view I need to display various information about them, one item being the number of "Progression" interviews they have had.
I have tried a couple of methods but they both take upwards of 30 secs to run in my MVC application:
<%TTMrequired = tg.Enrolments
.SelectMany(e => e.Student.Progressions
.Where(p => p.TTMChecked == false &&
p.TTMInterview == true &&
p.AcademicYear == year))
.Count(); %>
and
<%TTMrequired = tg.Enrolments
.Where(e => e.Student.Progressions
.Any(p => p.TTMChecked == false &&
p.TTMInterview == true &&
p.AcademicYear == year))
.Count(); %>
Anyone got any suggestions on how I can speed this up? I suspect the problem is me trying to do it a stupid way - it usually is!
You could try doing a Sum of counts instead of a SelectMany:
tg.Enrolments.Sum(e => e.Student.Progressions
.Count(p => p.TTMChecked == false &&
p.TTMInterview == true &&
p.AcademicYear == year)
);
Should be written using this syntax:
var TTMRequired = (from g in tg.Groups
join e in tg.Enrolment on g.groupid equals e.groupid
join s in tg.Students on e.studentid equals s.studentid
join p in tg.Progressions on s.studentid = p.studentid
where p.academicyear.Equals("12/13") && g.istutorgroup.Equals(1)
group g by g.reference into grp
select new {
grpRef = grp.Key,
grpCount = grp.Count()
});
Note: if g.istutorgroup is of type BIT instead of INT, consider using .Equals(true).

Resources