Join on get_where - is this possible? - php

I have a table called Listings and a table called wines and one more called wineinfo
I originally was using the following to get the info from the listings table only, how ever since I restructured my DB, it requires the use of two other tables.
$listing = $this->db->get_where( "listings", [ "listingID" => $id ]
)->row();
if( !$listing )
throw new Exception("Error: Listing you're trying to bid on does not exist.", 1);
// check not to bid on own listings
if( $listing->list_uID == $userid )
throw new Exception("Error: Dont't bid on your own listings.", 1);
I then tried changing the code so the JOIN statements could work
$this->db->select("FROM listings.*, Vintage, Vineyard, Wine_Name, Region, Advice, Grape,Producer,Type id,wine_id,Wine_Name,");
$this->db->from("wineinfo");
$this->db->where(["listingsID" => $id]);
$this->db->where(["wineinfo.wine_id" => "listings.wine_id"]);
$this->db->where(["wineinfo.Vintage" => "listings.wine_id"]);
$this->db->join("wines", "wineinfo.wine_id = wines.wine_id");
$listing = $this->db->get()->row();
I am being given this error.
Unknown table 'listings'
But there is 100% a table called listings.
I know I've missed something, or definitely messed up the code, I am only just learning about this and the code above worked for something else, but now I've amended it to this, it hasn't.

I then tried changing the code so the JOIN statements could work
you are trying to combine 3 tables with 2 FROM and one JOIN clauses, which is incorrect the way you do it.
you need to keep SELECT clean, just select the columns you need, like:
$this->db->select("listings.*, wineinfo.*, wine.*");
then the FROM clause:
$this->db->from("wineinfo");
then make the joins:
$this->db->join("listings", "wineinfo.wine_id = listings.listingsID");
$this->db->join("wines", "wineinfo.wine_id = wines.wine_id");
followed by your where clauses.
please note, I don't know your table structure, so I can only guess your JOIN relationships. Also this is a simplified example, where I suppose that the 3 tables don't have matching column names.
response to "ambiguous" comment: you can limit your select clause to just necessary columns, e.g.
$this->db->select("listings.*, wineinfo.vintage, wine.*");
or use an alias:
$this->db->select("listings.*, wineinfo.wine_id as my_wineID, wine.*");

The FROM in $this->db->select("FROM listings.*, Vintage, Vineyard, Wine_Name, Region, Advice, Grape,Producer,Type id,wine_id,Wine_Name,"); is not supposed to be there.
The generated out query now looks like:
SELECT FROM *... FROM sometable

Related

PHP CI sql query syntax

I need some help with a query as I don't seem to get my head around it.
First table vacancies:
vac_id
vac_title
vac_location
vac_description
is_deleted
status
Second table vacancies_labels:
vac_id
Label_id
Now I would like to get an output containing all vacancies within a certain location but they also cannot contain the label_id '10' nonetheless of the location.
SELECT `v`.*
FROM `vacancies` AS `v`
LEFT JOIN `vacancies_labels` as `vl` ON `v`.`vacancy_id` = `bl`.`vacancy_id`
WHERE `v`.`vac_location` = 'russia'
AND `v`.`is_deleted` != 1
AND `v`.`status` = 1
AND `vl`.`label_id` NOT IN ('10')
GROUP BY `v`.`vacancy_id`
This results only in the vacancies that have a record in the vacancies_labels table that are not 10. It leaves out however all vacancies that have no records at all in the vacancies_labels table but fit within the location range.
What am I missing here?
Thx!
Using a LEFT JOIN, if the record is not found, then the values will return null. But in your WHERE clause, you have
AND `vl`.`label_id` NOT IN ('10')
as NOT IN doesn't consider nulls you have to do something like...
AND ( `vl`.`label_id` NOT IN ('10') OR `vl`.`label_id` IS NULL)

How to group by aggregation of column in foreign table with many-to-many relation in Eloquent?

I am starting to get headaches over this so I thought I just post it here.
I have two tables that are related through a pivot table (as it is a many-to-many relationship). I use Laravel and Eloquent (but general help on how to achieve this with normal SQL queries is also highly appreciated).
I want to order the first table based a column of the second one but the column needs to be "aggregated" for this.
Example with Cars that are shared by many drivers and can have different colors:
Car-Table: [id, color]
Driver-Table: [id, name]
Car.Driver-Table: [car_id, driver_id]
I need a query that gets all drivers that only drive red cars and then all that don't drive red cars.
I have to use a query because I'll maybe do other things (like filtering) on this query afterwards and want to paginate in the end.
I already use queries that get either one of the two groups. They look like this:
In the Driver model:
public function redCars() {
return $this->cars()->where('color', 'red');
}
public function otherColoredCars() {
return $this->cars()->where('color', '<>', 'red');
}
And then in somewhere in a controller:
$driversWithOnlyRedCars = Driver::whereDoesntHave('otherColoredCars')->get();
$driversWithoutRedCars = Driver::whereDoesntHave('redCars')->get();
Is there a way to combine these two?
Maybe I am just thinking completely wrong here.
Update for clarification:
Basically I would need something like this (ot any other way that would lead to the same outcome)
$driversWithOnlyRedCars->addTemporaryColumn('order_column', 0); // Create temporary column with value 0
$driversWithoutRedCars->addTemporaryColumn('order_column', 1);
$combinedQuery = $driversWithOnlyRedCars->combineWith($driversWithoutRedCars); // Somehow combine both queries
$orderedQuery = $combinedQuery->orderBy('order_colum');
$results = $combinedQuery->get();
Update 2
I think, I found out how to get near my goal with raw queries.
Would be something like this:
$a = DB::table(DB::raw("(
SELECT id, 0 as ordering
FROM drivers
WHERE EXISTS (
SELECT * FROM cars
LEFT JOIN driver_car ON car.id = driver_car.car_id
WHERE driver.id = driver_car.driver_id
AND cars.color = 'red'
)
) as only_red_cars"));
$b = DB::table(DB::raw("(
SELECT id, 1 as ordering
FROM drivers
WHERE EXISTS (
SELECT * FROM cars
LEFT JOIN driver_car ON car.id = driver_car.car_id
WHERE driver.id = driver_car.driver_id
AND cars.color <> 'red'
)
) as no_red_cars"));
$orderedQuery = $a->union($b)->orderBy('ordering');
Now the problem is that I need the models ordered like this and paginated in the end so this is not really an answer to my question. I tried to convert this back to models but I didn't succeed yet. What I tried:
$queriedIds = array_column($orderedQuery->get()->toArray(), 'id');
$orderedModels = Driver::orderByRaw('(FIND_IN_SET(drivers.id, "' . implode(',', $queriedIds) . '"))');
But looks like FIND_IN_SET only allows for a column of the table as second parameter. Is there another way to get the Models in the right order out of the ordered union query?
You can use a UNION query:
$driversWithOnlyRedCars = Driver::select('*', DB::raw('0 as ordering'))
->whereDoesntHave('otherColoredCars');
$driversWithoutRedCars = Driver::select('*', DB::raw('1 as ordering'))
->whereDoesntHave('redCars');
$drivers = $driversWithOnlyRedCars->union($driversWithoutRedCars)
->orderBy('ordering')
->orderBy('') // TODO
->paginate();
How do you want drivers with the same ordering to be ordered? You should add a second ORDER BY clause to get a consistent order every time you execute the query.
This is the best I got:
$driversWithOnlyRedCars = Driver::whereHas('cars',function($q){
$q->where('color', 'red');
})->withCount('cars')->get()->where('cars_count',1);

PHP Codeigniter - Join a table if column does not equal NULL

Setup:
Codeigniter 3 running on a variant of PHP 5 server. Using the Query Builder to deal with the db.
Background:
Creating a software that has a client profile. Users can add multiple notes to the profile, and link a contact to that note. The profile then displays all the notes and the contact on the corresponding profile, using the join() method from Codeigniter 3's Query Builder.
Issue:
A note can be added without a client contact, in the case of a generalised note. This sets the default value of NULL in the DB which in turn prevents the client_model from returning the notes, because it cant join the tables.
Current code:
function get_client_notes($client_id)
{
$this->db->join('nh_note_types', 'nh_note_types.type_id = nh_client_notes.client_notes_type');
$this->db->join('nh_user_profiles', 'nh_user_profiles.user_profile_user_id = nh_client_notes.client_notes_added_by');
$this->db->join('nh_client_contacts', 'nh_client_contacts.client_contact_id = nh_client_notes.client_notes_client_contact_id');
$this->db->order_by("client_notes_added_date", "desc");
$query = $this->db->get_where('nh_client_notes', array('client_notes_client_id' => $client_id));
return $query;
}
Currently if the value for client_notes_client_contact_id is NULL it will not return any data for that row.
What I am trying to find out is if there is a way to do the following:
IF the client_notes_client_contact_id is not null then join the tables, else carry on past.
Or any other way that would join the tables if there is a value and where it is NULL, then don't join.
Any help is appreciated!
Your current MySQL query with the above Query builder would 'build' like this:
SELECT
*
FROM
nh_client_notes
JOIN nh_note_types ON
( nh_note_types.type_id = nh_client_notes.client_notes_type )
JOIN nh_user_profiles ON
( nh_user_profiles.user_profile_user_id = nh_client_notes.client_notes_added_by )
JOIN nh_client_contacts ON
( nh_client_contacts.client_contact_id
=
nh_client_notes.client_notes_client_contact_id
)
WHERE
client_notes_client_id = 479
ORDER BY
client_notes_added_date DESC
However, this will require all the JOINs to have an matching ID Available. Thus the correct MySQL would be LEFT JOIN on client_notes_client_contact_id key, which is as you've requested to be conditional.
In this instance, adjust your Query builder to have a third parameter on the join() to make this 'left'.
<?php
$this->db->join(
'nh_client_contacts',
'nh_client_contacts.client_contact_id = nh_client_notes.client_notes_client_contact_id',
'left'
);
?>
In doing so, this'll correct your query to bring back client_notes regardless of client_notes_client_contact_id.

Codeigniter active record left join issue

I have two tables 'accounts_transactions' and 'accounts_bills_transactions'.
I have to left join these two using active record of codeigniter.But the names of key columns used to join are different.So I am not getting the key column from the left table in the output .What query should I write to get the key column from the left table included in the result.
My code is
$this->db->select('*');
$this->db->from('accounts_transactions');
$this->db->join('accounts_bills_transactions', 'accounts_transactions.id = accounts_bills_transactions.transaction_id','left');
$query = $this->db->get();
So, as you see the key columns used to join here are , id from left table and transaction_id from second table.The problem is that I am not getting the id from left table in the result.But I am getting all other columns.I assume the problem is because of difference in column names used to join.ie both the column names are not named 'id' .So how can I get the id from left table included in the result.
You could alias them:
$this->db->select('accounts_transatctions.*, account_transactions.id AS a_id,
accounts_bills_transactions.*,
account_bills_transactions.id AS ab_id');
$this->db->from('accounts_transactions');
$this->db->join('accounts_bills_transactions', 'accounts_transactions.id = accounts_transactions.transaction_id','left');
$query = $this->db->get();
The two IDs will now be available as a_id and ab_id (or whatever alias you choose)
Note: I'm not sure if you can alias in AR without avoiding escaping (haven't been using CI for a while). Should you get any error for that reason, just pass false as second parameter of $this->db->select():
$this->db->select('...', false);
you can try this if you confuse of using $this->where or $this->join
$query = $this->db->query("select ......");
return $query;
You problem is so simple. You can use this query
$query = $this->db
->select('at.*')
->select('abt.id as abt_id');
->from('accounts_transactions at');
->join('accounts_bills_transactions abt', 'at.id = abt.transaction_id','left');
->get()
->result();
When same column are used in join it selects only one. You need to give alise to the other column in second table. The best practice is to use a structure like this
accounts_transatctions
--------------------------
accounts_transatctions_id
other_columns
accounts_bills_transactions
---------------------------
accounts_bills_transactions_id
accounts_transatctions_id
other_columns

Showing data from 2 models in a grid with Yii

I'm trying to select data from two tables and show that data in a paginated grid view. The problem is that Yii is joining the data with a SQL join (because I told it to do it) and because of that it's not showing the correct number of items per page.
To make in clear, I'm selecting from topics and messages_in_topics, and I'm joining then with
$criteria->together = true;
This makes MySQL to return a row for each message (and the related topics). Example:
id_topic topic id_messages message
1 topic1 1 message1_in_topic1
1 topic1 2 message2_in_topic1
1 topic1 3 message3_in_topic1
2 topic2 4 message1_in_topic2
2 topic2 5 message2_in_topic2
There are only 2 topics, but Yii's paginator thinks there are 5.
The fastest way to "fix" this is grouping by the id_topic field, anyways, I can't do that because there's a where condition which searches with the like statement.
Thank you
EDIT:
Here's my action code:
$criteria = new CDbCriteria;
$get_s = Yii::app()->request->getQuery('s', '');
if( $get_s ){
$criteria->compare("topic_title", $get_s, true);
$criteria->compare("message_text", $get_s, true, 'OR');
}
$criteria->with = array('messages');
$criteria->addCondition(array( ...... )); <--- some rules like if the topic is validated...
$dataProvider = new CActiveDataProvider('Topics', array(
'criteria'=>$criteria,
'pagination=>array('pageSize'=>15)
));
Actually, what happens there is that the information being displayed is in regard to your messages. The repeated topic values are because these topics are the corresponding related data to the message.
You could try to tell your query to use GROUP BY in the results...
SELECT t.id_topic, t.topic, COUNT(m.id_messages)
FROM topics t LEFT JOIN messages m ON t.id_topic = m.id_topic
GROUP BY t.id_topic
This way, more or less, you can display the count of messages-per-topic.
I could help you more if you'd show us your SQL.
EDIT: After seeing your code, here is my guess:
$criteria = new CDbCriteria;
$get_s = Yii::app()->request->getQuery('s', '');
if( $get_s ){
$criteria->compare("topic_title", $get_s, true);
$criteria->compare("message_text", $get_s, true, 'OR');
}
$criteria->with = array('messages');
$criteria->addCondition(array( ...... )); <--- some rules like if the topic is validated...
$dataProvider = new CActiveDataProvider('Topics', array(
'criteria'=>$criteria,
'pagination=>array('pageSize'=>15)
));
You can make sure the query isn't complicated by yii's formatting by pouring it using only three basic properties of CDbCriteria:
$criteria->select is exactly the list of columns you want to select.
$criteria->condition is exactly the WHERE condition, I honestly prefer using a string to an array, since using the string allows me to use the exact condition I put in here.
$criteria->join is attached immediately after the $model->tableName() you specify in the CActiveDataProvider constructor.
$criteria->group is added at the end of the query, you just need to specify the grouping column.
Also, You can also make sure to set these properties directly, so you actually descompose your query into the CDbCriteria object. For instance:
SELECT t.id_topic, t.topic, COUNT(m.id_messages)
FROM topics t LEFT JOIN messages m ON t.id_topic = m.id_topic
GROUP BY t.id_topic
would be like this
/*1*/ $criteria->select = 't.id_topic, t.topic, COUNT(m.id_messages)';
/*2*/ $criteria->condition = 't.topic_title LIKE %'.$get_s.'% OR ...';/* add your conditions here*/
/*3*/ $criteria->join = 'LEFT JOIN messages m ON t.id_topic = m.id_topic'; //Full join statement here, including the nature of the join.
/*4*/ $criteria->group = 't.id_topic';
IMPORTANT: take into account that since you pass your Topics classname to the CActiveDataProvider constructor, the table under Topics will be known as t. Any other tables must be specified as well (Pretty much like messages m or messages AS m)in the join condition otherwise you might get a column xxxx is ambiguous warning.
Don't pass out the chance of giving an eye to CDbCriteria and CActiveDataProvider for any questions you might have.
The problem isn't the paginator, it is you query. If you run the query:
SELECT *
FROM topics as t
INNER JOIN messages_in_topics as mt
ON mt.topics_id = t.id
INNER JOIN messages as m
ON m.id = mt.messages_id
You will get 5 results, as show in your example above.
So more importantly, what are you trying to do?
Also, your table shows a MANY_MANY relationship (message1 has 2 topics, topic1 has 3 messages), is your database set up the right way, and are your model relations configured accordingly?
Are you trying to show ALL messages in each topic on a single line?
Are you trying to show ALL topics and list each message with that topic?
Assuming you have relations set up correctly, you can just use: $messages=$topics->messages; and get an array with all the messages listed.
Conversely, you can do $topics=$messages->topics; to get the topics.

Categories