codeigniter active record join 2 databases - codeigniter

Can I use a $this->db->join('database.table') without inserting the second parameter of this function? I need to connect 2 databases, but they dont have nothing that binds them. I know that it works with SQL because I have tried it, however I want to change the model using active record, because I find it more flexible etc.. but I dont know how to insert the join
$this->db->select('users.user_id');
$this->db->distinct();
$this->db->from('users');
$this->db->join('user_detail', 'users.user_id = user_detail.id', 'left');
and now something like:
$this->db->join('cities.city');
So the SQL should be like:
SELECT DISTINCT users.user_id FROM users LEFT JOIN .... JOIN cities.city
Instead of:
SELECT DISTINCT users.user_id FROM users LEFT JOIN .... JOIN cities.city ON
What I actually have.
Please help. Thank you.

You cannot. If you look at the CI core files, you will see that ON will always be passed. I didn't realize this was the case until I checked the system files.
$join = $type.'JOIN '.$this->_protect_identifiers($table, TRUE, NULL, FALSE).' ON '.$cond;
Reference in /system/database/DB_active_rec.php line 340. Even in the latest version (GitHub) this is still the case, although either ON or USING() will be used.
Your best bet is to just use $this->db->query():
$this->db->query('SELECT DISTINCT users.user_id FROM users LEFT JOIN .... JOIN cities.city');
Update: Since you are using 2.1.3, you can hack the core files (since I do not believe there is a way to extend CI active records). Although you shouldn't edit core files on a normal basis, at least this will give you what you need. Open /system/database/DB_active_rec.php:
Change line 310 to:
public function join($table, $cond, $type = '', $use_on = TRUE)
Change line 340 to (which now becomes 2 lines):
$on_cond = ($use_on === TRUE) ? ' ON '.$cond : NULL;
$join = $type.'JOIN '.$this->_protect_identifiers($table, TRUE, NULL, FALSE).$on_cond;
Now in your model when you call your join() pass a 4th parameter, FALSE, to turn off the ON:
$this->db->join('cities.city', NULL, '', FALSE);

Related

Doctrine where condition for many to one

I have a many to one association mapping where many Exams have one subject
If I have to update marks property of all subject with exam id xxxx
I use
Update namespace/Exam e set e.marks = 4 where e.subject.id = xxxx
it is not working
can you please he
help me to do that in a proper way ?
You can not access e.subject.id without a join in your query. But a join is not possible in a Doctrine update statement, since its MySQL specific.
What should work is to compare the whole entity:
$subject = $this->getEntityManager()->getRepository('namespace\subject')
->find(xxx);
$query = $this->getEntityManager()->createQueryBuilder()
->update('namespace\Exam', 'e')
->set('e.marks', '4')
->where('e.subject = :subject')
->setParameter('subject', $subject);
Or, if you don't want to have another database query and the primary key of your subject entity consists only of one column (e.g. id), you could also compare with this primary key:
$query = $this->getEntityManager()->createQueryBuilder()
->update('namespace\Exam', 'e')
->set('e.marks', '4')
->where('e.subject = :id')
->setParameter('id', xxx);
Also have a look at this question, the user wasn't able to join in his update statement too.
Edit: added more general approach

How To Use Magento Collections to Run Query, Get Results, and Print Them

I am fairly new to programming, php, Magento, and most of all, SQL. Please forgive me if this is somehow a dumb question.
I am trying to use Magento collections to select two different columns in two different tables and join them. These two columns contain product numbers that follow the same conventions and my goal is to get and display the product numbers that field a (lets call it 'product_id') contains that field b ('item_nr') does not.
Here is my function so far, located currently in a model that will be called by a controller action.
public function importCompare() {
$orderlistCollect = Mage::getModel('personal_orderlist/orderlist')->getCollection()
->addFieldToSelect('product_id')
->addFieldToFilter('b.item_nr', null)
->getSelect()
->joinLeft( array('b'=>$this->getTable('catalog/product')), 'main_table.product_id = b.item_nr', array('b.item_nr'));
echo $orderlistCollect;
}
By echoing the variable, I get the following query.
SELECT `main_table`.`product_id`, `b`.`erp_item_nr`
FROM `mag1personal_orderlist` AS `main_table`
LEFT JOIN `` AS `b` ON main_table.product_id = b.item_nr
WHERE (b.item_nr = '')
This looks fairly close to what I want, however the big problem is that I have no idea how to retrieve the information I should derive from this query and echo it. I've tried to use a variety of collection manipulation methods to no avail. Is it easy to use them out of order as well?
Any help here is appreciated.
Collections use interfaces from PHP's standard library to implement for each-able behaviors.
If that sounded like greek, give this a try.
foreach($orderlistCollect as $item)
{
$data = $item->getData();
var_dump($data);
$sku = $item->getSku();
var_dump($sku);
$sku = $item->getData('sku');
var_dump($sku);
}
If you're interested in learning how to do with with your own PHP objects, the Object Iteration section of the manual is a good place to start. However, there's no need to dive deep on this — just treat a collection like you would an array of objects, and you'll be fine.

ObjectQuery lambda expression for select inside select

i want to create query like this using lambda expression in ObjectQuery
SELECT *
FROM tableA
WHERE ID in (SELECT ID in tableB)
I try using
var histories = _entities.ProductViewHistories.Take(5).OrderByDescending(p=>p.DateViewed);
var products = _entities.Products.Where(p => p.ID in histories );
but it's not working.
Can someone point me to the right direction?
Thanks
Sure - "in" isn't part of C# in that way. Try this though:
var products = _entities.Products.Where(p => histories.Contains(p.ID));
Contains is the appropriate method to detect whether a value is within another collection. However, you may find it would be more appropriate to use a join here.
Also, I'm concerned about this query:
That doesn't represent the 5 most recently viewed histories - it represents "some arbitrary 5 view histories" (the first 5 returned in some indeterminate order) then arranged in most-recent-first order.
I suspect you actually want:
var histories = _entities.ProductViewHistories.OrderByDescending(p=>p.DateViewed)
.Take(5);

Preventing Doctrine's query cache in Symfony

In my Symfony/Doctrine app, I have a query that orders by RANDOM(). I call this same method several times, but it looks like the query's result is being cached.
Here's my relevant code:
$query = $table->createQuery('p')
->select('p.*, RANDOM() as rnd')
->orderBy('rnd')
->limit(1)
->useQueryCache(null)
->useResultCache(null);
$result = $query->fetchOne();
Unfortunately, the same record is returned every time, regardless of me passing null to both useQueryCache and useResultCache. I tried using false instead of null, but that didn't work either. Lastly, I also tried calling both setResultCacheLifeSpan(0) and setResultCacheLifeSpan(-1), but neither call made a difference.
Any insight on how to prevent caching since I want a different random row to be selected each time I call this method?
Edit: I also tried calling clearResultCache(), but that just ended up causing an error stating: "Result Cache driver not initialized".
Edit 2: As requested, here's the SQL generated by calling $query->getSqlQuery():
SELECT c.id AS c__id, c.name AS c__name, c.image_url AS c__image_url,
c.level AS c__level, c.created_at AS c__created_at, c.updated_at
AS c__updated_at, RANDOM() AS c__0 FROM cards c ORDER BY c__0 LIMIT 1
It turns out I'm a moron. I tried to simplify my query for this question, and in doing so, I didn't capture the true cause. I had a where() and andWhere() call, and the combination of conditions resulted in only one possible record being matched. Thanks for taking the time to respond, everyone, sorry to have wasted your time!
Doctrine also caches entities you created in the same request/script run.
For instance:
$order = new Order();
$order->save();
sleep(10); // Edit this record in de DB in another procces.
$q = new Doctrine_Query();
$result = $q->select()
->from('Order o')
->where('o.id = '.$order->id);
$order = $result->getFirst();
print_r($order->toArray());
The print_r will not contain the changes you made during the sleep.
The following code will remove that kind of memory cache:
$manager = Doctrine_Manager::getInstance();
$connection = $manager->getCurrentConnection();
$tables = $connection->getTables();
foreach ( $tables as $table ) {
$table->clear();
}
PS: Added this answer because I found this topic trying to resolve above issue.

Can I force the auto-generated Linq-to-SQL classes to use an OUTER JOIN?

Let's say I have an Order table which has a FirstSalesPersonId field and a SecondSalesPersonId field. Both of these are foreign keys that reference the SalesPerson table. For any given order, either one or two salespersons may be credited with the order. In other words, FirstSalesPersonId can never be NULL, but SecondSalesPersonId can be NULL.
When I drop my Order and SalesPerson tables onto the "Linq to SQL Classes" design surface, the class builder spots the two FK relationships from the Order table to the SalesPerson table, and so the generated Order class has a SalesPerson field and a SalesPerson1 field (which I can rename to SalesPerson1 and SalesPerson2 to avoid confusion).
Because I always want to have the salesperson data available whenever I process an order, I am using DataLoadOptions.LoadWith to specify that the two salesperson fields are populated when the order instance is populated, as follows:
dataLoadOptions.LoadWith<Order>(o => o.SalesPerson1);
dataLoadOptions.LoadWith<Order>(o => o.SalesPerson2);
The problem I'm having is that Linq to SQL is using something like the following SQL to load an order:
SELECT ...
FROM Order O
INNER JOIN SalesPerson SP1 ON SP1.salesPersonId = O.firstSalesPersonId
INNER JOIN SalesPerson SP2 ON SP2.salesPersonId = O.secondSalesPersonId
This would make sense if there were always two salesperson records, but because there is sometimes no second salesperson (secondSalesPersonId is NULL), the INNER JOIN causes the query to return no records in that case.
What I effectively want here is to change the second INNER JOIN into a LEFT OUTER JOIN. Is there a way to do that through the UI for the class generator? If not, how else can I achieve this?
(Note that because I'm using the generated classes almost exclusively, I'd rather not have something tacked on the side for this one case if I can avoid it).
Edit: per my comment reply, the SecondSalesPersonId field is nullable (in the DB, and in the generated classes).
The default behaviour actually is a LEFT JOIN, assuming you've set up the model correctly.
Here's a slightly anonymized example that I just tested on one of my own databases:
class Program
{
static void Main(string[] args)
{
using (TestDataContext context = new TestDataContext())
{
DataLoadOptions dlo = new DataLoadOptions();
dlo.LoadWith<Place>(p => p.Address);
context.LoadOptions = dlo;
var places = context.Places.Where(p => p.ID >= 100 && p.ID <= 200);
foreach (var place in places)
{
Console.WriteLine(p.ID, p.AddressID);
}
}
}
}
This is just a simple test that prints out a list of places and their address IDs. Here is the query text that appears in the profiler:
SELECT [t0].[ID], [t0].[Name], [t0].[AddressID], ...
FROM [dbo].[Places] AS [t0]
LEFT OUTER JOIN (
SELECT 1 AS [test], [t1].[AddressID],
[t1].[StreetLine1], [t1].[StreetLine2],
[t1].[City], [t1].[Region], [t1].[Country], [t1].[PostalCode]
FROM [dbo].[Addresses] AS [t1]
) AS [t2] ON [t2].[AddressID] = [t0].[AddressID]
WHERE ([t0].[PlaceID] >= #p0) AND ([t0].[PlaceID] <= #p1)
This isn't exactly a very pretty query (your guess is as good as mine as to what that 1 as [test] is all about), but it's definitively a LEFT JOIN and doesn't exhibit the problem you seem to be having. And this is just using the generated classes, I haven't made any changes.
Note that I also tested this on a dual relationship (i.e. a single Place having two Address references, one nullable, one not), and I get the exact same results. The first (non-nullable) gets turned into an INNER JOIN, and the second gets turned into a LEFT JOIN.
It has to be something in your model, like changing the nullability of the second reference. I know you say it's configured as nullable, but maybe you need to double-check? If it's definitely nullable then I suggest you post your full schema and DBML so somebody can try to reproduce the behaviour that you're seeing.
If you make the secondSalesPersonId field in the database table nullable, LINQ-to-SQL should properly construct the Association object so that the resulting SQL statement will do the LEFT OUTER JOIN.
UPDATE:
Since the field is nullable, your problem may be in explicitly declaring dataLoadOptions.LoadWith<>(). I'm running a similar situation in my current project where I have an Order, but the order goes through multiple stages. Each stage corresponds to a separate table with data related to that stage. I simply retrieve the Order, and the appropriate data follows along, if it exists. I don't use the dataLoadOptions at all, and it does what I need it to do. For example, if the Order has a purchase order record, but no invoice record, Order.PurchaseOrder will contain the purchase order data and Order.Invoice will be null. My query looks something like this:
DC.Orders.Where(a => a.Order_ID == id).SingleOrDefault();
I try not to micromanage LINQ-to-SQL...it does 95% of what I need straight out of the box.
UPDATE 2:
I found this post that discusses the use of DefaultIfEmpty() in order to populated child entities with null if they don't exist. I tried it out with LINQPad on my database and converted that example to lambda syntax (since that's what I use):
ParentTable.GroupJoin
(
ChildTable,
p => p.ParentTable_ID,
c => c.ChildTable_ID,
(p, aggregate) => new { p = p, aggregate = aggregate }
)
.SelectMany (a => a.aggregate.DefaultIfEmpty (),
(a, c) => new
{
ParentTableEntity = a.p,
ChildTableEntity = c
}
)
From what I can figure out from this statement, the GroupJoin expression relates the parent and child tables, while the SelectMany expression aggregates the related child records. The key appears to be the use of the DefaultIfEmpty, which forces the inclusion of the parent entity record even if there are no related child records. (Thanks for compelling me to dig into this further...I think I may have found some useful stuff to help with a pretty huge report I've got on my pipeline...)
UPDATE 3:
If the goal is to keep it simple, then it looks like you're going to have to reference those salesperson fields directly in your Select() expression. The reason you're having to use LoadWith<>() in the first place is because the tables are not being referenced anywhere in your query statement, so the LINQ engine won't automatically pull that information in.
As an example, given this structure:
MailingList ListCompany
=========== ===========
List_ID (PK) ListCompany_ID (PK)
ListCompany_ID (FK) FullName (string)
I want to get the name of the company associated with a particular mailing list:
MailingLists.Where(a => a.List_ID == 2).Select(a => a.ListCompany.FullName)
If that association has NOT been made, meaning that the ListCompany_ID field in the MailingList table for that record is equal to null, this is the resulting SQL generated by the LINQ engine:
SELECT [t1].[FullName]
FROM [MailingLists] AS [t0]
LEFT OUTER JOIN [ListCompanies] AS [t1] ON [t1].[ListCompany_ID] = [t0].[ListCompany_ID]
WHERE [t0].[List_ID] = #p0

Resources