I have a blog-style app that allows users to tag each post with topics. I keep this data in three separate tables: posts (for the actual blog posts), topics (for the various tags), and posts_topics (a table that stores the relationships between the two).
In order to keep the MVC structure (I'm using Codeigniter) as clean as possible, I'd like to run one MySQL query that grabs all the post data and associated topic data and returns it in one array or object. So far, I'm not having any luck.
The table structure is like this:
Posts
+--------+---------------+------------+-----------+--------------+---------------+
|post_id | post_user_id | post_title | post_body | post_created | post_modified |
+--------+---------------+------------+-----------+--------------+---------------+
| 1 | 1 | Post 1 | Body 1 | 00-00-00 | 00-00-00 |
| 2 | 1 | Post 1 | Body 1 | 00-00-00 | 00-00-00 |
+--------+---------------+------------+-----------+--------------+---------------+
// this table governs relationships between posts and topics
Posts_topics
+--------------+---------------------+-------------------------+-----------------+
|post_topic_id | post_topic_post_id | post_topic_topic_id | post_topic_created |
+--------------+---------------------+-------------------------+-----------------+
| 1 | 1 | 1 | 00-00-00 |
| 2 | 1 | 2 | 00-00-00 |
| 3 | 2 | 2 | 00-00-00 |
| 4 | 2 | 3 | 00-00-00 |
+--------------+---------------------+-------------------------+-----------------+
Topics
+---------+-------------+-----------+----------------+
|topic_id | topic_name | topic_num | topic_modified |
+---------+-------------+-----------+----------------+
| 1 | Politics | 1 | 00-00-00 |
| 2 | Religion | 2 | 00-00-00 |
| 3 | Sports | 1 | 00-00-00 |
+---------+-------------+-----------+----------------+
I have tried this simple query with n success:
select * from posts as p inner join posts_topics as pt on pt.post_topic_post_id = post_id join topics as t on t.topic_id = pt.post_topic_topic id
I've also tried using GROUP_CONCAT, but that gives me two problems: 1) I need all the fields from Topics, not just the names, and 2) I have a glitch in my MySQL so all GROUP_CONCAT data is returned as a BLOB (see here).
I'm also open to hearing any suggestions where I run two queries and try to build out an array for each result; I tried that with the code below but failed (this code also includes joining the user table, which would be great to keep that as well):
$this->db->select('u.username,u.id,s.*');
$this->db->from('posts as p');
$this->db->join('users as u', 'u.id = s.post_user_id');
$this->db->order_by('post_modified', 'desc');
$query = $this->db->get();
if($query->num_rows() > 0)
{
$posts = $query->result_array();
foreach($posts as $p)
{
$this->db->from('posts_topics as p');
$this->db->join('topics as t','t.topic_id = p.post_topic_topic_id');
$this->db->where('p.post_topic_post_id',$p['post_id']);
$this->db->order_by('t.topic_name','asc');
$query = $this->db->get();
if($query->num_rows() > 0)
{
foreach($query->result_array() as $t)
{
$p['topics'][$t['topic_name']] = $t;
}
}
}
return $posts;
}
Any help greatly appreciated.
This query should do the trick. Just change the * to the field list you desire so you are not pulling excess data every time you run the query.
Select
*
FROM
posts,
post_topics,
topics
WHERE
post_topic_topic_id = topic_id AND
post_topic_post_id = post_id
ORDER BY
post_id, topic_id;
Select
*
FROM
posts,
post_topics,
topics,
users
WHERE
post_topic_topic_id = topic_id AND
post_topic_post_id = post_id AND
post_user_id = user_id
ORDER BY
post_id, topic_id;
Holy Cow, You Can do it! See it helps to help. Never knew, try this
Select
post_id,
GROUP_CONCAT(DISTINCT topic_name) as names
FROM
posts,
post_topics,
topics
WHERE
post_topic_topic_id = topic_id AND
post_topic_post_id = post_id
GROUP BY
post_id;
You get
1, 'politics,relligion'
2, 'sports,relligion'
Related
I am currently working on a system. This is how my tables look like:-
sales table:-
ID | avatar | ident
-------------------------------------
1 | img/photo11 | CCS7771
2 | img/photo32 | INL0987
3 | img/photo32 | INL0987
4 | img/photo6 | URS8827
5 | img/photo32 | INL0987
6 | img/photo9 | NSU8837
7 | img/photo3 | PPP9998
kudos table:-
ID | sale_id | ident_id
-------------------------------------
1 | 1 | INL0987
2 | 4 | INL0987
3 | 7 | INL0987
4 | 1 | KKU8837
5 | 1 | URS8827
6 | 4 | SHD8837
So I have this like system, and when A user press the like button to the table row, it stores the sales.id and sales.ident into another tables named kudos, like you can see above.
I am trying to query to see how many people have given kudos for each post. You can see in the above tables, example: the sales.id 1 has gotten 3 kudos from INL0987, KKU8837 and URS8827. I have tried a few different ways, but I cant seems to find a solution for this. This is what I have tried:
/* $resultSet = $mysqli->query("SELECT kudos.sale_id as makesName, sales.ident AS modelsName from kudos,sales where kudos.sale_id = '42' AND kudos.ident_id = sales.ident"); */
$resultSet = $mysqli->query("SELECT kudos.sale_id as TheID, sales.ident AS TheIdent from kudos,sales where kudos.sale_id = '42' AND sales.id = kudos.sale_id");
echo $resultSet->num_rows;
while ($rows = $resultSet->fetch_assoc()) {
$iid = $rows['TheID'];
$iident = $rows['TheIdent'];
echo "<br><br>TheID: $iid";
echo "<br>TheIdent: $iident";
}
Can someone help me out here? I can figure out what I am doing wrong.
I am trying to query to see how many people have given kudos for each post
I think you just want aggregation:
select sale_id, count(*) no_kudos
from kudos
group by sale_id
If you also want to include sale_ids that have no match in kudos, then you can use a correlated subquery:
select id, (select count(*) from kudos k where k.sale_id = s.id) no_kudos
from sales s
I have a really huge problem with my Social Network Plugin.
In my PHP script, I am concatenating SQL Where Queries, using conditional statements (if/else), because with the same function I'm getting the posts for the search bar and for the profile itselves.
I can't change, for this reason, the main statement.
I'm developing a Mention system, like Facebook's: When an user tags someone on its own profile, the post should be shown on the tagged user's profile too.
To achieve this, I'm editing the function that gets the posts, but can't figure out how to write the query.
Let's look the scenario:
I have two tables, one for posts, the other for the mentions linked to the posts' table.
Table: posts (user_id is the publisher, in_user_id is the profile where the post is published)
+---------+----------+------------+------------+------------+
| post_id | user_id | in_user_id | in_group | updated_at |
+---------+----------+------------+------------+------------+
| 1 | 12 | 12 | 0 | some_text |
| 2 | 1 | 1 | 0 | some_text2 |
| 3 | 2 | 2 | 0 | some_text3 |
| 4 | 5 | 12 | 0 | some_text4 |
| 5 | 5 | 5 | 0 | some_text4 |
| 6 | 5 | 5 | 0 | some_text4 |
+---------+----------+------------+------------+------------+
Table: posts_mentions (post_id is the post that contains the mention, user_id is the mentioned user)
+---------+----------+
| post_id | user_id |
+---------+----------+
| 2 | 12 |
| 3 | 12 |
| 6 | 1 |
+---------+----------+
What I need:
I need, when viewing the profile of user_id 12, to get the post_id of the posts published on 12's profile (posts.post_id 1 and 4), but also the posts where user_id 12 is tagged (posts.post_id 2 and 3), which are listed on posts_mentions.
What I tried:
The actual code is like this (this is an example):
$where="";
if($user_id->isFriend())
{
$where .= "WHERE (in_user_id = $user_id AND in_group = '0')";
}
...
$posts = $db->query(sprintf
("SELECT
*
FROM (SELECT posts.post_id
FROM posts ".$where.") posts
ORDER BY posts.post_id DESC");
(I used subqueries because otherwise I couldn't use conditional-concatenated queries)
I tried to add another sub-query in the $where statement, but it didn't work at all.
I'm really getting crazy. How can I resolve this problem?
You can add a subquery to get the post_ids from the post_mentions table. Something like:
$where .= "WHERE (in_user_id = $user_id AND in_group = '0')
OR post_id IN (
SELECT post_id FROM posts_mentions
WHERE user_id = $user_id
)
";
unfortunately i have to do this in mysql / php . I looked for three days, and there is like 10.000 explantions of this but NONE (and I repeat NONE) works for me. I tried it all. I have to ask, sorry.
I have two tables - articles and control.
table "articles"
------------------
art_id | name |
------------------
1 | aaa |
2 | bbb |
3 | ccc |
4 | ddd |
table "control"
--------------------------------------------
con_id | art_id | data |
--------------------------------------------
1 | 1 | something-a |
2 | 2 | something-b |
3 | 1 | something-a |
4 | 2 | something-c |
5 | 3 | something-f |
art_id exists in both tables. Now what i wanted - for query:
"select * from articles order by art_id ASC" displayed in a table
to have also one cell displaying the count for each of art_id's from table CONTROL...
and so i tried join, left join, inner join - i get errors ... I also tried for each get only one result (for example 2 for everything)... this is semi-right but it displays the array of correct results and it's not even with join!!! :
$query = "SELECT art_id, count(*) as counting
FROM control GROUP BY art_id ORDER BY con_id ASC";
$result = mysql_query($query);
while($row=mysql_fetch_array($result)) {
echo $row['counting'];
}
this displays 221 -
-------------------------------------------------
art_id | name | count (this one from control) |
-------------------------------------------------
1 | aaa | 221 |
2 | bbb | 221 |
3 | ccc | 221 |
and it should be:
for art_id(value1)=2,
for art_id(2)=2,
for art_id(3)=1
it should be simple - like a count of values from CONTROL table displayed in query regarding the "articles" table...
The result query on page for table articles should be:
"select * from articles order by art_id ASC"
-------------------------------------------------
art_id | name | count (this one from control) |
-------------------------------------------------
1 | aaa | 2 |
2 | bbb | 2 |
3 | ccc | 1 |
So maybe i should go with JOIN or with join plus for each... Tried tha too, but then i'm not sure what is the proper thing to echo... all-in-all i'm completely lost here. Please help. Thank you.
So imagine this in two steps:
Get the counts per art_id from the control table
Using your articles table, pick up the counts from step 1
That will give you a query that looks like this:
SELECT a.art_id, a.name, b.control_count
FROM articles a
INNER JOIN
(
SELECT art_id, COUNT(*) AS control_count
FROM control
GROUP BY art_id
) b
ON a.art_id = b.art_id;
Which will give you the results you're looking for.
However, instead of using a subquery, you can do it all in one shot:
SELECT a.art_id, a.name, COUNT(b.art_id) AS control_count
FROM articles a
INNER JOIN control b
ON a.art_id = b.art_id
GROUP BY a.art_id, a.name;
SQL Fiddle demo
SELECT *, (SELECT COUNT(control.con_id) FROM control WHERE control.art_id = articles.art_id) AS count_from_con FROM articles ORDER BY art_id DESC;
If I understood your question right, this query should do the trick.
Edit: Created the tables you have described, and it works.
SELECT * FROM articles;
+--------+------+
| art_id | name |
+--------+------+
| 1 | aaa |
| 2 | bbb |
| 3 | ccc |
| 4 | ddd |
+--------+------+
4 rows in set (0.00 sec)
SELECT * FROM control;
+--------+--------+------+
| con_id | art_id | data |
+--------+--------+------+
| 1 | 1 | NULL |
| 2 | 2 | NULL |
| 3 | 1 | NULL |
| 4 | 2 | NULL |
| 5 | 3 | NULL |
+--------+--------+------+
5 rows in set (0.00 sec)
SELECT *, (SELECT COUNT(control.con_id) FROM control WHERE control.art_id = articles.art_id) AS count_from_con FROM articles ORDER BY art_id ASC;
+--------+------+----------------+
| art_id | name | count_from_con |
+--------+------+----------------+
| 1 | aaa | 2 |
| 2 | bbb | 2 |
| 3 | ccc | 1 |
| 4 | ddd | 0 |
+--------+------+----------------+
You haven't quite explained what you want to accomplish with the print out but here is an example in PHP: (Use PDO instead of mysql_)
$pdo = new PDO(); // Make your connection here
$stm = $pdo->query('SELECT *, (SELECT COUNT(control.con_id) FROM control WHERE control.art_id = articles.art_id) AS count_from_con FROM articles ORDER BY art_id ASC');
while( $row = $stm->fetch(PDO::FETCH_ASSOC) )
{
echo "Article with id: ".$row['art_id']. " has " .$row['count_from_con'].' connected rows in control.';
}
Alternatively with the mysql_ extension:
$result = mysql_query('SELECT *, (SELECT COUNT(control.con_id) FROM control WHERE control.art_id = articles.art_id) AS count_from_con FROM articles ORDER BY art_id ASC');
while( $row = mysql_fetch_assoc($result) )
{
echo "Article with id: ".$row['art_id']. " has " .$row['count_from_con'].' connected rows in control.';
}
This should be enough examples to help you accomplish what you need.
So I'm having a bit of trouble thinking of how to approach this using a query builder. Currently, I have three objects that are the following:
HelpRequest
id
...
status
Filter
id
name
statuses -> ManyToMany(targetEntity="Status")
Status
id
name
A filter can have multiple statuses so there is a table that is keeping track what statuses are part of a specific filter.
Sample Data
help_requests
---
| id | content | status |
| 1 | hello | 3 |
filters
---
| id | name |
| 1 | Active |
| 1 | Inactive |
statuses
---
| id | name |
| 1 | Open |
| 2 | Closed |
| 3 | Waiting User Response |
status_filter
---
| status_id | filter_id |
| 1 | 1 |
| 3 | 1 |
| 2 | 2 |
The status_filter table is automatically generated from a ManyToMany relationship in doctrine between a Status object and a Filter object.
Based on the given information, I've written this SQL query but now I'm having troubles writing this with QueryBuilder.
SELECT * FROM help_requests WHERE status IN (SELECT status_id FROM status_filter WHERE filter_id = 1)
If there's any more information I can give, let me know. I've read multiple questions on SO and have tried a number of things but I can't seem to get it right. I'm aware I could just hard coded that query but I'd like the experience using QueryBuilder
Thanks for the help!
Update
In the end, since I couldn't get it to work with QueryBuilder and I didn't want to create a new entity solely to map two other entities together, I decided to use createQuery() instead and this is what I came up with:
SELECT
hr
FROM
HelpRequest hr
WHERE
hr.status
IN (
SELECT
s.id
FROM
Filter f
JOIN
f.statuses s
WHERE
f.name = :name
)
Thank you everyone for the help.
Try this query, and put is in your HelpRequestsRepository class:
$subquery = $this->->select('st.status_id')
->from('/path/to/StatusFilter', 'st')
->where('st.filter_id = 1');
$query = $this->createQueryBuilder('hr')
->select('*')
->where('hr.status IN (' . $subquery->getDQL() . ')')
->getQuery();
Try this approach in the HelpRequestsRepository class:
$qb = $this->createQueryBuilder('hr');
$qb->select("hr");
$qb->join("::Status","s",Expr\Join::INNER_JOIN, "hr.status=s" );
$qb->join("::Filter","f",Expr\Join::INNER_JOIN, "s.filters=f" );
$qb->where("f.name = :name");
$qb->setParameter('name', $nameOfTheFilterToBeFound)
Hope this help
I have two tables in my database announcements and announcements_external:
+----------+ +----------+
| table1 | | table2 |
+----------+ +----------+
| id | | id |
| catid | | eid |
| uid | | username |
| title | | source |
| text | | title |
| views | | section |
| images | | category |
| created | | text |
| modified | | views |
| pubdate | | images |
+----------+ | created |
| modified |
+----------+
In CodeIgniter framework, I want to merge them, so when I will do a foreach($results as $item), I would output them side by side, ordered by field created.
I tried this code but I know it's completly wrong with the merging:
public function get_mixed( $limit = NULL, $offset = NULL )
{
$this->db
->select('
announcements.id,
announcements.catid,
announcements.uid,
section.title AS section,
categories.title AS category,
announcements.title,
announcements.text,
announcements.views,
announcements.created,
announcements.modified,
announcements.pubdate,
announcements_external.id as eid,
announcements_external.eid,
announcements_external.source,
announcements_external.username,
announcements_external.section as esection,
announcements_external.category as ecategory,
announcements_external.title as etitle,
')
->join('categories', 'announcements.catid = categories.id', 'left')
->join('categories as section', 'categories.pid = section.id', 'left')
->join('announcements_external', 'announcements_external.id = announcements_external.id', 'right')
->order_by('created DESC, '.$this->_order_by);
$query = $this->db->get( $this->_table_name, $limit, $offset )->result();
return $query;
}
By the way, I do LEFT JOIN on categories table, which works as it should, but I thought it would be better to leave it in this code so you will see it also.
Any solution or maybe guide where I can get more information pls ? Thanks
It seems that Table1 and Table2 are separated entities so you need to use UNION ALL instead of JOIN to merge them.
(SELECT ..needed columns .. , Table1.created FROM Table1 JOIN categories ... )
UNION All
(SELECT ..needed columns .. , Table2.created FROM Table2 JOIN categories ... )
ORDER BY created ;
You will need to use fake columns aliases for nonidentical columns, if necessary.