I am trying to get a single record for one table by checking for a variable that is in two associated tables.
For instance my tables are
user:
id | name
3781 | Foo Manchu
user_programs:
id | user_id | page_id
4150 | 3781 | 16974
Page
id | title | section_id
16974 | Dudes | 3
So I need to query and return the Users who have a section_id of 3
Users are associated to the user_program table which are assocaited to specific pages through page_id.
This is what I have which is not returning anything:
if($section_id == 3) {
$q = $this->createQuery('u');
$q->leftJoin('u.user_programs up');
$q->leftJoin('up.Page p');
$q->where('u.published=1 AND u.is_preview = 0 AND u.featured=1 AND fp.deleted_at IS NULL');
$q->addWhere('p.section_id=?', $section_id);
$q->orderBy('RAND()')->limit(1);
I can succesfully return the u query without doing the join, but I need to limit the query to only return users with a section_id on the associated page of 3.
If the schema is correct, then a User should have a Pages (or something like that) relation. You can write the query like this:
$query = $this->createQuery('u')
->innerJoin('u.Pages p')
->andWhere('u.published = ?', 1)
->andWhere('u.is_preview = ?', 0)
->andWhere('u.featured = ?', 1)
->addWhere('p.section_id = ?', $section_id)
->limit(1)
;
I removed the fp.deleted_at part as it makes no sense when using SoftDelete (I presume you use that) and the fp alias was not in the original query anyway.
Related
Okay so I have a simple category table and a separate posts table easy right but when the user posts a post I wast think should I store both the sub and parent cat in the posts table but would that not be a lot of data duplication so I instead just store the sub_cat then I use a few PHP functions to query the database for the primary cat and its name.
categories table
ID | cat_name | main_cat
1 | Dinner | 0
2 | Chicken | 1
posts table
ID | title | sub_cat | fields that are not related to Q
1 | test | 2 |
Get parent(main) category
$sub_cat = is from a selection query that gets posts and their sub_cats
function main_cat($sub_cat){
require("conn_posts.php");
$stmt = $conn_posts->prepare("SELECT `main_cat` FROM `cats` WHERE `ID` = ?");
$stmt->bind_param("s", $sub_cat);
$stmt->execute();
$stmt_results = $stmt->get_result(); // get result
while($row_get = $stmt_results->fetch_assoc()){
if($row_get['main_cat'] == 0){
return $sub_cat;
}elseif($row_get['main_cat'] !== ""){
return $row_get['main_cat'];
}
}
}
This function gets any category name as long as the id is valid
function cat_name($cat_number){
require("conn_posts.php");
$stmt = $conn_posts->prepare("SELECT `cat_name` FROM `cats` WHERE `ID` = ?");
$stmt->bind_param("s", $cat_number);
$stmt->execute();
$stmt_results = $stmt->get_result(); // get result
$row_get = $stmt_results->fetch_assoc();
if($stmt_results->num_rows <= 0){
return 0;
}elseif($stmt_results->num_rows == 1){
return $row_get['cat_name'];
}
}
My question is is this a good way to process my posts sub-category and parent category are there better ways of doing what I am currently doing? eg. is my database schema good(by good I mean is it better to just include the parent cat id in the posts table than to do the PHP server-side processing)?
Your database schema is good: it doesn't include any replication, I wouldn't change it. The way you're handling fetching the categories in PHP isn't really optimal though: you should almost always aim to minimize the number of queries as it (in general) will affect performance more than the complexity of a query.
If you're running MySQL 8+, a great way to do this is with a recursive CTE; it will allow you to fetch all parents with one query:
WITH RECURSIVE cte AS (
SELECT id, cat_name, main_cat, 0 as depth FROM categories WHERE ID=3
UNION ALL
SELECT categories.id, categories.cat_name, categories.main_cat, cte.depth+1 as depth
FROM cte inner join categories
ON cte.main_cat = categories.id
)
SELECT cat_name FROM cte order by depth ASC
The number '3' in that query can be replaced by the category you're trying to retrieve. You can check this DB fiddle for a live example. If I see your code, incorporating it into your PHP should be fairly trivial. If not, leave a comment and I'll try to expand.
I'm making a social media kind of website, where people can post messages and become friends and such.
And so I have a database with the following tables.
Friends
+-----+-----------+----------+----------+--------+
| id | Bevriend | UserID1 | UserID2 | vType |
+-----+-----------+----------+----------+--------+
| 1 | 1 | 1 | 3 | 0 |
+-----+-----------+----------+----------+--------+
Blog
+-----+----------------+-------+---------+
| id | title | text | userid |
+-----+----------------+-------+---------+
| 1 | My first entry | Test | 1 |
+-----+----------------+-------+---------+
I want to write a query of sorts to get the blog posts only from the person I am friends with.
So long as either UserID1 or UserID2 contains the userID from my session AND in the database the bit known as "Bevriend" is set to 1 it should get the records.
I am at a loss, if I need to supply you with more information please let me know.
I am giving you a query that might help. It takes only items from blog which are posted by friend, but not current user. You query should look like (excluding blog posts where the current user is the author):
SELECT
b.*
FROM Blog b
INNER JOIN Friends f ON (b.userid = f.UserID1 OR b.userid = f.UserID2)
WHERE
f.Bevriend = 1
AND
(
b.userid = f.UserID1 AND f.UserID2 = '{$currentUserID}'
OR
b.user_id = f.UserID2 AND f.UserID1 = '{$currentUserID}'
)
What we do here is to JOIN the table this way that either UserID1 or UserID2 appear as Blog record "owner". Then in the where clause we say give me only these blog records where where the owner is my friend and not me, for each column UserID1 and UserID2.
This should look like in CI (including the blog posts where current user is the author):
$currentUserID = (int) $this->session->userdata('userID');
$page = (int) $this->input->get('page'); // example retrieving of desired page num
$pagesize = (int) $this->config->item('blog_pagesize'); // example retrieving of pagesize
// Prevent unexpected behavior
if(0 >= $page)
$page = 1;
// Prevent unexpected behavior
if(0 >= $pagesize)
$pagesize = 20; // If config is wrong we ensure it will continue to work
// Additional page checks go here...
$query = $this->db->query('
SELECT
b.*
FROM Blog b
INNER JOIN Friends f ON (b.userid = f.UserID1 OR b.userid = f.UserID2)
WHERE
f.Bevriend = 1
AND
( f.UserID2 = ? OR UserID1 = ?)
LIMIT ?, ?
', array($currentUserID, $currentUserID, ($page - 1) * $pagesize, $pagesize));
foreach ($query->result() as $row) {
echo $row->id;
echo $row->title;
echo $row->text;
}
// Clean up at the end
$query->free_result();
You could adapt the columns you want to retrieve, ordering and remove duplications with GROUP BY or DISTINCT. We run custom query here, because is much more cleaner that using $this->db->select, $this->db->from, $this->db->join,$this->db->where, etc. when the query got more complex. If you use them you have to pass third paramater true to disable each of these functions escaping and it will look like "ugly code".
You cannot use pagination directly with custom query through $this->db->query. This is because it is CI query, but not an active record. Below I translate the query into active record one.
To use the default CI active record functionality:
$results = $this->db->select('b.*')
->from('Blog b')
->join('Friends f', 'b.userid = f.UserID1 OR b.userid = f.UserID2')
->where('f.Bevriend', 1)
->where("(f.UserID1 = {$currentUserID} OR f.UserID2 = {$currentUserID})")
->group_by('b.id')
//->get()
//->results();
The example above should enable you to use the default pagination, although I find it not very useful and flexible.
I'm having a problem at the moment where I have a column called rating in the links table and there is definitely values other than 0 within the column but 0 is the only value which is returned foreach link. When I do a simple get for that column it then shows all the other values but not when I do an SQL Join.
I know the problem is my joining of the tables but I'm unsure how I would go about joining these specific tables.
Database Table Structure
The rating column is the one which is causing me problems.
'links' id | title | url | user_id | list_id | rating | weight | date_created
'list' id | list_title | list_description | user_id | rating | views | date_created
'link_ratings' id | user_id | link_id | rated | date_created
Model:
public function get_latest(){
$this->db->limit(100);
$this->db->order_by('links.date_created', 'DESC');
$this->db->select('*');
$this->db->select('links.id as current_link_id');
$this->db->from('links');
$this->db->join('list', 'links.list_id = list.id');
$this->db->join('users', 'links.user_id = users.id');
$this->db->join('link_ratings', 'links.id = link_ratings.link_id','left');
$get_latest = $this->db->get();
return $get_latest;
}
Any Help is appreciated.
You should try this:
function get_latest(){
$this->db->select('list.*, users.*, links.id as current_link_id');
$this->db->from('links');
$this->db->join('list', 'links.list_id = list.id');
$this->db->join('users', 'links.user_id = users.id');
$this->db->join('link_ratings', 'links.id = link_ratings.link_id','left');
$this->db->order_by('links.date_created', 'DESC');
$this->db->limit(100);
$get_latest = $this->db->get()->result_array(); #fetch all rows here
echo "<pre>";print_r( $get_latest );die; #print all rows and see if its fetching ratings corrctly or not.
echo $this->db->last_query();die; #check the query generated
return $get_latest;
}
The reason will be purely logical, in that the join will be causing no results to be returned because there are no results. I've fallen into this many times.
I am not able to diagnose your particular problem but when faced with issues like this I:
1- turn on the CI profiler
2- var_dump the array so you can see what's going on
3- write a traditional SQL query and run it in PHPMyAdmin
One, or a combination of all three, will enable you to diagnose.
I have 2 tables in a single database.
For example:
Table 1 Columns:
id | code | name
Table 2 Columns:
id | code | family | etc.
How can I query both tables based on the overlapping code column to retrieve family column?
This is what I currently have:
$query = $this->db
->select('*')
->from('table 1')
->where('code', '123');
$query->get()->result();
The above query will retrieve the row(s) with code 123 but I'd like to get the corresponding family data from table 2. How can I do this?
Use join(). Something like:
$query = $this->db
->select('*')
->from('table1')
->join('table2', 'table1.code = table2.code')
->where('code', '123');
Docs on the function are here: http://ellislab.com/codeigniter/user-guide/database/active_record.html#select
well you must add a sentence Join, it will let you query two tables.
$dataquery = array('table1.code' => '123'); //var in order to where
$this->db->select('table1.id As id1, table2.id As id2') //separate the ids with names
$this->db->join('table2.code','tabla1.code'); //code is overlapping
$query = $this->db->get_where('table1',$dataquery);
return $query->get()->result_array();//Return array if you want
Cheers!
I have two tables with following costruction:
errortype:
| id | errortype_text |
errorreason:
| id | errorreason_text | errortype_id |
I would like to select all data from errorreason table and replace the errortype_id data with the coresponding errortype_text.
How is this possible?
if the tables have a foreign key you can do that in your model:
$this->db->select('erroreason_text');
$this->db->join('errorreason', 'errortype.id = errorreason.id');
$query = $this->db->get('errortype');
return $query->result();
If you are talking about SQL/MySQL it should be:
SELECT `errortype_text`, `errorreason_text`, `errortype_id` FROM `errorreason` JOIN `errortype` ON `errortype`.`id`=`errorreason`.`id`
This joins your tables based on entries having the same id and it is exactly the method Yan suggested.
If you are explicitely referring to PHP/Codeigniter, you have to pass this as a parameter to mysql_query to be afterwards evaluated:
$query=mysql_query("SELECT `errortype_text`, `errorreason_text`, `errortype_id` FROM `errorreason` JOIN `errortype` ON `errortype`.`id`=`errorreason`.`id`");