SQL - Get boolean values for each matching field - mysql

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.

Related

Two tables with all rows including allocated based on id

Im trying to make this generic as it might help others in the future.
For an example i have two tables one with books and the other is the user with which book they have read, So ide like to display all the books and include a temporary column value as a (yes / no or 0/1), i have tried a join but the ( WHERE user_id = 3) clause only then return the one row and not all the other rows.
book.book_id book.book_name
10 Book 1
11 Book 2
12 Book 3
-------------
user.user_id user.book_id
1 10
1 12
2 11
3 12
Desired output:
user_id book_id temp_col_read
3 10 0 // yes, on or null
3 12 1 // or yes
3 13 0
This is actually quite simple. In the event that a user could read a book multiple times, I would go with exists in the select:
select b.*,
(case when exists (select 1
from reads r
where r.book_id = b.book_id and r.user_id = 3
)
then 1 else 0
end) as user_read_book
from book b;
In MySQL, the case is not strictly necessary because a boolean expression is treated as 0/1 in many contexts:
select b.*,
(exists (select 1
from reads r
where r.book_id = b.book_id and r.user_id = 3
) as user_read_book
from book b;
You can use a left join and where the join is unresolved then is not read
select
user.user_id
, book.book_id
, case
when book.book_id is null
then 'NO' else 'YES'
end as temp_col_read
from book
left join user on user.book_id = book.book_id

Print parent hierarchy from same table in mysql database

I have a table category which has fields and values as shown below in MYSQL database.
id name parent sort_order
1 Men null 0
2 Women null 1
3 shirt 1 0
4 salwar 2 1
Here parent is a foreign key points to the same table. In my category list page i want to print the parent hierarchy(if any) along with the category name. can i do this in a single query.
I have tried with group_concat in mysql but not able to generate the required result.
Expected Result:
1 men
2. women
3. men>shirt
4. women> salwar
You can use a self-join:
SELECT *
FROM
(
SELECT name, 1 AS rank, sort_order
FROM category
WHERE parent IS NULL
UNION ALL
SELECT CONCAT(c1.name,
CASE WHEN c2.parent IS NOT NULL THEN CONCAT('>', c2.name) ELSE '' END), 0 AS rank, c1.sort_order
FROM category c1 LEFT JOIN category c2
ON c1.id = c2.parent
WHERE c1.parent IS NULL
) t
ORDER BY t.rank DESC, t.sort_order
SQLFiddle

Getting Follower and FollwedBy Users from Table in one list

I have a Table that tracks followers
FollowerUserId, FollowingUserId
1 2
2 1
3 1
4 1
1 5
I want to get all user that given Id follows and is followed by or Both.
for example for UserId 1,I want result to be: (FG: Following, FD: Followed, B: Both ways)
2,B
5,FG
3,FD
4,FD
i can easily get FG and FD by doing union
Select FollowerUserId, 'FD' From Table Where FollowingUserId =1
Union
Select FollowingUserId, 'FG' From Table Where FollowerUserId =1;
with above i get user 2 as
2,FG
2,FD
from above but I really need 2,B without UserId 2 duplicated.
How can this be done efficiently?
You can use aggregation on your basic query:
SELECT UserId,
(CASE WHEN COUNT(DISTINCT which) = 1 THEN MIN(which)
ELSE 'B'
END)
FROM (Select FollowerUserId as UserId, 'FD' as which From Table Where FollowingUserId = 1
Union ALL
Select FollowingUserId, 'FG' From Table Where FollowerUserId = 1
) f
GROUP BY UserId;

Join a new column by a condition

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).

MySQL - loop through rows

I have the following code
select count(*)
from (select Annotations.user_id
from Annotations, Users
where Users.gender = 'Female'
and Users.user_id = Annotations.user_id
and image_id = 1
group by Annotations.user_id
having sum(case when stem = 'taxi' then 1 else 0 end) > 0 and
sum(case when stem = 'zebra crossing' then 1 else 0 end) > 0
) Annotations
It produces a count of how many females who have given the stem 'taxi' and 'zebra crossing' for image 1.
Sample data
user id, image id, stem
1 1 image
1 1 taxi
1 1 zebra crossing
2 1 person
2 1 zebra crossing
2 1 taxi
3 1 person
3 1 zebra crossing
Expected result (or similar)
stem1, stem2, count
taxi , zebra crossing 2
person, zebra crossing 2
However, as there are over 2000 stems, I cannot specify them all.
How would I go around looping through the stem rows with the image_id = 1 and gender = female as opposed to specifying the stem string?
Thank you
As per my understanding, you need to fetch female users that have 2 or more stems
Update: It seems you need to display the user's that have a stem that is used by another user too, I have updated the query for the same
SELECT
distinct a.user_id,
group_concat(DISTINCT a.stem ORDER BY a.stem)
FROM
Annotations a
JOIN Users u ON ( a.user_id = u.user_id AND u.gender = 'Female' )
JOIN
(
SELECT
b.user_id,
b.stem
FROM
Annotations b
) AS b ON ( a.user_id <> b.user_id AND b.stem = a.stem )
WHERE
a.image_id = 1
GROUP BY
a.user_id
UPDATE: As I understand it, you want to select all combinations of 2 stems, and get a count of how many users have that combination of stems. Here is my solution:
SELECT stem1, stem2, count(*) as count FROM
(
SELECT a.user_id,a.image_id,a.stem as stem1,b.stem as stem2
FROM Annotations a JOIN Annotations b
ON a.user_id=b.user_id && b.image_id=a.image_id && a.stem!=b.stem
JOIN Users ON Users.user_id = a.user_id
WHERE Users.gender = "Female"
) as stems GROUP BY stem1, stem2 having count > 1 WHERE image_id=1;
The caveat here is that it will return 2 rows for each combinations of stems. (The second occurrence will have the stems in reverse order).
Here's my attempt to solve your problem:
SELECT COUNT(*) AS Count, a1.stem AS Stem1, a2.Stem AS Stem2
FROM Annotations AS a1
INNER JOIN Annotations AS a2 ON a1.user_id = a2.user_id AND a1.image_id = a2.image_id
AND a1.stem < a2.stem
WHERE a1.image_id = 1
GROUP BY a1.stem, a2.Stem
HAVING COUNT(*) > 1;
I did not include image_id logic.
Please see my SQL Fiddle here: http://sqlfiddle.com/#!2/4ee69/33
Based on the following data (copied from yours) I get the result posted underneath it.
CREATE TABLE Annotations
(`user_id` int, `image_id` int, `stem` varchar(14))
;
INSERT INTO Annotations
(`user_id`, `image_id`, `stem`)
VALUES
(1, 1, 'image'),
(1, 1, 'taxi'),
(1, 1, 'zebra crossing'),
(2, 1, 'person'),
(2, 1, 'zebra crossing'),
(2, 1, 'taxi'),
(3, 1, 'person'),
(3, 1, 'zebra crossing')
;
COUNT STEM1 STEM2
2 person zebra crossing
2 taxi zebra crossing