"Table1":
id
name
1
Ulrich
2
Stern
"Table2":
id
school
tid
1
A
1
2
B
1
I want to join 2 table to get all information. With SQL query like this:
SELECT Table1.id,
name,
school
FROM `Table1`
INNER JOIN `Table2`
ON Table1.id = Table2.tid
It gives me all information as I expect (I mean 2 rows with name 'Ulrich').
But when I do with Yii2 query:
$query = self::find();
$query -> alias('t1')
-> innerJoin(['t2'=>'Table2'], 't1.id=t2.tid')
$result = NULL;
if($total = $query->count()) {
$result = $query
-> select([t1.*, t2.school])
->asArray()
->all()
;
$result[0]['count'] = $total;
}
it only gives me 1 row with name 'Ulirch'.
Can anyone help me with this problem. Thank you very much.
If you use ActiveRecord::find() method to create query you will get instance of yii\db\ActiveQuery. This is query designed to load ActiveRecord models. Because of that if you do any type of join and your result set contains primary key of your main model (The model which find() method was called to create query) the ActiveQuery will remove any rows it considers duplicate.
The duplicates are recognised based on main model primary key so the rows in resultset will be considered duplicate even if the data from joined table are different. That's exactly what happened in your case.
To avoid that you have to use query builder instead of ActiveQuery.
Your query can look for example like this:
$query = (new \yii\db\Query())
->from(['t1' => self::tableName()])
->innerJoin(['t2'=>'Table2'], 't1.id=t2.tid');
Related
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);
I am creating a query and I used the LEFT JOIN to join two tables. But I'm having trouble in getting the fb_id value from the table, it gives me an empty result. Here is my code:
$sql = "SELECT * FROM tblfeedback a LEFT JOIN tblreply b ON a.fb_id = b.fb_id WHERE a.fb_status = 1 ORDER BY a.fb_id DESC";
$res = $con->query($sql);
....
....
The query above would give me a result like this :
I think that's why I don't get the fb_id value is because the last column is null. How do I get the value of fb_id from the first column? Thanks. I am really having trouble with this. I hope someone can enlightened my mind.
You should give an alias to the column in the parent table, because the column names are the same in both tables. When fetch_assoc() fills in $row['fb_id'], it gets the last one in the result row, which can be NULL because it comes from the second table.
SELECT a.fb_id AS a_id, a.*, b.*
FROM tblfeedback a
LEFT JOIN tblreply b ON a.fb_id = b.fb_id
WHERE a.fb_status = 1
ORDER BY a_id DESC
Then you can access $row['a_id'] to get this column.
More generally, I recommend against using SELECT *. Just select the columns you actually need. So you can select a.fb_id without selecting b.fb_id, and it will always be filled in.
Because you are using a left join, the 2 rows in your result set image are the rows from tblfeedback whose fb_id were not found in tblreply. We know this is true because all the tblreply columns in the result set are null.
With that said, its not real clear what you are asking for. If you are asking how you access the tblfeedback.fd_id column from your query via php, you can use the fetch_array method and use the 0 index.
$sql = "SELECT * FROM tblfeedback a LEFT JOIN tblreply b ON a.fb_id = b.fb_id WHERE a.fb_status = 1 ORDER BY a.fb_id DESC";
$res = $con->query($sql);
while($row = $res->fetch_array()) {
echo "fb_id: " . $row[0] . "<br>";
}
My Problem: Getting query results from 2 db tables with PROPEL2 even when second table has no corresponding entries. If the second has corresponding entries than it is no problem.
I have 3 tables: Entry, Contingent and Favorit.
The schema is as follow:
Entry.id [PK]
Entry.contingent_id [FK]
Entry.expert_id
Contingent.id [PK]
Contingent.name
Favorit.id [PK]
Favorit.contingent_id [FK]
Favorit.expert_id
Favorit.pos
I want to get for a specified expert_id ($id) all entries from Entry with contingent-name and if exists the favorit.pos for this expert and contingent. I get the wanted with:
$result = EntryQuery::create()
->filterByExpertId($id)
->join('Entry.Contingent')
->withColumn('Contingent.name','_contingentName')
->join('Contingent.Favorit')
->where('Favorit.expert_id = ?', $id)
->find();
This works only if there exists such a favorit.pos . In some cases this element doesn’t exists (what is wanted from the system). In these cases I want to get the result too just with favorit.pos as empty, null or 0. But Propel doesn’t return me these records.
With MySQL I have no problem to get the desired result:
SELECT entry.* ,
(SELECT favorit.position
FROM contingent, favorit
WHERE
favorit.expert_id = entry.expert_id
AND entry.contingent_id = contingent.id
AND contingent.id = favorit.contingent_id
)
FROM `entry`
JOIN contingent
ON entry.contingent_id = contingent.id
WHERE
entry.expert_id=1;
Use Join left in code:
->join('Contingent.Favorit','selection conditon','left' )
This left work when empty database when condition is false
in condition like 'id'=$id
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
Hi i have following query where it's use joininner statement to get all possible businesses. But when a business is created for first time only 1 category will be updated the rest 2 will remain null
public function searchBusinessByCategoryString($str = null, $city=null,$start,$perpage)
{
$select = $this->getDbTable()->getAdapter()->select();
$select->from('business as b', array('b.business_name','b.business_url','b.reviews_num','b.cat_id','b.business_id','b.rating','b.business_phone','b.business_add1','b.business_add2','b.x','b.y','b.photo_url'))
->joinInner('business_category as bc','b.cat_id = bc.cat_id',array('bc.cat_name'))
->joinInner('business_sub_category as bsc','b.sub_cat_id = bsc.b_sub_cat_id',array('bsc.b_subcat_name','bsc.b_sub_cat_id'))
->joinInner('business_sub_category as bsc2','b.sub_cat2_id = bsc2.b_sub_cat_id',array('bsc2.b_subcat_name','bsc2.b_sub_cat_id'))
->joinInner('business_sub_category as bsc3','b.sub_cat3_id = bsc3.b_sub_cat_id',array('bsc3.b_subcat_name','bsc3.b_sub_cat_id'))
->where("bsc.b_subcat_name like '".$str."%'")
->orWhere("bsc.b_subcat_name like '%".$str."'")
->orWhere("bsc.b_subcat_name= '".$str."'")
->orWhere("bsc2.b_subcat_name like '%".$str."'")
->orWhere("bsc2.b_subcat_name = '".$str."'")
->orWhere("bsc2.b_subcat_name like '".$str."%'")
->orWhere("bsc3.b_subcat_name like '%".$str."'")
->orWhere("bsc3.b_subcat_name = '".$str."'")
->orWhere("bsc3.b_subcat_name like '".$str."%'");
$result = $this->getDbTable()->getAdapter()->fetchAll($select);
return $result;
}
Now the issues is how can i not doing joininner query if the rest 2 categories are null? My above statement return empty result event though there is businesses with one category.
use leftJoin instead of innerJoin where the joined table can contain NULL value. INNER JOIN will join table, using the condition and will not keep lines when a null value is found on the joined table. LEFT JOIN will allow you to keep this line