Join a new column by a condition - mysql

How can I join a new column in my query by condition? I have 3 tables:
Users table:
userid name
1 John
2 Joe
3 Sam
4 Alex
Questions table:
userid questionid asked
1 1 2011-11-11 22:52
1 2 2011-11-11 22:52
3 3 2011-11-11 22:52
2 4 2011-11-11 22:52
Like table:
userid questionid
1 1
1 2
1 3
1 4
I'd like to query a question of a user and determine whether the user liked the question or not. The result should be if the user liked the question:
userid questionid liked
1 1 1
Other case when the user didn't like the question:
userid questionid liked
2 1 0
Conrete code I'm using now: (a bit different fieldnames)
SELECT temp.`id`, temp.`userid`, `categories`.`name`, `user`.`username`, temp.`title`,
temp.`details`, temp.`date` FROM (
SELECT `id`, `categoryid`, `details`, `title`, `userid`, `date`, #a := id, if(#a = ". $position .", #b := 1, #b) AS join_id
FROM `questions`
join(SELECT #a := 0, #b := 0) t
ORDER BY `date` DESC
) as temp
LEFT JOIN `user`
ON temp.`userid` = `user`.`userid`
LEFT JOIN `categories`
ON temp.`categoryid` = `categories`.`categoryid`
WHERE join_id = 1
LIMIT 1,5;
Position means the id to query from. ATM userid 31 has only liked questions. SQLfiddle: http://sqlfiddle.com/#!2/5fe17/2

Use a left (outer) join to join the Like table to the Question table.
For each match Like.userid (and Like.questionid) will be not null, else null.
You can check this in the select column with the IF() function:
SELECT userid, questionid, if(Questions.userid is null, 0, 1) as liked
FROM Questions
LEFT OUTER JOIN Like ON Questions.userid = Like.userid and Questions.questionid = Like.questionid;
This lists all questions a user was asked. You can just narrow it down to one user and one question by adding a WHERE clause.
(Note that LIKE is a keyword in MySQL. You should choose another table name or quote/escape the table name in your statement).

Related

MySQL Putting in duplicate id's

I'm trying to run an UPDATE query that uses the same table and I'm getting an error saying "1093 - Table 'queues_monitor_times' is specified twice, both as a target for 'UPDATE' and as a separate source for data".
UPDATE queues_monitor_times
SET queue_id = IF((
SELECT id
FROM queues_monitor_times
INNER JOIN(
SELECT pcc_group, pcc, gds, queue, category, `name`
FROM queues_monitor_times
GROUP BY pcc_group, pcc, gds, queue, category, `name`
HAVING COUNT(id) > 1
)temp ON queues_monitor_times.pcc_group = temp.pcc_group AND
queues_monitor_times.pcc = temp.pcc AND
queues_monitor_times.gds = temp.gds AND
queues_monitor_times.queue = temp.queue AND
queues_monitor_times.category = temp.category AND
queues_monitor_times.`name` = temp.`name`), 1, id)
WHERE
id NOT IN (SELECT MIN(id) FROM queues_old GROUP BY pcc_group, pcc, gds, queue, category, `name`);
I ran the select query by itself and it showed all the rows that were duplicates, which is what I wanted. I want queue_id to be set with the lowest duplicate row's id if the row is a duplicate or the row id if it is not.
Example of what the query should do:
id dup_id name value
1 1 John 13
2 2 John 13
3 3 Sally 6
4 4 Frank 4
5 5 Sally 6
And after running the query it will turn into
id dup_id name value
1 1 John 13
2 1 John 13
3 3 Sally 6
4 4 Frank 4
5 3 Sally 6
Please advise and thank you for your help.
I was able to solve my problem. Thanks for all your help!
UPDATE queues_monitor_times
SET queue_id = (
SELECT
id
FROM
queues_old
WHERE
queues_old.pcc_group = queues_monitor_times.pcc_group
AND queues_old.pcc = queues_monitor_times.pcc
AND queues_old.gds = queues_monitor_times.gds
AND queues_old.queue = queues_monitor_times.queue
AND queues_old.category = queues_monitor_times.category
AND queues_old.`name` = queues_monitor_times.`name`
GROUP BY pcc_group, pcc, gds, queue, category, `name`
HAVING COUNT(id) > 1)
WHERE
id NOT IN (SELECT MIN(id) FROM queues_old GROUP BY pcc_group, pcc, gds, queue, category, `name`);
For those that will want to use this in the future, queues_monitor_times table and queues_old table have the exact same data.

SQL - Get boolean values for each matching field

I need help with SQL request.
I have 3 tables:
Table User
id name
1 Jon
2 Jack
3 Bill
Table Type
id name
1 View
2 Edit
3 Delete
Table Right
id user type
1 1 1
2 1 2
3 1 3
4 2 1
5 3 1
So table Right contains linked pairs of user-type. I need a request which gets user name, and a boolean (BIT) value for each enrty in table Type, which exists in Right table for this user. Something like this for my example tables:
Username View Edit Delete
Jon 1 1 1
Jack 1 0 0
Bill 1 0 0
Thank you very much in advance!
untested:
select name,
coalesce(select 1 from `right` where `type` = 1 and right.user = user.id, 0) as `View`,
coalesce(select 1 from `right` where `type` = 2 and right.user = user.id, 0) as `Edit`,
coalesce(select 1 from `right` where `type` = 3 and right.user = user.id, 0) as `Delete`
from User
Alternatively:
select name, coalesce(RVIEW.R, 0) as `View`, coalesce(REDIT.R, 0) as `Edit`, coalesce(RDEL.R, 0) as `Delete`
from User
left join (select 1 R from `right` where `type` = 1) RVIEW on (right.user = user.id)
left join (select 1 R from `right` where `type` = 2) REDIT on (right.user = user.id)
left join (select 1 R from `right` where `type` = 3) RDEL on (right.user = user.id)
In your example, you are using reserved words as table names.
If you want to learn more about naming conventions for table names, have a look at the links in an earlier question on Stack Overflow here
Example below shows yet another way of getting the data you want (with other names for the tables):
select person.name as Username
, max( if( person_right.type_id = 1, 1, 0 ) ) as `View`
, max( if( person_right.type_id = 2, 1, 0 ) ) as `Edit`
, max( if( person_right.type_id = 3, 1, 0 ) ) as `Delete`
from person
left outer join person_right
on person_right.user_id = person.id
group by person.name
order by person.id
Another thing that might be worth looking at is the datamodel,
because Rights are normally quite "fixed".
If anyone accidentally changes one of the names in the Type table, you might have a serious security issue.
What you can do is change the person_right table to look like this
windowid user_id view_access edit_access delete_access
1 1 1 1 1
1 2 1 0 0
1 3 1 0 0
where the primary key would be window_id+user_id allowing you to setup different rights per user in a particular window/part of your application.
Hope this helps.

MySQL select rand and exclude users with condition

I need to select random user_id from "user" table, and completely exclude any user_id if current user have any "ongoing" battles with him battles.status
Query:
SELECT user.id
FROM user
LEFT JOIN battles b ON b.uid = user.id AND b.status <> 'ongoing'
WHERE user.id <> 1
ORDER BY RAND( )
LIMIT 1
But the query is not sufficient, because a user can have multiple battles with specific other users, one of them "ongoing" and the others "finished",
My query should select users from the "finished" row.
Tables structure:
user table:
id name
1 John
2 Sarah
3 Jack
4 Andy
5 Rio
battles table:
id uid uid2 status
1 1 2 finished
2 1 2 ongoing
3 2 3 ongoing
4 1 4 finished
5 3 5 finished
If "my" id = "1",
I want to completely exclude any user I have ongoing battle with him, like "2" in the above case and accept all other ids (i.e.3,4 and 5)
You probably want something along the lines of this:
SELECT foe.*
-- Select yourself and join all other users to find potential foes
FROM `user` AS me
INNER JOIN `user` AS foe
ON (me.id <> foe.id)
-- Here we select the active user
WHERE me.`id` = 1
-- Now we exclude foes we have ongoing battles with
-- (your id could be in either uid or uid2)
AND foe.`id` NOT IN (
SELECT `uid` FROM `battles`
WHERE `uid2` = me.`id` AND `status` = 'ongoing'
UNION ALL
SELECT `uid2` FROM `battles`
WHERE `uid` = me.`id` AND `status` = 'ongoing'
);
This will return a list of users which you do not currently have ongoing battles with. You can customise this to return just one of them using LIMIT and random ordering like in your example.

How to make my mysql queries for showing friends feeds?

Here is my table structure.(fun_friends)
id user_id,friend_id,status,createdat,updatedat
1 1 2 1 123456 125461
2 1 3 1 454545 448788
3 2 4 1 565659 898889
4 1 5 1 877878 878788
Here is the table structure of user_uploads
id user_id parent_id category_id title slug tags description video_type source video_link video_thumb
1 2 1 2 fun fun ['4','5'] coolvid 1 ytu link thumb
I need to show the latest upload of my friends
Can you tell me how can i join this tables together? i tried with
SELECT * FROM fun_friends WHERE (user_id= '".$_SESSION['user_row_id']."' AND `status` =1) OR (friend_id= '".$_SESSION['user_row_id']."' AND `status` =1)
and it is showing all friends of logged-in user
You can just join both table using user_id field. sample query bellow will return one record with latest user_uploads.id.
select *
from fun_friends a
inner join user_uploads b on a.user_id = b.user id
order by b.id desc limit 0,1
how about using UNION to get the friends user and wrapping it inside a subquery which later join on the other tabel,
SELECT usr.*
FROM user_uploads usr
INNER JOIN
(
SELECT user_ID AS ID
FROM Fun_Friends
WHERE friend_ID = 'ID_HERE'
UNION
SELECT friend_ID As ID
FROM Fun_Friends
WHERE ser_ID = 'ID_HERE'
) idList ON usr.user_ID = idList.ID

MySQL: find most recent value for list of subdocuments

I have a collection content that has four columns; id, timestamp, locationID, and authorID. Here is an example of my data; in production, this is tens of millions of rows in length.
id timestamp locationID authorID
1 2012-03-01 11:52:00 1 1
2 2012-03-16 19:56:00 1 2
3 2012-04-02 11:26:00 2 1
4 2012-04-22 11:52:00 2 3
5 2012-05-19 09:48:00 2 2
6 2012-05-30 07:12:00 2 1
7 2012-06-04 19:17:00 1 2
I'd like to collect the list of authorIDs whose most recent content (ordered by timestamp) matched a specific locationID.
The correct values for a query of locationID = 2 would be: [ 1, 3 ], as authorID 1 and 3 were most recently 'seen' at locationID = 2, while authorID 2's most recent content was at locationID 1.
I can certainly execute one query per authorID, but on production the authorID array has a length >100,000. This seems terribly inefficient (especially when each 'subquery' would be hitting this multi-million row content collection), and I'm looking for a better way to emerge this data from my dataset, ideally fast enough to be executed on a page render.
Something like this? This is from SQL Server, but I think it should work in mySQL as well.
DECLARE #locationId INT
SET #locationId = 2;
SELECT *
FROM (SELECT AuthorId, Max(TimeStamp) as MaxTimeStamp
FROM Content C
WHERE LocationId = #locationId
GROUP BY AuthorId) AS CBL
LEFT JOIN Content AS C ON CBL.AuthorId = C.AuthorId
AND C.TimeStamp > CBL.MaxTimeStamp
WHERE C.AuthorId IS NULL
For locationId = 2, it returns 1 and 3; and for locationId = 1, it returns 2
Per JW (thanks!), the correct mySql approach:
SET #locationId := 2;
SELECT *
FROM (SELECT AuthorId, Max(TimeStamp) as MaxTimeStamp
FROM Content C
WHERE LocationId = #locationId
GROUP BY AuthorId) AS CBL
LEFT JOIN Content AS C ON CBL.AuthorId = C.AuthorId
AND C.TimeStamp > CBL.MaxTimeStamp
WHERE C.AuthorId IS NULL
Try derieved subquery
SELECT
*
FROM content as c
INNER JOIN(
SELECT
MAX(id) as ID
FROM content
WHERE locationID = 2
GROUP BY authorID
) as t on t.ID = c.id
SQL FIDDLE DEMO