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.
Related
I wonder if someone could help me with an SQL query.
I am trying to display all results except where in Table 2 both the userid column equals 1 and the hidden column equals 1.
I am basically trying to hide results from individual users based on their id and the value in the hidden column either 1 or empty.
I have so far managed to make a query that does the opposite and can't figure out how to change it. I have tried adding !=, <>, NOT and a few other things, but nothing is working for me!
Here is the query I am working with.
$stmt = $conn->prepare("SELECT tl.id, tl.name, tl.locale, uh.hidden
FROM theList AS tl
LEFT JOIN user_hidden_list AS uh ON uh.est_id = tl.id
WHERE uh.userid = '1' AND uh.hidden = '1'");
How do I display all results expect those with a uh.userid = 1 and uh.hidden = 1
UPDATE: Tables
Table: user_hidden_list
userid | Hidden | est_id
---------------------------
1 | 1 | 1
2 | 1 | 1
1 | 1 | 2
Table: theList
id | name | locale
------------------------
1 | Jacks | LDN
2 | MacD's | LDN
3 | BK | LDN
4 | Byron | LDN
So if I am logged in with and userid of:
1 I should see: BK and Byron.
2 I should see: MacD's, BK and Byron.
3 (or anything else) I should see: Jacks, MacD's, BK and Byron.
If i understood correctly, the table user_hidden_list mantains a relation between one user and the users he can't see. So for, example, user with id equal to 1 (matching on column userid) can't see users 1 and 2 (matching on column est_id).
So, for a particular user with ID = X, we can get the ID list of user he can't see like next:
SELECT
est_id
FROM
user_hidden_list
WHERE
userid = X AND hidden = 1;
Using the previous query, we can get the visible users for user X like on next query:
SELECT
tl.id, tl.name, tl.locale
FROM
theList AS tl
WHERE
tl.id NOT IN (SELECT est_id
FROM user_hidden_list
WHERE userid = X AND hidden = 1);
I'm sure there will be a better (elegant) way to do this, but i'm just leaving work and my mind is not working nice now.
I am trying to display all results except where in Table 2 both the userid column equals 1 and the hidden column equals 1.
This does not suggest an outer join. You seem to want:
SELECT tl.id, tl.name, tl.locale, uh.hidden
FROM theList tl JOIN
user_hidden_list uh
ON uh.est_id = tl.id
WHERE NOT (uh.userid = 1 AND uh.hidden = 1);
I am guessing that userid and hidden are numbers of some sort, so I removed the single quotes. If they are really strings, then use the single quotes.
This also assumes that these values cannot be NULL. If that is a possibility, then the logic can be adjusted (using the null-safe comparator <=>).
Try out this, with even parantheses:
SELECT tl.id, tl.name, tl.locale, uh.hidden
FROM theList tl JOIN
user_hidden_list uh
ON uh.est_id = tl.id
WHERE (uh.userid <> 1 AND uh.hidden <> 1);
I can't figure out how to get results from 2 tables, in 1 query result (can't simple JOIN)
I have these 2 tables in my MySQL database:
Table 1: sales
id
name
info
Table 2: users
sale_id
user_id
Now, every sale have different number of assigned users. Some sale have 2 users, some sale have 10 users.
In single row, I need to have columns from sale table, and all assigned users to it (connected with same Sale_id)
I need result, something like this:
enter image description here
Try this :
SELECT s.*,
(SELECT GROUP_CONCAT(u.user_id SEPARATOR ', ')
FROM users u
WHERE u.sale_id = s.id) AS users
FROM sales s
Some insight on your programming language would have been nice.
And yes, as suggested by wogsland and icoder, one typically use joins and loop through results to build en array. But the use of GROUP_CONCAT, as Yoleth pointed out, is what you need. I don’t know if it was the goal here, but it can reduce memory used in the result because there is no row repetition.
SELECT info FROM Sales AS s,
(
SELECT sale_id, GROUP_CONCAT(user_id) AS assigned_users
FROM Users
GROUP BY sale_id) AS u
WHERE s.id=u.sale_id;
In a single query, with a fancy JOIN:
SELECT s.info AS info, u.sale_id AS sale_id, GROUP_CONCAT(u.user_id) AS assigned_users
FROM Sales AS s LEFT JOIN Users AS u
ON s.id=u.sale_id
WHERE sale_id IS NOT NULL GROUP BY u.sale_id;
You can simply join two tables and get query result set like this:
saleID | saleName | userID | userName
1 | Oct Sale | 5 | Tim
1 | Oct Sale | 6 | Nik
2 | Nov Sale | 7 | Bill
Then you can walk each row and build associative array from that data:
$sales = array();
while( $row = mysqli_fetch_assoc($result)) {
if (!array_key_exists($row['saleID'], $sales)) {
$sales[$row['saleID']] = array(
'saleID' => $row['saleID'],
'saleName' => $row['saleName'],
'users' => array()
);
}
array_push($sales[$row['saleID']]['users'], array(
'userID' => $row['userID'],
'userName' => $row['userName']
));
}
Well, MySQL isn't going to return you a nice nested array like that. But you can create it by looping through the result. Assuming your MySQL connection is named $mysqli then try something like
$sales = array();
$result = $mysqli->query("SELECT sales.*, users.user_id FROM sales, users WHERE sales.id = users.sales_id");
while ($row = $result->fetch_assoc()) {
$sales[$row->id]['sales_id'] = $row->id;
$sales[$row->id]['name'] = $row->name;
$sales[$row->id]['info'] = $row->info;
$sales[$row->id]['assigned_users'][] = $row->user_id;
}
In my database I have a table (likes) that lists items that users have liked.
When visiting a users profile I need to determine how many of those "likes" we have in common.
What's the best way to go about writing a query that will display the mutual likes between users?
likes table
id | user | item | activated
-----------------------------------
1 | 3 | 14 | 1
2 | 4 | 14 | 1
I need to return 14 in this example.
Suppose should be something like this:
select userViewer.item, items.itemName
from likes userViewer,
likes userProfile,
items
where userProfile.user = $profileUserId
and userViewer.user = $userViewer
and userProfile.item = userViewer.item
and items.item = userViewer.item
where $profileUserId - userId of the profile user and $userViewer - userId of current user
Same query on "join on" form with "items" table, as example:
select userViewer.item, items.itemName
from likes userViewer inner join likes userProfile
on userProfile.user = $profileUserId
and userViewer.user = $userViewer
and userProfile.item = userViewer.item
inner join items
on userViewer.item = items.item
I wants to know why you have not used matching caondition in where clause
SELECT
userViewer.item, items.itemName
FROM
likes AS userViewer
INNER JOIN
likes as userProfile
ON
userProfile.item = userViewer.item
INNER JOIN items
ON
userViewer.item = items.item
WHERE
userViewer.user = $userViewer
AND
userProfile.user = $profileUserId
I am just getting started in learning how to do INNER JOINS correctly and I can't think of the best/easiest way to do this.
I am building a url shortener and I am trying to build a query that will get all long_url.destination's matching a slug "test". One slug might point to multiple long_url.destination's(URL shuffling, GEO matching, etc...). So I need the slug to get all long_url.destination's with the same short_url.slug.
Before I was running another query to get the short_id from the slug, then running another query to select all rows in long_url that had a matching short_id.
I think it might be quicker if I use an inner join, but I am unsure how to properly set it up.
I want to get all destination columns in table long_url with only the slug data in short_url without having to run a separate query to get the short_id from the slug.
Table: short_url
Columns: short_id | slug | enabled | timestamp
example: 1 test 1 1323343922
Table: long_url
Columns: long_id | short_id | destination | geo | enabled | timestamp
example: 1 1 http://www.test.com US 1 132334922
example: 2 1 http://www.test.co.uk UK 1 132334922
I got this so far:
SELECT destination, geo FROM long_url INNER JOIN short_url
ON long_url.short_id = short_url.short_id WHERE enabled = 1;
function get_long_urls($slug) {
$query = "SELECT....";
$stmt = $db->prepare($query);
$stmt->execute(array(':slug' => $slug));
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
return (array) $results:
}
example $results = array(
'http://www.test.com' => 'US',
'http://www.test.co.uk' => 'UK',
);
Thanks for any help.
select long_url.destination
, long_url.geo
from long_url
inner
join short_url
on long_url.short_id = short_url.short_id
where short_url.slug = :slug
and long_url.enabled = 1
You don't need to qualify all column names like I did, because in this particular query there wasn't any ambiguity. All I really did is add a bound parameter placeholder.
SELECT destination, geo FROM long_url LEFT JOIN short_url
ON (long_url.short_id = short_url.short_id) WHERE enabled = 1
Will do my best to describe the problem Im having :)
Each thread/topic in my forum represents one disc. Registered members of the forum use a series of checkboxes (one displayed next to each disc) to tick each disc that they have in their collection. When the form is $_POST'ed it stores the information in a table like so:
| user_id - disc_id |
+--------------------+
| 2 - 571 |
| 2 - 603 |
| 2 - 4532 |
When the user next views the forum I have the checkboxes ticked and disabled on discs that the user owns. This is done using:
$sql = 'SELECT id, poster, subject, posted, last_post, last_post_id,
last_poster, num_views, num_replies, closed, sticky, moved_to, topicimage,
c.user_id, c.disc_id FROM topics LEFT JOIN collections AS c ON c.disc_id=id
WHERE forum_id='.$id.' ORDER BY sticky DESC;
The above grabs all of the discs, which I then display using the following (stripped down) code:
$result = $db->query($sql) or error('Unable to fetch topic list '.$sql.'', __FILE__, __LINE__, $db->error());
// If there are topics in this forum
if ($db->num_rows($result))
{
while ($cur_topic = $db->fetch_assoc($result))
{
// If logged in users ID matches the current discs user_id (i.e if this user owns this disc)
if ($cur_topic['user_id']==$pun_user['id']) {
$read = ' I own this!';
} else {
$read = ' I own this!';
}
}
}
This works great, until a second user adds the same disc ID to his collection, eg:
| user_id - disc_id |
+--------------------+
| 2 - 571 |
| 2 - 603 |
| 6 - 571 |
This causes a duplicate thread to appear in the forum. One is correctly ticked (because I own it), the other is not ticked, though it shares all of the same information such as topic id and image.
My first thought was to try adding GROUP BY c.disc_id to the SQL, which does successfully remove the duplicate topic - However, it is removing the wrong one. The disc that I have ticked is no longer shown, leaving only the unticked version.
Hope that makes sense. Can anyone offer any insight or ideas?
Many Thanks.
This is a guess, since I don't know your schema, but I don't see you specifying the user's ID in your WHERE clause.
What about something like the following?
SELECT t.id, t.poster, t.subject, t.posted, t.last_post, t.last_post_id,
t.last_poster, t.num_views, t.num_replies, t.closed, t.sticky,
t.moved_to, t.topicimage, c.user_id, c.disc_id
FROM topics AS t LEFT JOIN collections AS c ON c.disc_id = t.id
WHERE forum_id = '.$id.'
AND c.user_id = '.$user_id.'
ORDER BY t.sticky DESC;
Also, you're joining on Topic ID = Disc ID. Is that intentional?
I can see two easy way for solving this:
first:
with two query, you group query and a second to fetch all the disc_id owned by the user
second:
with your first query:
if ($db->num_rows($result)) {
$array = Array();
while ($cur_topic = $db->fetch_assoc($result)) {
$id = $cur_topic['disc_id'];
if (!array_key_exists ($id, $array)) { // allow only result per disc_id
$array[$id] = $cur_topic;
$array[$id]['owned'] = false;
}
// If logged in users ID matches the current discs user_id (i.e if this user owns this disc)
if ($cur_topic['user_id']==$pun_user['id']) // check if one is owned by the user
$array['owned'] = true;
}
foreach ($array as $cur_topic) {
if ($cur_topic['owned']) {
$read = '<br /><input type="checkbox" disabled="disabled" checked="checked" /> <span style="color:#999">I own this!</span>';
} else {
$read = '<br /><input type="checkbox" name="discs[]" value="'.$cur_topic['id'].'" /> I own this!';
}
}
}