MySQL 3 table search with one table not directly related - php

I have 3 tables :
wp_users - stores main information about all users,
wp_usermeta - stores additional information about users(first/last name/etc),
wp_friends - stores information about friends from third party services related to a specific user from wp_users
If you are not familiar with WordPress, you can see the structure of both tables at http://codex.wordpress.org/images/9/9e/WP3.0-ERD.png
The structure of my custom table wp_friends is as follows:
CREATE TABLE wp_friends (
id bigint(20) unsigned NOT NULL auto_increment,
uid bigint(20) unsigned NOT NULL default '0',
fr_id VARCHAR (60) NOT NULL default '',
service VARCHAR (20) NOT NULL default '',
name VARCHAR (80) NOT NULL default '',
photo VARCHAR (255) NOT NULL default '',
PRIMARY KEY (id),
KEY uid (uid),
KEY fr_id (fr_id),
KEY service (service)
)`
The uid column is corresponds to the ID column in the wp_users table - this is how I determine which record corresponds to which user.
What I'm trying to do is to create a query that will look in all of the three tables for a match against a keyword. Here is what I've come with so far(the first part was generated by a search function of WordPress):
SELECT
wp_users.ID,wp_users.display_name,wp_users.user_login,
wp_users.user_email,fr.fr_id,fr.name,fr.photo,fr.service
FROM wp_users
INNER JOIN wp_usermeta ON (wp_users.ID = wp_usermeta.user_id)
LEFT JOIN wp_socialaccess_friends AS fr ON fr.uid = 2
WHERE
(
(user_login LIKE '%nik%' OR user_nicename LIKE '%nik%')
AND
(wp_usermeta.meta_key = 'wp_user_level' AND CAST(wp_usermeta.meta_value AS CHAR) != '0')
)
OR ( fr.uid = 2 AND (fr.fr_id LIKE '%nik%' OR fr.name LIKE '%nik%'))
GROUP BY wp_users.ID,fr.fr_id ORDER BY user_login ASC
In the above query, the keyword is "nik"(which also matches a user_login column). The fr.uid part is needed so the returned results are only for the current user. The query fails in the following ways:
It returns all rows from the wp_friends table(because the user_login column is matched as well), that have wp_friends.uid = 2
It returns rows that have wp_friends.uid = 2 but matched with users where wp_users.ID != 2
Is it possible to create a single query, that would return the selected columns, but will also prevent duplicates?

What about joining on a sub-select like:
SELECT
wp_users.ID,wp_users.display_name,wp_users.user_login,
wp_users.user_email
FROM wp_users
INNER JOIN wp_usermeta ON (wp_users.ID = wp_usermeta.user_id)
left join(
select fr_id, uid,name,photo,service from wp_socialaccess_friends where
uid = 2 and
(fr_id LIKE '%nik%' OR name LIKE '%nik%')
) AS fr ON wp_users.ID = fr.uid
WHERE
(
(user_login LIKE '%nik%' OR user_nicename LIKE '%nik%')
AND
(wp_usermeta.meta_key = 'wp_user_level' AND CAST(wp_usermeta.meta_value AS CHAR) != '0')
)
GROUP BY wp_users.ID,fr.fr_id ORDER BY user_login ASC

Related

Subquery in INSERT executed differently in PDO than SQL

I want to insert a new dataset into a MySQL table tab with external data, but also with data from another table otherTab using the others' table primary key and another condition. However, it could be that the requested row simply does not exist (anymore) or the result set is empty due to a mismatch in the other supplied data. Then, the original INSERT should fail. All columns are forbidden to be NULL.
My first attempt was:
INSERT INTO tab (id, extid1, extid2, value)
SELECT 1,
(SELECT id FROM otherTab WHERE id = 12 AND data = 'TXT'),
(SELECT id FROM otherTab WHERE id = 34 AND data = 'JPG'),
1234
but the problem with it is that a returned empty result set is cast to the type of the column in tab, leading to a 0 as entry data.
The query shall be efficient and avoid unnecessary querying. This is how I achieve it with four subqueries:
INSERT INTO tab (id, extid1, extid2, value)
SELECT 1,
(SELECT id FROM otherTab WHERE id = 12 AND data = 'TXT'),
(SELECT id FROM otherTab WHERE id = 34 AND data = 'JPG'),
1234
WHERE EXISTS (SELECT id FROM otherTab WHERE id = 12 AND data = 'TXT')
AND EXISTS (SELECT id FROM otherTab WHERE id = 34 AND data = 'JPG')
I tried with other constructs, e.g. (SELECT IFNULL(SELECT id FROM otherTab WHERE id = 12 AND data = 'TXT', NULL)) to enforce NULL or even a string into the target column, but it also gets casted to a 0 or some value instead.
Here is the code for dbFiddle:
code
CREATE TABLE `tab` (
`id` int NOT NULL,
`seUuid4` binary(16) NOT NULL,
`rxUuid4` binary(16) NOT NULL,
`text` varchar(16)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
CREATE TABLE `otherTab` (
`uuid4` binary(16) NOT NULL,
`lgUuid4` binary(16) NOT NULL,
`data` varchar(16)
) ENGINE = InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
ALTER TABLE `otherTab`
ADD PRIMARY KEY(`uuid4`);
ALTER TABLE `tab`
ADD CONSTRAINT `tab_ibfk_1` FOREIGN KEY (`rxUuid4`) REFERENCES `otherTab` (`uuid4`) ON DELETE RESTRICT ON UPDATE RESTRICT,
ADD CONSTRAINT `tab_ibfk_2` FOREIGN KEY (`seUuid4`) REFERENCES `otherTab` (`uuid4`) ON DELETE RESTRICT ON UPDATE RESTRICT;
INSERT INTO `otherTab` (uuid4, lgUuid4, data) VALUES
(UNHEX("22224444aaaa49c782408b2fe8c4dee0"), UNHEX("00001234aaaa4444aaaa432187654321"), "JPG"),
(UNHEX("11113333aaaa49c782408b2fe8c4dee0"), UNHEX("12340000bbbb6666bbbb432187654321"), "TXT");
INSERT INTO tab (id, seUuid4, rxUuid4, text)
SELECT
1,
(SELECT uuid4 FROM otherTab WHERE lgUuid4 = UNHEX('00001234aaaa4444aaaa432187654321') AND data = 'JPK' LIMIT 0,1),
(SELECT uuid4 FROM otherTab WHERE lgUuid4 = UNHEX('12340000bbbb6666bbbb432187654321') AND data = 'TXT' LIMIT 0,1),
'some text'
This interestingly works exactly as expected: Note the JPK instead of JPG. I verified my code and the PDO prepared statement fires out exactly the same command, but it gets inserted as INSERT INTO tab (id, seUuid4, rxUuid4) VALUES (1, 0x00000000000000000000000000000000, 0x00000000000000000000000000000000, 'datatext'); while the SQL client and phpMyadmin deliver the expected cannot insert null error message.
I could not find anything in the PDO options. If it helps, I use PDO with emulated prepared statements, but also tried without with no change.
PS: I posted already at dba.stackexchange.com/posts/276868
You could use a subquery:
INSERT INTO tab (id, extid1, extid2, value)
SELECT
FROM (
SELECT
1 id,
(SELECT id FROM otherTab WHERE id = 12 AND data = 'TXT') extid1,
(SELECT id FROM otherTab WHERE id = 34 AND data = 'JPG') extid2
1234 value
) t
WHERE extid1 IS NOT NULL and extid2 IS NOT NULL
Or, probably better yet, you can CROSS JOIN the two subqueries:
INSERT INTO tab (id, extid1, extid2, value)
SELECT 1, t1.id, t2.id, 1234
FROM (SELECT id FROM otherTab WHERE id = 12 AND data = 'TXT') t1,
CROSS JOIN (SELECT id FROM otherTab WHERE id = 34 AND data = 'JPG') t2
Actually, since you are reurning the same value that you are filtering on, two exists subqueries are probably sufficient:
INSERT INTO tab (id, extid1, extid2, value)
SELECT t.*
FROM (SELECT 1 id, 12 extid1, 34 extid2, 1234 value) t
WHERE EXISTS (SELECT 1 FROM otherTab t1 WHERE t1.id = t.extid1 AND t1.data = 'TXT')
AND EXISTS (SELECT 1 FROM otherTab t1 WHERE t1.id = t.extid2 AND t1.data = 'JPG')

How to query for many to many relationship between products and filters in MySQL?

I have three tables viz. tb_filters, tb_products, and tb_products_to_filters. The structure of these tables along with some dummy data is given by:
tb_filters:
CREATE TABLE IF NOT EXISTS `tb_filters`
(
`filter_id` INT (11) AUTO_INCREMENT PRIMARY KEY,
`filter_name` VARCHAR (255)
);
INSERT INTO `tb_filters`
(`filter_name`)
VALUES ('USB'),
('High Speed'),
('Wireless'),
('Ethernet');
tb_products:
CREATE TABLE IF NOT EXISTS `tb_products`
(
`product_id` INT (11) AUTO_INCREMENT PRIMARY KEY,
`product_name` VARCHAR (255)
);
INSERT INTO `tb_products`
(`product_name`)
VALUES ('Ohm precision shunt resistor'),
('Orchestrator Libraries'),
('5cm scanner connection'),
('Channel isolated digital'),
('Network Interface Module');
tb_products_to_filters:
CREATE TABLE IF NOT EXISTS `tb_products_to_filters`
(
`id` INT (11) AUTO_INCREMENT PRIMARY KEY,
`product_id` INT (11),
`filter_id` INT (11)
);
INSERT INTO `tb_products_to_filters`
(`product_id`, `filter_id`)
VALUES (1, 1),
(2, 2),
(3, 3),
(4, 3),
(1, 3);
By looking into above "tb_products_to_filters" table, my required queries are:
When filter id = 1 and 3 are selected via checkbox on the page, all those products which belong to filter id 1 as well as filter id 3 must be fetched from the database. In this case, the product with id 1 should come.
Second, when only one filter (say id = 3) is checked, then all those products which fall under this id should be fetched. In this condition, the products id 1, 3 and 4 will come.
If filter id 2 is selected, then only one product with id = 2 will come.
If combination of filter (2 and 3) is selected, then no product will come because there is no product which belongs to both of them.
What is the way of writing queries to obtain above goal?
Please note that I want to include columns: product_id, product_name, filter_id and filter_name to display data in table result set.
EDIT:
The output should match below when filter ids 1 and 3 were checked:
EDIT 2:
I'm trying below query to fetch results when filter 1 and 3 were checked:
SELECT `p`.`product_id`, `p`.`product_name`,
GROUP_CONCAT(DISTINCT `f`.`filter_id` ORDER BY `f`.`filter_id` SEPARATOR ', ') AS filter_id, GROUP_CONCAT(DISTINCT `f`.`filter_name` ORDER BY `f`.`filter_name` SEPARATOR ', ') AS filter_name
FROM `tb_products` AS `p` INNER JOIN `tb_products_to_filters` AS `ptf`
ON `p`.`product_id` = `ptf`.`product_id` INNER JOIN `tb_filters` AS `f`
ON `ptf`.`filter_id` = `f`.`filter_id` GROUP BY `p`.`product_id`
HAVING GROUP_CONCAT(DISTINCT `ptf`.`filter_id` SEPARATOR ', ') = ('1,3')
ORDER BY `p`.`product_id`
But unfortunately, it returns an empty set. Why?
You can use the HAVING clause with GROUP_CONCAT :
SELECT t.product_id,tp.product_name,
GROUP_CONCAT(t.filter_id) as filter_id,
GROUP_CONCAT(tb.filter_name) as filter_name
FROM tb_products_to_filters t
INNER JOIN tb_filters tb ON(t.filter_id = tb.filter_id)
INNER JOIN tb_products tp ON(t.product_id = tp.product_id)
WHERE t.filter_id IN(1,3)
GROUP BY t.product_id
HAVING COUNT(distinct t.filter_id) = 2
You can adjust this any way you want. Note that the number of arguments placed inside the IN() should be the same as the COUNT(..) = X
EDIT:
A DISTINCT keyword is required in GROUP_CONCAT while fetching those columns otherwise all the filters would come in the list. I tried it by doing
SELECT t.product_id,tp.product_name,
GROUP_CONCAT(DISTINCT t.filter_id ORDER BY `t`.`filter_id` SEPARATOR ', ') as filter_id,
GROUP_CONCAT(DISTINCT tb.filter_name ORDER BY tb.filter_name SEPARATOR ', ') as filter_name
FROM tb_products_to_filters t
INNER JOIN tb_filters tb ON(t.filter_id = tb.filter_id)
INNER JOIN tb_products tp ON(t.product_id = tp.product_id)
WHERE t.filter_id IN(1,3)
GROUP BY t.product_id
HAVING COUNT(distinct t.filter_id) = 2
But still all the filter names (Ethernet, High Speed, USB, Wireless) are coming in the list. How to list only those filter names whose corresponding filter id (1, 3) are in the string?

How to query the second column of a JOIN table?

I would like to query the name of the friends of person n°4, so the numbers in 'otherPerson_id', but my attempt only query the id or name in 'person_id'. How could I ask to query the informations about the friends of person n°4, and not the information about person n°4 itself?
Here is my attempt :
$q = "SELECT DISTINCT p.idperson, p.name FROM person p INNER JOIN people_friends pf ON p.idperson = pf.person_id AND p.idperson = 4";
$res = $connexion->query($q);
$res->setFetchMode(PDO::FETCH_COLUMN);
while($record = $res->fetch()) {
echo $record[name];
echo "<br/>";
}
the table : (so when I look for the friends of person n°4, I would like it to return 1 and 7 )
EDIT :
CREATE TABLE people_friends
(
person_id integer NOT NULL,
otherperson_id integer NOT NULL,
CONSTRAINT people_friends_pkey PRIMARY KEY (person_id, otherperson_id),
CONSTRAINT people_friends_person_id_fkey FOREIGN KEY (person_id)
REFERENCES person (idperson) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION,
CONSTRAINT people_friends_person_id_fkey1 FOREIGN KEY (person_id)
REFERENCES person (idperson) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
)
CREATE TABLE person
(
idperson integer NOT NULL,
name character varying(50),
map bytea,
CONSTRAINT person_pkey PRIMARY KEY (idperson)
)
Thanks
Try this query.
SELECT DISTINCT
pFriend.idperson, pFriend.name
FROM people_friends pf
INNER JOIN person pFriend on pf.otherperson_id = pFriend.idperson
WHERE
pf.person_id = 4
You just have to add a WHERE clause where you check the pf table.
SELECT DISTINCT p.idperson
,p.name
FROM person p
INNER JOIN people_friends pf
ON p.idperson = pf.person_id
WHERE pf.otherperson_id = 4

How to order comments by likes/dislikes in MySQL

On my website people can thumbs up or thumbs down a comment.
To do this I use two tables:
$sql = "CREATE TABLE content
(
id INT NOT NULL AUTO_INCREMENT,
PRIMARY KEY(id),
content TEXT NOT NULL,
date date,
time time
)";
and
$sql2 = "CREATE TABLE ratings
(
rating_id INT AUTO_INCREMENT PRIMARY KEY NOT NULL ,
rating VARCHAR (10) NOT NULL ,
id INT NOT NULL ,
ip VARCHAR (50) NOT NULL
)";
The data stored in the ratings would be as follows:
Comment ID like/dislike user IP
1 l 86.42.173.83
1 d 86.42.173.43
2 l 86.42.173.79
2 l 86.42.173.34
2 d 86.42.173.22
The problem I'm having is that I'm finding it extremely difficult to create a SQL statement to order the comments by the amount of likes they have.
If anyone has any ideas on how to do this it would be greatly appreciated.
It would be easier if you stored likes as integers and not letters.
I added up the likes using a case statement and grouped by comment.
SELECT C.content,
SUM(CASE WHEN R.rating = 'l' THEN 1 ELSE -1 END) AS overallRating
FROM content C
LEFT JOIN ratings R ON R.id = C.id
GROUP BY C.content
ORDER BY overallRating
something like this will work
select content.text, count(*) likes
from content join ratings on content.id = ratings.id
group by context.text
order by likes

MySQL inclusion/exclusion of posts

This post is taking a substantial amount of time to type because I'm trying to be as clear as possible, so please bear with me if it is still unclear.
Basically, what I have are a table of posts in the database which users can add privacy settings to.
ID | owner_id | post | other_info | privacy_level (int value)
From there, users can add their privacy details, allowing it to be viewable by all [privacy_level = 0), friends (privacy_level = 1), no one (privacy_level = 3), or specific people or filters (privacy_level = 4). For privacy levels specifying specific people (4), the query will reference the table "post_privacy_includes_for" in a subquery to see if the user (or a filter the user belongs to) exists in a row in the table.
ID | post_id | user_id | list_id
Also, the user has the ability to prevent some people from viewing their post in within a larger group by excluding them (e.g., Having it set for everyone to view but hiding it from a stalker user). For this, another reference table is added, "post_privacy_exclude_from" - it looks identical to the setup as "post_privacy_includes_for".
My problem is that this does not scale. At all. At the moment, there are about 1-2 million posts, the majority of them set to be viewable by everyone. For each post on the page it must check to see if there is a row that is excluding the post from being shown to the user - this moves really slow on a page that can be filled with 100-200 posts. It can take up to 2-4 seconds, especially when additional constraints are added to the query.
This also creates extremely large and complex queries that are just... awkward.
SELECT t.*
FROM posts t
WHERE ( (t.privacy_level = 3
AND t.owner_id = ?)
OR (t.privacy_level = 4
AND EXISTS
( SELECT i.id
FROM PostPrivacyIncludeFor i
WHERE i.user_id = ?
AND i.thought_id = t.id)
OR t.privacy_level = 4
AND t.owner_id = ?)
OR (t.privacy_level = 4
AND EXISTS
(SELECT i2.id
FROM PostPrivacyIncludeFor i2
WHERE i2.thought_id = t.id
AND EXISTS
(SELECT r.id
FROM FriendFilterIds r
WHERE r.list_id = i2.list_id
AND r.friend_id = ?))
OR t.privacy_level = 4
AND t.owner_id = ?)
OR (t.privacy_level = 1
AND EXISTS
(SELECT G.id
FROM Following G
WHERE follower_id = t.owner_id
AND following_id = ?
AND friend = 1)
OR t.privacy_level = 1
AND t.owner_id = ?)
OR (NOT EXISTS
(SELECT e.id
FROM PostPrivacyExcludeFrom e
WHERE e.thought_id = t.id
AND e.user_id = ?
AND NOT EXISTS
(SELECT e2.id
FROM PostPrivacyExcludeFrom e2
WHERE e2.thought_id = t.id
AND EXISTS
(SELECT l.id
FROM FriendFilterIds l
WHERE l.list_id = e2.list_id
AND l.friend_id = ?)))
AND t.privacy_level IN (0, 1, 4))
AND t.owner_id = ?
ORDER BY t.created_at LIMIT 100
(mock up query, similar to the query I use now in Doctrine ORM. It's a mess, but you get what I am saying.)
I guess my question is, how would you approach this situation to optimize it? Is there a better way to set up my database? I'm willing to completely scrap the method I have currently built up, but I wouldn't know what to move onto.
Thanks guys.
Updated: Fix the query to reflect the values I defined for privacy level above (I forgot to update it because I simplified the values)
Your query is too long to give a definitive solution for, but the approach I would follow is to simply the data lookups by converting the sub-queries into joins, and then build the logic into the where clause and column list of the select statement:
select t.*, i.*, r.*, G.*, e.* from posts t
left join PostPrivacyIncludeFor i on i.user_id = ? and i.thought_id = t.id
left join FriendFilterIds r on r.list_id = i.list_id and r.friend_id = ?
left join Following G on follower_id = t.owner_id and G.following_id = ? and G.friend=1
left join PostPrivacyExcludeFrom e on e.thought_id = t.id and e.user_id = ?
(This might need expanding: I couldn't follow the logic of the final clause.)
If you can get the simple select working fast AND including all the information needed, then all you need to do is build up the logic in the select list and where clause.
Had a quick stab at simplifying this without re-working your original design too much.
Using this solution your web page can now simply call the following stored procedure to get a list of filtered posts for a given user within a specified period.
call list_user_filtered_posts( <user_id>, <day_interval> );
The whole script can be found here : http://pastie.org/1212812
I haven't fully tested all of this and you may find this solution isn't performant enough for your needs but it may help you in fine tuning/modifying your existing design.
Tables
Dropped your post_privacy_exclude_from table and added a user_stalkers table which works pretty much like the inverse of user_friends. Kept the original post_privacy_includes_for table as per your design as this allows a user restrict a specific post to a subset of people.
drop table if exists users;
create table users
(
user_id int unsigned not null auto_increment primary key,
username varbinary(32) unique not null
)
engine=innodb;
drop table if exists user_friends;
create table user_friends
(
user_id int unsigned not null,
friend_user_id int unsigned not null,
primary key (user_id, friend_user_id)
)
engine=innodb;
drop table if exists user_stalkers;
create table user_stalkers
(
user_id int unsigned not null,
stalker_user_id int unsigned not null,
primary key (user_id, stalker_user_id)
)
engine=innodb;
drop table if exists posts;
create table posts
(
post_id int unsigned not null auto_increment primary key,
user_id int unsigned not null,
privacy_level tinyint unsigned not null default 0,
post_date datetime not null,
key user_idx(user_id),
key post_date_user_idx(post_date, user_id)
)
engine=innodb;
drop table if exists post_privacy_includes_for;
create table post_privacy_includes_for
(
post_id int unsigned not null,
user_id int unsigned not null,
primary key (post_id, user_id)
)
engine=innodb;
Stored Procedures
The stored procedure is relatively simple - it initially selects ALL posts within the specified period and then filters out posts as per your original requirements. I have not performance tested this sproc with large volumes but as the initial selection is relatively small it should be performant enough as well as simplifying your application/middle tier code.
drop procedure if exists list_user_filtered_posts;
delimiter #
create procedure list_user_filtered_posts
(
in p_user_id int unsigned,
in p_day_interval tinyint unsigned
)
proc_main:begin
drop temporary table if exists tmp_posts;
drop temporary table if exists tmp_priv_posts;
-- select ALL posts in the required date range (or whatever selection criteria you require)
create temporary table tmp_posts engine=memory
select
p.post_id, p.user_id, p.privacy_level, 0 as deleted
from
posts p
where
p.post_date between now() - interval p_day_interval day and now()
order by
p.user_id;
-- purge stalker posts (0,1,3,4)
update tmp_posts
inner join user_stalkers us on us.user_id = tmp_posts.user_id and us.stalker_user_id = p_user_id
set
tmp_posts.deleted = 1
where
tmp_posts.user_id != p_user_id;
-- purge other users private posts (3)
update tmp_posts set deleted = 1 where user_id != p_user_id and privacy_level = 3;
-- purge friend only posts (1) i.e where p_user_id is not a friend of the poster
/*
requires another temp table due to mysql temp table problem/bug
http://dev.mysql.com/doc/refman/5.0/en/temporary-table-problems.html
*/
-- the private posts (1) this user can see
create temporary table tmp_priv_posts engine=memory
select
tp.post_id
from
tmp_posts tp
inner join user_friends uf on uf.user_id = tp.user_id and uf.friend_user_id = p_user_id
where
tp.user_id != p_user_id and tp.privacy_level = 1;
-- remove private posts this user cant see
update tmp_posts
left outer join tmp_priv_posts tpp on tmp_posts.post_id = tpp.post_id
set
tmp_posts.deleted = 1
where
tpp.post_id is null and tmp_posts.privacy_level = 1;
-- purge filtered (4)
truncate table tmp_priv_posts; -- reuse tmp table
insert into tmp_priv_posts
select
tp.post_id
from
tmp_posts tp
inner join post_privacy_includes_for ppif on tp.post_id = ppif.post_id and ppif.user_id = p_user_id
where
tp.user_id != p_user_id and tp.privacy_level = 4;
-- remove private posts this user cant see
update tmp_posts
left outer join tmp_priv_posts tpp on tmp_posts.post_id = tpp.post_id
set
tmp_posts.deleted = 1
where
tpp.post_id is null and tmp_posts.privacy_level = 4;
drop temporary table if exists tmp_priv_posts;
-- output filtered posts (display ALL of these on web page)
select
p.*
from
posts p
inner join tmp_posts tp on p.post_id = tp.post_id
where
tp.deleted = 0
order by
p.post_id desc;
-- clean up
drop temporary table if exists tmp_posts;
end proc_main #
delimiter ;
Test Data
Some basic test data.
insert into users (username) values ('f00'),('bar'),('alpha'),('beta'),('gamma'),('omega');
insert into user_friends values
(1,2),(1,3),(1,5),
(2,1),(2,3),(2,4),
(3,1),(3,2),
(4,5),
(5,1),(5,4);
insert into user_stalkers values (4,1);
insert into posts (user_id, privacy_level, post_date) values
-- public (0)
(1,0,now() - interval 8 day),
(1,0,now() - interval 8 day),
(2,0,now() - interval 7 day),
(2,0,now() - interval 7 day),
(3,0,now() - interval 6 day),
(4,0,now() - interval 6 day),
(5,0,now() - interval 5 day),
-- friends only (1)
(1,1,now() - interval 5 day),
(2,1,now() - interval 4 day),
(4,1,now() - interval 4 day),
(5,1,now() - interval 3 day),
-- private (3)
(1,3,now() - interval 3 day),
(2,3,now() - interval 2 day),
(4,3,now() - interval 2 day),
-- filtered (4)
(1,4,now() - interval 1 day),
(4,4,now() - interval 1 day),
(5,4,now());
insert into post_privacy_includes_for values (15,4), (16,1), (17,6);
Testing
As I mentioned before I've not fully tested this but on the surface it seems to be working.
select * from posts;
call list_user_filtered_posts(1,14);
call list_user_filtered_posts(6,14);
call list_user_filtered_posts(1,7);
call list_user_filtered_posts(6,7);
Hope you find some of this of use.

Categories