Using a join statement in Doctrine when no parameters are required - sql

I am trying to add a join statement to my DQL in my search functionality, as one of the fields that a user can search is actually linked with another table, notably a customer name which is only referenced in the root table as an id.
I have seen how the join syntax should work on the Doctrine site (example only below):
$qb->join('u.Group', 'g', 'WITH', 'u.status = ?1', 'g.id')
but I don't want to match a specific value, I only want to join it the way I would in mySQL as follows:
...JOIN table2 ON table2.id = table1.existingCustomer
Here is my current search DQL, where m represents a table called Message and c represents a table called Customer:
$qb = $this->createQueryBuilder('m');
$qb->select('m');
$qb->where('c.firstName LIKE :s');
$qb->orWhere('c.surname LIKE :s');
$qb->orWhere('m.postcode LIKE :s');
$qb->orWhere('m.town LIKE :s');
$qb->orWhere('m.phone1 LIKE :s');
$qb->andWhere('m.archived = :archived');
$qb->orderBy('m.messageDate', 'DESC')
->setParameter('s', '%'.$s.'%')
->setParameter('archived', 0);
return $qb->getQuery()->getResult();
As you can see there is reference to c which is meant to be the alias for the Customer table, as the Message table ONLY uses an id to cross reference this.
What I need to know is, is there a way to join the Customer table by linking the existingCustomer field in the Message table to the id field of the Customer table without having to match a specific value in order to make this work?

Did you try this:
us \Doctrine\ORM\Query\Expr\Join;
// ...
->leftJoin('YourBundle:Table2', 'table2', Join::WITH, 'table2.id = table1.existingCustomer')

Related

SQL select with three tables

Hi guys I'm new with databases and I'm trying to make a query where I join 3 tables. I could make it and I want to clean up the result. I want to know how can I delete the column "pin" from users table and maybe some "ids" columns.
Select * from "wish-list"
Join products
On "wish-list".id = products.holiday_id
Join users
On "wish-list".user_id = users.id
Where "wish-list".id = 1
You need to specify which columns you really need in your output. At the moment you are using
SELECT * which outputs all columns of all joined tables.
Here is what it should look like:
SELECT holiday, products.description, users.pin FROM "wish-list"
JOIN products ON "wish-list".id = products.holiday_id
JOIN users ON "wish-list".user_id = users.id
WHERE "wish-list".id = 1
It's important that you reference all columns which are not your main entity (here wish-list) with tablename.column (products.description and not only description). It will work without referencing strictly but only if the column name is unique in your query.
Furthermore you can rename columns. This is useful for example if you want to get the id's of the product table and the wish-list table.
SELECT product.id AS product_id, id AS wishlist_id FROM "wish-list"
...
Hope that helps!

How to implement EVERY and NO operations for a joined 'list' table

I have two tables Person and _Person_Name which contains a column Name and a column Owner with Person.Id as foreign key. I'm looking for two search operations I'd call EVERY and NO.
1.) Every
The following returns only the Person IDs for which all corresponding names match for query LIKE '%n%':
SELECT Person.Id as Result FROM Person
INNER JOIN _Person_Name AS __T1 ON Person.Id = __T1.Owner
WHERE __T1.Name LIKE '%n%'
GROUP BY Result
HAVING Count(Result)=(Select Count(*) FROM _Person_Name
WHERE Person.Id=_Person_Name.Owner)
But the problem is that I also have to deal with other queries for which just a single match suffices, and the HAVING clause applies to all terms in the WHERE clause.
Is there a way to get the same effect as in the HAVING clause in the above query, but somehow express this within the WHERE clause such that other, ordinary conditions can be added to it?
Example:
SELECT Person.Id as Result FROM Person
INNER JOIN _Person_Name AS __T1 ON Person.Id = __T1.Owner
WHERE (EVERY __T1.Name LIKE '%n%')
OR (__T1.Name LIKE 'John')
The second disjunct should just behave in the ordinary way without any restrictions. Now I'm looking for a way to express EVERY like in the above HAVING clause. The query should return __T1.Owner (=Person.Id) whenever it is the case that one field with matching owner has a name field __T1.Name LIKE 'John' or it is the case that all __T1.Name fields with matching owner contain an 'n'.
If it is not possible to express this in the WHERE clause, how can it be expressed?
2.) No
The NO search operation is just like EVERY but the base condition is negated, i.e., I'm looking for the Persons for which none of their associated Name parts matches the query. I suppose I can get that easily if I have a solution for EVERY.
Let me start with an idea for NO: you can check if no row exists if you look up by condition LIKE '%n%'. You can do it in the WHERE clause with EXISTS subquery:
WHERE
NOT EXISTS (
SELECT 1 FROM _Person_Name AS __T2 WHERE Person.Id = __T2.Owner
and __T2.Name LIKE '%n%'
)
OR (__T1.Name LIKE 'John')
NOT EXISTS checks if there is NO match.
Based on this the implementation for EVERY is to see if the negation of your condition for no row is true, so you are looking for NOT LIKE '%n%':
WHERE
NOT EXISTS (
SELECT 1 FROM _Person_Name AS __T2 WHERE Person.Id = __T2.Owner
and __T2.Name NOT LIKE '%n%'
)
OR (__T1.Name LIKE 'John')
This time NOT EXISTS will make sure no rows found that does NOT match your criteria - therefore EVERY row matched it.

Updating column values in a table based on join with another table?

I have two tables called resource and resource_owners.
The resource_owners table contains two columns called resource_id and owner_id.
resource_id | owner_id |
-------------+-----------
The resource table contains two relevant columns: parentresource_id and id.
parentresource_id | id |
-------------------+------
resource_owners.resource_id, resource.id and resource.parentresource_id are all join columns between the two tables. Now what I want to do is the following:
For every row in the resource table, take the value in id, match it with a corresponding resource_owners.resource_id, retrieve the corresponding resource_owners.owner_id value (call it $owner_value), then set resource_owners.owner_id to $owner_value where resource_owners.resource_id equals resource.parentresource_id.
In conversational terms, this is what I want to do: For each resource, I want to re-assign the parent-resource's owner_id to be the resource's owner_id.
I've tried to wrap my head around this problem and it looks like I'll need two different table joins (resource.id with resource_owners.resource_id and resource.parentresource_id with resource_owners.resource_id).
Can someone point me in the right direction? Is what I want even possible with a single query? I'm okay with a PostgreSQL script as well if that works better for my use case.
I'm not sure what database you are using but you should be able to accomplish using the logic below if I understood your question correctly:
UPDATE RESOURCE_OWNER SET
OWNER_ID = UP.OWNER_ID
FROM (SELECT rc.ID, TMP.OWNER_ID FROM (SELECT RSC.ID, ROWRS.OWNER_ID, ROWRS.RESOURCE_ID FROM RESOURCE RSC JOIN RESOURCE_OWNER ROWRS
ON RSC.ID = ROWRS.RESOURCE_ID) TMP JOIN RESOURCE rc on rc.PARENTRESOURCE_ID = TMP.RESOURCE_ID) UP WHERE RESOURCE_OWNER.RESOURCE_ID = UP.ID;

SQL query multiple tables in database

I need to query 3 tables from a database.
The table Client includes client id no. and client name.
The table Property includes property id no. and property name amongst other things.
The table clientsInterestedInProperties includes client id no. property id no. and date of visit to property.
I want to list the name of clients who have an interest in a specific property (with the name, not id. no) and the date they visited the property.
For example say the property is called Barker Hall, who is interested in it and when did they visit?
Can anyone help?
First, use main filtering condition in your from table. So you must start with
select ... from Property ... ,
Then connect visitations to the property. It will generate different row for each visit, with
LEFT JOIN to get visitation to the property
Then we need to get readably client names, not IDs, so we do
LEFT JOIN to get client names
And some additional filtering/conditions can be added with
WHERE clauses ( or INNER JOIN ) , like visiting period.
Example SQL for your case ( i don't know your table/column names )
select PropertyName, ClientsInProp_VisitDate, ClientName
from Property
left join ClientsInProp on ClientsInProp_Propertyid = Property_PropertyID
left join Client on Client_Clientid = ClientsInProp_ClientId
where Proprty_Name = 'House 1' and ClientsInProp_VisitDate > '01.10.2013'
Cheers !
What you want is an IN clause:
SELECT * FROM Client
WHERE ClientId IN
(SELECT ClientID
FROM clientsInterestedInProperties cip
INNER JOIN Property p
ON cip.PropertyID = p.PropertyID
WHERE p.ProperyName = #propertyName)

Update values in each row based on foreign_key value

Downloads table:
id (primary key)
user_id
item_id
created_at
updated_at
The user_id and item_id in this case are both incorrect, however, they're properly stored in the users and items table, respectively (import_id for in each table). Here's what I'm trying to script:
downloads.each do |download|
user = User.find_by_import_id(download.user_id)
item = item.find_by_import_id(download.item_id)
if user && item
download.update_attributes(:user_id => user.id, :item.id => item.id)
end
end
So,
look up the user and item based on
their respective "import_id"'s. Then
update those values in the download record
This takes forever with a ton of rows. Anyway to do this in SQL?
If I understand you correctly, you simply need to add two sub-querys in your SELECT statement to lookup the correct IDs. For example:
SELECT id,
(SELECT correct_id FROM User WHERE import_id=user_id) AS UserID,
(SELECT correct_id FROM Item WHERE import_id=item_id) AS ItemID,
created_at,
updated_at
FROM Downloads
This will translate your incorrect user_ids to whatever ID you want to come from the User table and it will do the same for your item_ids. The information coming from SQL will now be correct.
If, however, you want to update the tables with the correct information, you could write this like so:
UPDATE Downloads
SET user_id = User.user_id,
item_id = Item.item_id
FROM Downloads
INNER JOIN User ON Downloads.user_id = User.import_id
INNER JOIN Item ON Downloads.item_id = Item.import_id
WHERE ...
Make sure to put something in the WHERE clause so you don't update every record in the Downloads table (unless that is the plan). I rewrote the above statement to be a bit more optimized since the original version had two SELECT statements per row, which is a bit intense.
Edit:
Since this is PostgreSQL, you can't have the table name in both the UPDATE and the FROM section. Instead, the tables in the FROM section are joined to the table being updated. Here is a quote about this from the PostgreSQL website:
When a FROM clause is present, what essentially happens is that the target table is joined to the tables mentioned in the fromlist, and each output row of the join represents an update operation for the target table. When using FROM you should ensure that the join produces at most one output row for each row to be modified. In other words, a target row shouldn't join to more than one row from the other table(s). If it does, then only one of the join rows will be used to update the target row, but which one will be used is not readily predictable.
http://www.postgresql.org/docs/8.1/static/sql-update.html
With this in mind, here is an example that I think should work (can't test it, sorry):
UPDATE Downloads
SET user_id = User.user_id,
item_id = Item.item_id
FROM User, Item
WHERE Downloads.user_id = User.import_id AND
Downloads.item_id = Item.import_id
That is the basic idea. Don't forget you will still need to add extra criteria to the WHERE section to limit the rows that are updated.
i'm totally guessing from your question, but you have some kind of lookup table that will match an import user_id with the real user_id, and similarly from items. i.e. the assumption is your line of code:
User.find_by_import_id(download.user_id)
hits the database to do the lookup. the import_users / import_items tables are just the names i've given to the lookup tables to do this.
UPDATE downloads
SET downloads.user_id = users.user_id
, downloads.item_id = items.items_id
FROM downloads
INNER JOIN import_users ON downloads.user_id = import_users.import_user_id
INNER JOIN import_items ON downloads.item_id = import_items.import_item_id
Either way (lookup is in DB, or it's derived from code), would it not just be easier to insert the information correctly in the first place? this would mean you can't have any FK's on your table since sometimes they point to one table, and others they point to another. seems a bit odd.