I am making a dating app similar to tindler, where users can like or dislike each other. If two users both like each other, they should be able to chat with each other. I have come up with the following query to handle pulling a list of users you can chat/have chats with already - the problem i'm having is i only want to pull the most recent chat message, solely to display a little blurb before you click into the chat itself. My query works, but it returns the oldest (lowest ID) chat record, instead of the newest. Order by does not seem to have an impact on returning the correct result.
$data = $this->db->select('users.id,display_name,city,state,gender,users_pictures.picture,users_chats.message')
->join('users_pictures','users_pictures.user_id=users.id')
->join('users_chats','users_chats.user_id=users.id OR users_chats.foreign_user_id=users.id','left outer')
->where('EXISTS (SELECT 1 FROM users_likes_dislikes ld WHERE (ld.foreign_user_id = '.$this->user_id.' AND ld.user_id=users.id AND ld.event_type=1) OR (SELECT 1 FROM users_likes_dislikes ld WHERE ld.foreign_user_id = users.id AND ld.user_id='.$this->user_id.' AND ld.event_type=1))', '', FALSE)
->where('NOT EXISTS (SELECT 1 FROM users_blocks ub WHERE (ub.foreign_user_id = users.id AND ub.user_id='.$this->user_id.') OR (SELECT 1 FROM users_blocks ub WHERE ub.foreign_user_id = '.$this->user_id.' AND ub.user_id=users.id))', '', FALSE)
->where('((users_chats.user_id='.$this->user_id.' OR users_chats.foreign_user_id='.$this->user_id.') OR (users_chats.user_id is NULL AND users_chats.foreign_user_id is NULL))')
->order_by('users_chats.id','DESC')
->group_by('users.id')
->get('users')
->result_array();
Here is the current mysql table for users_chats:
id user_id foreign_user_id message created
1 1 4 test 2013-05-22 15:42:44
2 1 4 test2 2013-05-22 15:44:38
I assumed the order_by would ensure that the test2 message is what displayed.
Here is sample output:
Array ( [0] => Array ( [id] => 4 [display_name] => testinguser [city] => west hills [state] => ca [gender] => 2 [picture] => testasdfasdf.jpg [message] => test ) )
Any help is much appreciated :)
edit - the query itself (without group by, this works but i need it to group to the user.id so that i don't have multiple entries for the same user in the array):
SELECT
`users`.`id`,
`display_name`,
`city`,
`state`,
`gender`,
`users_pictures`.`picture`,
`users_chats`.`message`
FROM (`users`)
JOIN `users_pictures`
ON `users_pictures`.`user_id` = `users`.`id`
JOIN `users_chats`
ON `users_chats`.`user_id` = `users`.`id`
OR users_chats.foreign_user_id = users.id
WHERE EXISTS(SELECT
1
FROM users_likes_dislikes ld
WHERE (ld.foreign_user_id = 1
AND ld.user_id = users.id
AND ld.event_type = 1)
OR (SELECT
1
FROM users_likes_dislikes ld
WHERE ld.foreign_user_id = users.id
AND ld.user_id = 1
AND ld.event_type = 1))
AND NOT EXISTS(SELECT
1
FROM users_blocks ub
WHERE (ub.foreign_user_id = users.id
AND ub.user_id = 1)
OR (SELECT
1
FROM users_blocks ub
WHERE ub.foreign_user_id = 1
AND ub.user_id = users.id))
AND ((users_chats.user_id = 1
OR users_chats.foreign_user_id = 1)
OR (users_chats.user_id is NULL
AND users_chats.foreign_user_id is NULL))
ORDER BY `users_chats`.`created` DESC
Your group by clause could be the culprit here. I believe that the grouping operation happens first, leaving you with your first result.
Rather than selecting all these rows (when there's tons it'll take longer), you should specify how many you want - it looks to me like that isn't too far out of the picture here, anyways. Specify how many you want, get rid of the group by clause, and you should order by date since you HAVE a date column.
Does that help?
I'm not sure how to do it with your db Abstraction, but the query you want is
SELECT
`users`.`id`,
`display_name`,
`city`,
`state`,
`gender`,
`users_pictures`.`picture`,
chats1.`message`
FROM (`users`)
JOIN `users_pictures`
ON `users_pictures`.`user_id` = `users`.`id`
JOIN `users_chats` AS chats1
ON (chats1.`user_id` = `users`.`id`
OR chats1.foreign_user_id = users.id)
Here comes the important part
AND NOT EXISTS(
SELECT *
FROM users_chats AS chats2
WHERE ((chats2.user_id = chats1.user_id AND chats2.foreign_user_id = chats1.foreign_user_id)
OR (chats2.user_id = chats1.foreign_user_id AND chats1.user_id = chats2.foreign_user_id))
AND chats2.created_date > chats1.created_date --which I assume is a time stamp
)
It's not pretty, I know.
WHERE EXISTS(SELECT 1
FROM users_likes_dislikes ld
WHERE (ld.foreign_user_id = 1
AND ld.user_id = users.id
AND ld.event_type = 1)
OR (SELECT 1
FROM users_likes_dislikes ld
WHERE ld.foreign_user_id = users.id
AND ld.user_id = 1
AND ld.event_type = 1)
)
AND NOT EXISTS(SELECT 1
FROM users_blocks ub
WHERE (ub.foreign_user_id = users.id
AND ub.user_id = 1)
OR (SELECT
1
FROM users_blocks ub
WHERE ub.foreign_user_id = 1
AND ub.user_id = users.id)
)
AND ((chats1.user_id = 1
OR chats1.foreign_user_id = 1)
OR (chats1.user_id is NULL
AND chats1.foreign_user_id is NULL))
ORDER BY `users_chats`.`created` DESC
Basically, only successfully join if there's no more recent message. There are some better native solutions - TSQL (Microsoft SQL Server) has CROSS APPLY, which would be great here - but without knowing more about your DB layer, I can't be sure. You may want to considered re-architecting your chat structure:
Users(int id /*also other user info*/)
chats(int id, datetime date_initiated, bool /*or bit, or short int*/ is_active)
chat_users (int chat_id, int user_id)
chat_messages (int chat_id, int user_id /*author*/, datetime date_sent, varchar(n) message)
With a structure like that, you could get all your most recent messages like this:
SELECT *
FROM Users AS u
INNER JOIN chat_users AS cu
ON u.id = cu.user_id
INNER JOIN chats AS c
ON c.id = cu.chat_id
AND c.is_active = 1
INNER JOIN chat_messages AS m
ON m.chat_id = c.id
AND NOT EXISTS (
SELECT 1
FROM chat_messages AS m2
WHERE m2.chat_id = m.chat_id
AND m.date_sent < m2.date_sent
)
INNER JOIN Users as sender
ON m.user_id = sender.id
WHERE u.id = ###
ORDER BY m.date_sent DESC
You could even create a "Chat most recent message" view like:
CREATE VIEW Chat_Recent AS
SELECT * /* WHATEVER YOU LIKE */
FROM chats AS c
INNER JOIN chat_messages AS m
ON m.chat_id = c.id
AND NOT EXISTS (
SELECT 1
FROM chat_messages AS m2
WHERE m2.chat_id = m.chat_id
AND m.date_sent < m2.date_sent
)
INNER JOIN Users as sender
ON m.user_id = sender.id
Try MySQL MAX():
$this->db->join('(SELECT MAX(message) AS lastMsg FROM users_chats WHERE users_chats.user_id=users.id OR users_chats.foreign_user_id=users.id GROUP BY users.id)', 'left outer');
Then add the "lastMsg" on your select.
Related
i need select some data from two tables ,
please help me use inner join for this selection .
players in selction2 must not be in selection1...
first select :
$rs = "SELECT *
FROM `player`
WHERE `status`=1 AND `credit`>=1 AND `username` NOT LIKE '$user'
ORDER BY ls ASC,credit DESC
LIMIT 0 ,10;
Second: this players must remove from result of selection1
$rs2 = "SELECT *
FROM `ip_log`
WHERE `playerid`='$ui' AND `win`='1' AND `date`='$date' ";`
You can use LEFT JOIN for this:
This shows the log messages for everyone not in selection 1.
SELECT l.*
FROM ip_log AS l
LEFT JOIN
(SELECT username
FROM player
WHERE status = 1 AND credit >= 1 AND username NOT LIKE '$user'
ORDER BY ls ASC, credit DESC
LIMIT 10) AS p
ON l.player = p.username
WHERE win = 1 and date = '$date'
AND p.username IS NULL
This shows the top 10 player data, except the ones with log messages in selection 2
SELECT p.*
FROM player AS p
LEFT JOIN ip_log AS l ON l.player = p.username AND l.win = 1 AND l.date = '$date'
WHERE p.status = 1 AND p.credit >= 1 AND p.username NOT LIKE '$user'
AND l.player IS NULL
ORDER BY p.ls ASC, p.credit DESC
LIMIT 10
In both cases, testing a column in the second table with IS NULL makes it return only the rows in the first table that don't have a match in the second table. See
Return row only if value doesn't exist
You can do it with LEFT JOIN
SELECT player.*,ip_log.* FROM `player` LEFT JOIN `ip_log` ON player.id!=ip_log.playerid GROUP BY player.id
The following statement gets row counts for user_ids from various tables/conditions where the users are within specific computers of a specific account. It works as expected. An example output would be something like this :
Array
(
[0] => Array
(
[computer_name] => COMPUTER_1
[username] => Steve
[t1count] => 13
[t2count] =>
[t3count] => 23
[t4count] => 64
)
... and so on for each
the statement :
$stmt = $db->prepare("
SELECT c.computer_name, users.username, t1count, t2count, t3count, t4count
FROM
( SELECT account_id, computer_id, computer_name
FROM computers
WHERE account_id = ".$_SESSION['user']['account_id']."
ORDER BY computer_id ASC LIMIT 0, ".$_SESSION['user']['licenses']."
) as c
LEFT JOIN users
on users.computer_id = c.computer_id
LEFT JOIN
(SELECT user_id, COUNT(user_id) as t1count
FROM t1
WHERE t1.title LIKE 'started'
GROUP BY user_id) as t_t1
on t_t1.user_id = users.user_id
LEFT JOIN
(SELECT user_id, COUNT(user_id) as t2count
FROM t2
GROUP BY user_id) as t_t2
on t_t2.user_id = users.user_id
LEFT JOIN
(SELECT user_id, COUNT(user_id) as t3count
FROM t1
WHERE t1.title LIKE 'blocked'
GROUP BY user_id) as t_t3
on t_t3.user_id = users.user_id
LEFT JOIN
(SELECT user_id, COUNT(user_id) as t4count
FROM t1
WHERE t1.title LIKE 'closed'
GROUP BY user_id) as t_t4
on t_t4.user_id = users.user_id
... and so on for each
WHERE c.account_id = ?
");
I want to also return a totalsum = t1count + t2count + t3count + t4count in this statement as well, but can't seem to get anything working. In this situation I cannot do outside processing (adding the values that are returned)... it needs to be in the statement. I am also open to any suggestions in what I already have if there are better options.
What are you trying to do with these values? SUM(t1count,t2count,t3count,t4count) AS totalsum should work to get a count of counts.
Your query is a bit hard to read. You have implicit and explicit JOINs. You are grouping numerous times and pulling columns when you can generally do this all in one shot. If you share an SQLFiddle, I will be able to clean this up, but in short:
SELECT
c.computer_name,
users.username,
count(t_t1.user_id) AS t1count,
count(t_t2.user_id) AS t2count,
count(t_t3.user_id) AS t3count,
count(t_t4.user_id) AS t4count,
(count(t_t1.user_id)+count(t_t2.user_id)+count(t_t3.user_id)+count(t_t4.user_id)) AS totalsum FROM users
LEFT JOIN computers AS c ON users.computer_id=c.computer_id AND c.account_id=?
LEFT JOIN t1 AS t_t1 ON t_t1.user_id = users.user_id AND t_t1.title LIKE "started"
LEFT JOIN t2 AS t_t2 ON t_t2.user_id = users.user_id
LEFT JOIN t1 AS t_t3 ON t_t3.user_id = users.user_id AND t_t3.title LIKE 'blocked'
LEFT JOIN t1 AS t_t4 ON t_t4.user_id = users.user_id AND t_t4.title LIKE 'closed'
WHERE c.account_id = ?
AND t_t1.title LIKE started
GROUP BY users.user_id;
This may need tweaking, as I stated, but it is a lot cleaner and easier to read and should accomplish something very similar.
Alternately, if you can't get the query to run the way you want it to when you change it to entirely explicit joins, rather than using SUM, try adding the values together like I did in the example above. It should prevent them from aggregating in the same way.
EDIT
After viewing your SQLFiddle, I have doctored up a solution which does away with nested queries. The positive is that it is cleaner. The negative is that it requires you to specify the users using an IN clause.
SELECT computers.account_id,computers.computer_id,computers.computer_name,users.user_id,users.username,count(distinct t_count1.log_id) AS count1,count(distinct t_count2.log_id) AS count2,count(distinct t_count3.log_id) AS count3, count(distinct t_count4.event_id) AS count4,
(count(distinct t_count1.log_id) + count(distinct t_count2.log_id) + count(distinct t_count3.log_id) + count(distinct t_count4.event_id)) AS totalcount
FROM users
INNER JOIN computers ON computers.computer_id=users.computer_id
LEFT JOIN logs AS t_count1 ON t_count1.type LIKE 'type1' AND t_count1.user_id=users.user_id
LEFT JOIN logs AS t_count2 ON t_count2.type LIKE 'type2' AND t_count2.user_id=users.user_id
LEFT JOIN logs AS t_count3 ON t_count3.type LIKE 'type3' AND t_count3.user_id=users.user_id
LEFT JOIN events AS t_count4 ON t_count4.user_id = users.user_id
WHERE computers.account_id=1 AND computers.computer_id in (1,2)
GROUP BY users.user_id
ORDER BY users.user_id ASC,computers.computer_id ASC;
If you choose to keep your current query structure for any reason, adapting it like so should make it work for you:
select *,ifnull(count1,0)+ifnull(count2,0)+ifnull(count3,0)+ifnull(count4,0) AS totalcount from
( select account_id, computer_id, computer_name
from computers
order by computer_id asc limit 0, 2
) as c
left join users
on users.computer_id = c.computer_id
left join
(select user_id, count(user_id) as count1
from logs
where logs.type like 'type1'
group by user_id) as t_count1
on t_count1.user_id = users.user_id
left join
(select user_id, ifnull(count(user_id),0) as count2
from logs
where logs.type like 'type2'
group by user_id) as t_count2
on t_count2.user_id = users.user_id
left join
(select user_id, count(user_id) as count3
from logs
where logs.type like 'type3'
group by user_id) as t_count3
on t_count3.user_id = users.user_id
left join
(select user_id, count(user_id) as count4
from events
group by user_id) as t_count4
on t_count4.user_id = users.user_id
where c.account_id = 1;
My advice would be to follow along each query to understand what you are asking SQL to do. To actually add all values together, you should be counting the number of records that are returned. Counting values of your primary keys helps you to count records. Also, using your other example, ifnull in the second example make sure that null values are not going to interfere with adding. "If a value is null make it 0 instead."
I'm trying to optimize the following query. I'm thinking an outer join would do the trick, but I can't wrap my mind around how to put it together.
// ---------------------------------
// Simplified representation of data
// ---------------------------------
create table views (
user_id,
article_id
)
create table article_attributes (
article_id,
article_attribute_id
)
create table articles (
id,
title,
date
)
Views table has tens of millions of records.
Articles table has a couple hundred thousand.
I'm trying to match all articles with a certain attribute associated with it, and that have not been viewed by a user.
What I have tried, but doesn't scale well:
select a.title, a.sid as article_id, a.total_views as times_read, a.date
from articles a
join article_attributes att on att.article_id = a.sid
where a.sid not in(
select v.article_id
from views v
join article_attributes att on att.article_id = v.article_id
where user_id = 132385
and att.article_attribute_id = 10
group by v.article_id
)
and att.article_attribute_id = 10
and a.date >= DATE_SUB(CURRENT_DATE(), INTERVAL 7 day)
order by total_views desc
limit 5
This works fine, but gets significantly slower the more articles the user has viewed. Any ideas or suggestions would be appreciated.
SELECT a.title, a.sid AS article_id, a.total_views AS times_read, a.date
FROM articles a
JOIN article_attributes att
ON a.id = att.article_id AND att.article_attribute_id = 10
LEFT JOIN views v
ON a.id = v.article_id AND v.user_id = 132385
WHERE v.user_id IS NULL
The first join gets you only the articles with the given attribute.
The second join takes the first join's result and returns rows with the user_id and all the remaining rows from first result that don't have the user_id.(Basically ALL articles with attribute 132385 with the user_id being either 10 or NULL)
Then all we want is that result where user_id is NULL
Try to avoid nested queries and let the engine do it's job. Note you can tag on your other filters (DATE, ORDER BY) on the end.
Try this query
select a.title, a.sid as article_id, a.total_views as times_read, a.date
from
articles a
left join
views v
on
a.sid = v.article_id AND v.article_id is null
join
article_attributes att
on
att.article_id = v.article_id AND v.user_id = 132385 AND att.article_attribute_id = 10
where
a.date >= DATE_SUB(CURRENT_DATE(), INTERVAL 7 day)
order by
total_views desc limit 5
Create necessary index for articles table (total_views, sid, date)
view table (article_id, user_id)
article_attributes table (article_id, article_attribute_id)
Hope this helps.
Instead of using the subquery as a where condition, I suggest using it in a join. Also, I suggest you not to use group by in your subquery, but select distinct:
select
a.title, a.sid as article_id, a.total_views as times_read, a.date
from
(articles a
inner join article_attributes att on a.sid = att.article_id)
left join (
select distinct
v.article_id
from views v
inner join article_attributes att on v.article_id = att.article_id
where
user_id = 132385
and att.article_atribute_id = 10
) as b on a.sid = b.article_id
where
b.article_id is null
and att.article_attribute_id = 10
and a.date >= DATE_SUB(CURRENT_DATE(), INTERVAL 7 day)
Hope this helps
EXISTS should work better than IN:
SELECT a.title,
a.sid AS article_id,
a.total_views AS times_read,
a.date
FROM articles a
JOIN article_attributes att ON att.article_id = a.sid
WHERE NOT EXISTS (SELECT 0
FROM views v
JOIN article_attributes att ON att.article_id = v.article_id
WHERE user_id = 132385
AND att.article_attribute_id = 10
AND v.article_id = a.sid )
AND att.article_attribute_id = 10
AND a.date >= DATE_SUB(CURRENT_DATE(), INTERVAL 7 DAY)
ORDER BY total_views DESC LIMIT 5
I'm writing sort of a travel-'dating' app.
Users register themselves
Users tell the app if they are male or female
Users tell the app which countries they would like to visit
Users tell the app if they want to travel with males (pref_m=1) or females (pref_f=1)
My tables
table 1: users
id (key) | gender | pref_m | pref_f
------------------------------------
1 male 1 0
2 male 1 1
table 2: countryselection
id (key) | userid | countryid
------------------------------------
1 1 123
2 1 111
3 1 100
4 1 110
5 2 123
6 2 111
7 2 202
8 2 210
So what the select statement has to do
Input: the userid of the current user
Output (in logic): SELECT the userids AND matching countries OF ALL people that want to travel to the same countries as I do, and want to travel with someone that has my gender
(join) Of that selection I obviously only need the people that are of the gender that I am looking for.
ORDERED by people that have the most matching countries with me DESC.
What I have so far (warning: not much)
$sql = "SELECT userid,count(*) AS matches from countryselection";
$sql .= " WHERE countryid IN (SELECT countryid FROM countryselection WHERE userid = :userid) GROUP BY userid ORDER BY matches DESC;";
This gives me a list of all people that want to travel to the same countries as me (and how many countries we have in common)
final note
I'm obviously struggling with the gender-selection part.
Not sure if I have done the right thing to store the user selections in the way that I have.
I might need some guidance there too.
Obviously - thanks all.
SELECT
us2.id, -- etc.
COUNT(cs2.countryid) as countries_in_common
FROM
countryselection cs1 -- let's gather user countries he want to visit
LEFT JOIN -- now let's find other users!
countryselection cs2 ON
(
cs2.userid <> :userid AND -- which are not him
cs2.countryid = cs1.countryid -- and want to visit same countries
)
INNER JOIN -- let's grab our user_data
users us1 ON
(
us1.id = cs1.userid
)
INNER JOIN -- and let's grab other user data!
users us2 ON
(
us2.id = cs2.userid
)
WHERE
cs1.userid = :userid AND -- finding our user countries he want to visit
-- final checks
(
(us1.pref_m = 1 AND us2.gender = 'male')
-- he is looking for male and second user is male
OR
(us1.pref_f = 1 AND us2.gender = 'female')
-- he is looking for female and second user is female
) AND
(
(us2.pref_m = 1 AND us1.gender = 'male')
OR
(us2.pref_f = 1 AND us1.gender = 'female')
)
GROUP BY
cs2.userid -- finally group by user_id
Best thing is there are no sub-queries, and you can easily use this query in many ways. (changing order, group by, and using aggregate functions)
It's pretty easy if you don't do the sorting by most countries in common (you could do it in code later if the result sets won't be too large):
SELECT
o.id userid, u_cs.countryid
FROM users u
JOIN countryselection u_cs ON (u.id = u_cs.userid)
JOIN countryselection o_cs ON (u_cs.countryid = o_cs.countryid)
JOIN users o ON (o_cs.userid = o.id)
WHERE
u.id = :userid AND -- The user we want
u.id <> o.id AND -- Exclude ourselves
( -- Check whether the other person is
-- compatible with us
(u.pref_m = 1 AND o.gender = 'male') OR
(u.pref_f = 1 AND o.gender = 'female')
) AND
( -- Check whether we're compatible with the
-- other person
(o.pref_m = 1 AND u.gender = 'male') OR
(o.pref_f = 1 AND u.gender = 'female')
)
SQL Fiddle
If you do want the sorting, I think the best option is to use GROUP_CONCAT (because MySQL sucks and doesn't support windowing/analytic functions).
SELECT
o.id userid, GROUP_CONCAT(u_cs.countryid) countries
FROM users u
JOIN countryselection u_cs ON (u.id = u_cs.userid)
JOIN countryselection o_cs ON (u_cs.countryid = o_cs.countryid)
JOIN users o ON (o_cs.userid = o.id)
WHERE
u.id = :userid AND -- The user we want
u.id <> o.id AND -- Exclude ourselves
( -- Check whether the other person is
-- compatible with us
(u.pref_m = 1 AND o.gender = 'male') OR
(u.pref_f = 1 AND o.gender = 'female')
) AND
( -- Check whether we're compatible with the
-- other person
(o.pref_m = 1 AND u.gender = 'male') OR
(o.pref_f = 1 AND u.gender = 'female')
)
GROUP BY o.id
ORDER BY COUNT(u_cs.countryid) DESC
You could probably pull this off with some nasty subqueries too, but I get the feeling it'll kill performance.
SQL Fiddle
SELECT t4.id, COUNT(t4.id) AS frequency
FROM users t1
LEFT JOIN countryselection t2
ON t1.id = t2.userid
INNER JOIN countryselection t3
ON t2.userid != t3.userid AND t2.countryid = t3.countryid
INNER JOIN users t4
ON t3.userid = t4.id
AND ((t4.pref_m = 1 AND t1.gender = 'male' OR t4.pref_f = 1 AND t1.gender = 'female')
AND (t1.pref_m = 1 AND t4.gender = 'male' OR t1.pref_f = 1 AND t4.gender = 'female'))
WHERE t1.id = ?
GROUP BY t4.id
ORDER BY frequency DESC
Similar to others here but using appropriate join types and join conditions instead of where conditions.
From MySQL docs:
The conditional_expr used with ON is any conditional expression of the
form that can be used in a WHERE clause. Generally, you should use the
ON clause for conditions that specify how to join tables, and the
WHERE clause to restrict which rows you want in the result set.
I think this works
select me.id meid
, them.id themid
, me.gender mygender
, them.gender themgender
, me.pref_m mepref_m
, me.pref_f mepref_f
, them.pref_m thempref_m
, them.pref_f thempref_f
, csme.countryid
from users me
cross
join users them
inner
join countryselection csme
on me.id = csme.userid
inner
join countryselection csthem
on them.id = csthem.userid
where csme.countryid = csthem.countryid
and ((me.gender = 'male' and them.pref_m) or (me.gender = 'female' and them.pref_f))
and ((them.gender = 'male' and me.pref_m) or (them.gender = 'female' and me.pref_f))
and me.id != them.id
and me.id = 2
http://sqlfiddle.com/#!2/06351/25/0
I intentionally left out the group by so that the results are more easily verified.
inferred DDL :
create table users (id int, gender text, pref_m bool, pref_f bool);
create table countryselection (id int, userid int, countryid int);
Here is CSV that you can .import (using sqlite3, after doing .separator ,) into the tables in the question:
users.csv:
1,male,1,0
2,male,1,1
countryselection.csv
1,1,123
2,1,111
3,1,100
4,1,110
5,2,123
6,2,111
7,2,202
8,2,210
peter's sql, edited to use field names from question:
SELECT
us2.id,
COUNT(cs2.*) as countries_in_common
FROM
countryselection cs1
LEFT JOIN
countryselection cs2 ON
(
cs2.userid <> $userid AND
cs2.countryid = cs1.countryid
)
LEFT JOIN
users us1 ON
(
us1.id = cs1.userid
)
LEFT JOIN
users us2 ON
(
us2.id = cs2.userid
)
WHERE
cs1.userid = $userid AND
cs2.userid IS NOT NULL AND
(
(us1.pref_m = 1 AND us2.gender = 'male')
OR
(us1.pref_f = 1 AND us2.gender = 'female')
) AND
(
(us2.pref_m = 1 AND us1.gender = 'male')
OR
(us2.pref_f = 1 AND us1.gender = 'female')
)
GROUP BY
cs2.userid
;
you can execute it like this:
sqlite3 myDBname < peters_sql.sql
setting $userid to 1, i get 2 as output.
UPDATE: (added the countries)
SELECT u1.id AS uid1
, u2.id AS uid2
, cs.countryid
FROM users u1
, users u2
JOIN countryselection cs ON cs.userid = u2.id
-- WHERE u1.id < u2.id -- tiebreaker
WHERE u1.id = 12345
AND EXISTS (
SELECT * FROM countryselection cs1
JOIN countryselection cs2 ON cs1.countryid = cs2.countryid
WHERE cs1.userid = u1.id
AND cs2.userid = u2.id
)
AND ((u1.pref_m = True AND u2.gender = 'male')
OR (u1.pref_f = True AND u2.gender = 'female') )
-- the love must be mutual ...
AND ((u2.pref_m = True AND u1.gender = 'male')
OR (u2.pref_f = True AND u1.gender = 'female') )
;
Hello guys i have these tables;
USERS( user_id ,fullname ,username etc.)
POSTS ( post_id, user_id, post, orig_post_id, replyto date)
USER_PROFILE (id, user_id, profile_image_path etc)
Examples
USERS (1 ,John Doe ,johndoe etc.),( 2 ,Stack Flow ,stackflow etc.)
POSTS (2, 1, My naame is John doe and i approve this message, 0, 0,sometimestamp),
(3, 12, My naame is Stack Flow and i approve this message, 0, 2,sometimestamp)
USER_PROFILE (1, 1, ppdjodjf.jpg etc),(2, 2, grsdjodjf.jpg etc)
Basically, i want the query to output this if the replyto field is 0
array('post_id' => 2,
'user_id' => 1,
'post' => the post,
'orig_post_id,' => 0
'replyto,' => 0
username => johndoe,
fullname => John Doe,
profile_image_path => etc)
And when it's not zero
array('post_id' => 3,
'user_id' => 2,
'post' => Another post,
'orig_post_id,' => 0
'replyto,' => 2
username => stackflow,
fullname => Stack Flow,
profile_image_path => etc,
'replies' => array(all the replies)
These are basics in SQL. You really should learn some SQL.
It's easy to learn, you could spend half an hour on it, and it would be very beneficial.
First, the posts for a given user would be:
select posts.*
from posts
where posts.user_id = '$user_id'
To get the user fields you want, do a join
select posts.*,users.username,users.fullname
from posts
inner join users where posts.user_id = users.user_id
where posts.user_id = '$user_id'
You should be able to figure out how to join to user_profile to get those fields.
To filter only those records without orig_post_id, you might need to test for zero,
or you might need to test for NULL. Maybe both, so assuming you want to test for both:
where posts.user_id = '$user_id'
and (orig_post_id = 0 or orig_post_id is null)
After spending the whole day on this, My code ended up looking like this.
public function get_connected_post()
{
$user_id = $this->session->userdata('user_id');
$sql = "SELECT p.*,up.fullname,u.username,upi.file_path_thumb,IF(hi5c.hi5_count is NULL,'0',hi5c.hi5_count) AS count_hi5,
IF(brn.branch_count is NULL,'0',brn.branch_count) AS count_branch, IF(rply.reply_count is NULL,'0',rply.reply_count) AS count_reply,
IF((SELECT COUNT(*) FROM post_highfives ph WHERE ph.user_id = $user_id AND ph.post_id = p.post_id),'1','0') AS count_is_hi5ed,
IF((SELECT COUNT(*) FROM posts pt WHERE pt.user_id = $user_id AND pt.is_branch_of_id = p.post_id),'1','0') AS count_is_branched
FROM (
SELECT user_id
FROM user_followers
WHERE follower_id = $user_id
UNION ALL
SELECT $user_id
) uf
JOIN posts p
ON p.user_id = uf.user_id
JOIN user_profile up
ON up.user_id = p.user_id
JOIN user_profile_images upi
ON upi.image_id = up.profile_image_id
JOIN users u
ON u.user_id = p.user_id
LEFT OUTER JOIN (SELECT ph.post_id, count(*) AS hi5_count
FROM post_highfives ph
GROUP BY ph.post_id) hi5c
ON p.post_id = hi5c.post_id
LEFT OUTER JOIN (SELECT pst.post_id,pst.is_branch_of_id, count(*) AS branch_count
FROM posts pst
GROUP BY pst.is_branch_of_id) brn
ON p.post_id = brn.is_branch_of_id
LEFT OUTER JOIN (SELECT pst.post_id,pst.reply_to, count(*) AS reply_count
FROM posts pst
GROUP BY pst.reply_to) rply
ON p.post_id = rply.reply_to
ORDER BY p.post_date DESC";
$query = $this->db->query($sql);
if ($query) {
$result = array();
foreach($query->result_array() as $r){
$branch_id = $r['is_branch_of_id'];
if($branch_id != 0){
$branch_array = $this->branch_query($this->postid_return_user_id($branch_id),$branch_id);
}else{
$branch_array = array();
}
$result[] = array_merge((array)$branch_array, (array)$r);
}
return $result;
}
else {
return false;
}
}
I know this looks different from the question i asked but i was trying to simplfy what i was actually doing.I didn't think asking a question stating this problem would get me anywhere, although simplyfying the question still didn't get me anywhere as no one understood me haha. For my question anyway, the relevant part is the last bit of the code where i put a condition ssaying if branch_id is not zero get some array data and if it is return an empty array.The I merged the array together with then query result.
Now i have to think of how i can simplyfy this.