Unable to negate condition in mysql - php

I have table for storing people following other people. Two columns. I can query those who a certain user follows (and has a certain book) by
$stmt = $conn->prepare("
SELECT USERS.NAME, USERS.NAME2
FROM USERS
LEFT JOIN USERS_BOOKS ON USERS_BOOKS.USERID = USERS.ID
LEFT JOIN FOLLOW ON FOLLOW.FOLLOW_ID = USERS.ID
WHERE USERS_BOOKS.BOOKID = ? AND FOLLOW.USER_ID = ?
");
$stmt -> execute(array($bookid, $session_userid));
However, I have no idea how to query those that I don't follow. I cannot simply modify the WHERE clause to ... AND NOT FOLLOW.USER_ID = ?
because the result will consist those as well that I already follow. The following table is an example where USER_ID is the one who follows and FOLLOW_ID is who is being followed.
ROW USER_ID FOLLOW_ID
1 6 1
2 15 5
3 13 8
4 15 10
5 15 12
6 4 5
The result of the first query is row No. #2,#4,#5 because USER_ID No.15 follows those three users. However, when I say NOT FOLLOW.USER_ID = ? the result will be row No. #1,#3,#6, meaning users 1,8,5. But I already follow user 5, so how do I do this? I need only two rows as the result: #1 and #3.

You need to use a NOT EXISTS with a sub-query:
SELECT USERS.NAME, USERS.NAME2
FROM USERS
LEFT JOIN USERS_BOOKS ON USERS_BOOKS.USERID = USERS.ID
WHERE NOT EXISTS (
SELECT * FROM FOLLOW
WHERE FOLLOW.USER_ID = ?
AND FOLLOW.FOLLOW_ID = USERS.ID
)
AND USERS_BOOKS.BOOKID = ?

Related

Suggestion SQL query

Basically what i am trying to do is to suggest people based on common interests.
I have a table of Users.
I have a table of Interested_People where UserID + InterestID is stored.
I have a table of Contactlist where people who are added with each other is stored.
What I want is to only output people who are not your friends.
I searched a lot in internet but couldn't find something like so.
Although I created a query but it is very slow. Now I Kindly request you guys if you can edit my query a bit and make it much more bandwidth & time efficient.
SELECT *
FROM users
WHERE id IN(SELECT userid
FROM interested_people
WHERE interested_in IN(SELECT interested_in
FROM interested_people
WHERE userid = [userid])
AND id NOT IN(SELECT user1 AS my_friends_userid
FROM contactlist f
WHERE f.user2 = [userid]
AND accepted = 1
UNION
SELECT user2 AS my_friends_userid
FROM contactlist f
WHERE f.user1 = [userid]
AND accepted = 1))
AND id != [userid]
ORDER BY Rand ()
LIMIT 0, 10;
This query actually does the job but it takes very long about 16 sec in my local machine. and that's not what I want. I want a fast and reliable one.
Thanks in advance!
Subqueries in WHERE clauses are often slow in MySQL; at least slower than comparable JOINs.
SELECT others.*
FROM interested_people AS userI
INNER JOIN interested_people AS othersI
ON userI.interestid = othersI.interestid
AND userI.userid <> othersI.userid
INNER JOIN users AS others ON othersI.user_id = others.userid
LEFT JOIN contactlist AS cl
ON userI.userid = cl.user1
AND others.userid = cl.user2
AND cl.accepted = 1
WHERE userI.userid = [userid]
AND cl.accepted IS NULL
ORDER BY RAND()
LIMIT 0, 10;
Note: intuition makes me wonder if contactlist might be better as a where subquery.
The AND cl.accepted IS NULL ends up processed after the JOINs, resulting in allowing only results that did NOT have a match in contactlist.
If you want to enhance things a bit further:
SELECT others.*, COUNT(1) AS interestsCount
...
GROUP BY others.userid
ORDER BY interestsCount DESC, RAND()
LIMIT 0,10;
This would give you a random selection of the people that share the most interests in common.
First, looking at your interested-in query and assuming the "userID"
you are testing with is = 1. Sounds like you are trying to get one level
away from those user 1 is also interested in...
SELECT userid FROM interested_people
WHERE interested_in IN
( SELECT interested_in FROM interested_people
WHERE userid = [userid] )
Sample Data for Interested_People
userID Interested_In
1 5
1 7
1 8
2 3
2 5
2 7
7 1
7 2
7 5
8 3
In this case, the innermost returns interested_in values of 5, 7, 8.
Then, getting all users who are interested in 5, 7 and 8 would return 2 and 7.
(but since both users 2 and 7 are interested in 5, the 2 ID would be returned TWICE
thus a possible duplicate join later on. I would do distinct. This same
result could be done with the following query which you could sample times with...
SELECT distinct ip2.userid
from
interested_people ip
join interested_people ip2
ON ip.interested_in = ip2.interested_in
where
userid = [parmUserID]
Now, you need to exclude from this list all your contacts already accepted.
You could then left-join TWO TIMES for the from/to contact and ensure NULL
indicating not one of the contacts... Then join again to user table to
get the user details.
SELECT
u.*
from
users u
JOIN
( SELECT distinct
ip2.userid
from
interested_people ip
join interested_people ip2
ON ip.interested_in = ip2.interested_in
left join contactList cl1
ON ip2.userid = cl1.user1
AND cl1.accepted = 1
left join contactList cl2
ON ip2.userid = cl2.user2
AND cl2.accepted = 1
where
ip.userid = [parmUserID]
AND NOT ip2.userID = [parmUserID] ) PreQuery
ON u.id = PreQuery.userID
order by
RAND()
limit
0, 10
I would have two indexes on your contactList table to optimize both left-joins... with user1 and user2 in primary position... Similarly for the interested_people table.
table index
contactList ( user1, accepted )
contactList ( user2, accepted )
interested_people ( userid, interested_in )
interested_people ( interested_in, userid )
I would expect your user table is already indexed on the ID as primary key.
I think this will give you the same results but perform a lot better:
SELECT * FROM Users u
INNER JOIN interested_people i
ON u.id = i.userid
WHERE NOT EXISTS
(SELECT * FROM contacts WHERE user1 = [userid] or user2 = [userid] and accepted=1)
AND id != [userid]
ORDER BY Rand()
LIMIT 0, 10
Skip the ORDER BY clause if that is at all reasonable. That will be the most expensive part
The select and join clauses give you the users who are interested in connecting and the WHERE NOT EXISTS is a performant way to exclude those contacts already listed.

How to count records in MySQL and merge results in PHP

I have a table which stores clients like this:
id name
-- ----
1 John
2 Jane
...
I also have another table which stores links created by clients:
id client_id link created
-- --------- ---- -----------
1 1 ... 2015-02-01
2 1 ... 2015-02-26
3 1 ... 2015-03-01
4 2 ... 2015-03-01
5 2 ... 2015-03-02
6 2 ... 2015-03-02
I need to find how many links a client has created today, this month and during all the time. I also need their name in the result, so I'll be able to craete a HTML table to display the statistics. I thought I can code as less as possible like this:
$today = $this->db->query("SELECT COUNT(*) as today, c.id as client_id, c.name FROM `links` l JOIN `clients` c ON l.client_id = c.id WHERE DATE(l.created) = CURDATE() GROUP BY c.id");
$this_month = $this->db->query("SELECT COUNT(*) as this_month, c.id as client_id, c.name FROM `links` l JOIN `clients` c ON l.client_id = c.id WHERE YEAR(l.created) = YEAR(NOW()) AND MONTH(l.created) = MONTH(NOW()) GROUP BY c.id");
$yet = $this->db->query("SELECT COUNT(*) as yet, c.id as client_id, c.name FROM `links` l JOIN `clients` c ON l.client_id = c.id WHERE GROUP BY c.id");
And then merge them in PHP as I asked HERE before, like this:
$result = array_replace_recursive($today, $this_month, $yet);
So I'll be able to loop into the result and print my HTML table.
But there are logical problems here. Everything works fine, but the result in a month is a wrong number, forexample the whole created links of one person is 1 but it shows 4 in the monthly counter! I also tried to use RIGHT JOIN in SQL query to get all clients, so array_replace_recursive in PHP could work fine as I think it doesn't work properly at the moment, but no success and got wrong results again.
Can anyone show me a way to make the job done?
This query should do it for today
$query_today="
SELECT name, id AS user_id, (
SELECT COUNT( * )
FROM links
WHERE client_id = user_id AND created = '2015-03-02'
) AS alllinks
FROM clients"
adjust the WHERE clause in the subquery for months and all
$query_month="
SELECT name, id AS user_id, (
SELECT COUNT( * )
FROM links
WHERE client_id = user_id AND created like '2015-03%'
) AS alllinks
FROM clients"
$query_all="
SELECT name, id AS user_id, (
SELECT COUNT( * )
FROM links
WHERE client_id = user_id
) AS alllinks
FROM clients"

SUM points from table

I have table with files and their have their points/score. Iam trying to summarize those points and print it. I have a where clause for id files, but if I select random files it has same score as others like where clause isnt working.
$id is correct for each file.
This is my query:
$result = mysql_query("SELECT *,SUM(body) FROM soubory, users, hodnoti WHERE
soubory.id='".$id."' AND soubory.users_id = users.id");
hodnoti table
users_ID soubory_id body
14 44 7
15 44 9
And now, if there is no record (in table hodnoti) for soubory_id = 45 the result is same as for 44 despite of where clause.
users table
id nick
14 user1
15 user2
soubory table
id nazev users_id
44 file1 14
45 file2 14
That query should help me print this:
Who uploaded, nazev(title) of the file and then points. But if file2 has no record in table hodnoti, still has score of file1. Hope it helps.
$sql = "SELECT *, SUM(hodnoti.body) as sum FROM soubory
INNER JOIN users ON soubory.users_id = users.id
INNER JOIN hodnoti ON soubory.id = hodnoti.soubory_id
WHERE soubory.id='".$id."' ";
$result = mysql_query($sql);
This query works for me. When i set id=44 and get a result and when set id=45 and get all null value. i have attached two result image. I have just create table and insert your given data and run query on phpmyadmin sql tab query box. You can try this for check your query works or not.
Use JOIN.
$sql = "SELECT *, SUM(hodnoti.body) as sum FROM soubory
INNER JOIN users ON soubory.users_id = users.id
INNER JOIN hodnoti ON soubory.id = hodnoti.soubory_id
WHERE soubory.id='".$id."' ";
$result = mysql_query($sql);
SUGGESTION: Above is a direct answer for your question. Avoid using mysql_* statements as they are deprecated now. Learn mysqli_* or PDO and start implementing that.

MySQL, check if result has value, if not take another [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
I have a table structure like this:
Page_id || type || user_id
1 1 0
2 2 0
3 3 0
4 1 1
5 2 1
6 3 1
From this table I would like to get page_id 4,5 and 6.
But I can also have table data like this
Page_id || type || user_id
1 1 0
2 2 0
3 3 0
4 1 1
5 2 1
Then I would like to get page_id 4, 5 and 3.
So I have to get all the types, but with the priority user_id and if there is no record with user_id 1, then take the one with 0
Have tried a lot. I know I can sort it with PHP, but I hope there is a way with MySQL.
Regards Andreas
//////// ANSWER /////////
I got a lot of suggestions, and I haven't tried them all, so I can't tell it they where right or not. But I have accepted an answer, which worked for me.Thank to everybody.
SELECT a.Type, a.Page_ID
FROM table a
INNER JOIN
(SELECT Type, MAX(User_ID) AS User_ID
FROM table
GROUP BY Type ) b
ON a.Type = b.Type AND a.User_ID = b.User_ID
You can execute a SELECT query as follow
SELECT Page_id
FROM table
WHERE user_id != 0
This SQL Fiddle demonstrates the below query:
SELECT DISTINCT
(
SELECT s1.Page_id
FROM myTable AS s1
WHERE m.type = s1.type
ORDER BY s1.Page_id
LIMIT 1
) AS PageID, type,
(
SELECT s2.user_id
FROM myTable AS s2
WHERE m.type = s2.type
ORDER BY s2.Page_id
LIMIT 1
) AS User
FROM myTable AS m
The results are the records where Page_id is 1, 2, and 4. As you can see in both of the sub queries I am ordering by Page_id to make sure the data is pulled from the same record and the first Page_id for that occurrence of the type is selected.
To return only one record unique to a couple columns, you'll want to use the GROUP BY statement. Then for any other column outside of the group by columns, you need to pick an aggregate function so it knows how to summarize the value if it finds multiple records in that group. In this case you want non-zero, so max() would work
SELECT type, max(user_id) as user_id
FROM table
GROUP BY type
how about that?
select page_id,type from (
select page_id,type, user_id from mytable
group by page_id,type, user_id having user_id=max(user_id)
) as x where user_id=1
Will there ever be multiple rows for a page where user_id is not zero? Because if not (if at most you only have one row per page where user_id = 1) then this will work:
SELECT ifNull(t1.page_id,t2.page_id) as page_id, t1.type,
CASE WHEN t2.page_id IS NULL THEN t1.user_id ELSE t2.user_id END as user_id
#start with all rows (including duplicates)
FROM myTable t1
#look for a user_id > 0 for this type
LEFT OUTER JOIN myTable t2 ON t1.type = t2.type AND t2.user_id > 0
WHERE t2.page_id IS NULL # if no record with user_id > 0 found, then no need to filter
# if a record with user_id > 0 was found, then filer out the user_id = 0 record
OR (t2.page_id IS NOT NULL AND t1.user_id > 0)
See in SQLFiddle: http://sqlfiddle.com/#!2/ba877/5

php MySql QUERY for Friends relations Table help

EDIT by:lawrence.
I got the right query now
select *
from users, friends
where (users.id=friends.user_id1 and friends.user_id2=$profileID) or (users.id=friends.user_id2 and friends.user_id1=$profileID)
Question answered
I need some help joining results from my friends and users table
This is what my friends table look like
id user_id1 user_id2
1   | 2         | 3
1   | 2         | 4
1   | 2         | 5
1   | 6         | 2
Users table
id name
2 |  sarah
3 |  emma
4 |  lawrence
5 |  cynthia
6 |  suzie
I could easily just have two rows for each relation and do a simple query.
But i prefer having one row per relation,
So lets assume that we are watching page member.php?profile=2
and there is a list of friends, what does the query look like.
This works fine if i have two rows per relation but i dont want that....
SELECT * FROM friends, users WHERE friends.user_id1 = $profileID AND friends.user_id2 = users.id ORDER BY friends.id DESC LIMIT 16
Do you get me? something along like
SELECT * FROM friends,users WHERE friends.user_id1 = $profileID AND ALSO WHERE friends.user_id2 = $profileID AND THEN GET FROM users WHERE users.id = friends.user_id1 AND ALSO WHERE users.id = friends.user_id2
I hope I made myself clear
I'm not sure i understand your question but won't this do?
SELECT * FROM friends, users where friends.user_id1 = $profileID or friends.userid2 = $profileID and users.id = friends.user_id1 or users.id = friends.user_id2
You want a left join (using the LEFT JOIN operator), not a cartesian join (using the FROM table1, table2 syntax).
http://www.w3schools.com/sql/sql_join_left.asp
Tip: With your cross-reference table instead of having an id column you can create a compound key.

Categories