Problems using SQL ALL operator - mysql

I'm having trouble using/understanding the SQL ALL operator. I have a table FOLDER_PERMISSION with the following columns:
+----+-----------+---------+----------+
| ID | FOLDER_ID | USER_ID | CAN_READ |
+----+-----------+---------+----------+
| 1 | 34353 | 45453 | 0 |
| 2 | 46374 | 342532 | 1 |
| 3 | 46374 | 32352 | 1 |
+----+-----------+---------+----------+
I want to select the folders where all the users have permission to read, how could I do it?

Use aggregation and having:
select folder_id
from t
group by folder_id
having min(can_read) = 1;

Gordon's answer seems better but for the sake of completeness, using ALL a query could look like:
SELECT x1.folder_id
FROM (SELECT DISTINCT
fp1.folder_id
FROM folder_permission fp1) x1
WHERE 1 = ALL (SELECT fp2.can_read
FROM folder_permission fp2
WHERE fp2.folder_id = x1.folder_id);
If you have a table for the folders themselves replace the derived table (aliased x1) with it.
But this only respects users present in folder_permissions. If not all users have a reference in that table you possibly won't get the folders really all users can read.

You can do aggregation :
SELECT fp.FOLDER_ID
FROM folder_permission fp
GROUP BY fp.FOLDER_ID
HAVING SUM( can_read = 0 ) = 0;
You can also express it :
SELECT fp.FOLDER_ID
FROM folder_permission fp
GROUP BY fp.FOLDER_ID
HAVING MIN(CAN_READ) = MAX(CAN_READ) AND MIN(CAN_READ) = 1;

If you wanted to return the full matching records, you could try using some exists logic:
SELECT ID, FOLDER_ID, USER_ID, CAN_READ
FROM yourTable t1
WHERE NOT EXISTS (SELECT 1 FROM yourTable t2
WHERE t2.FOLDER_ID = t1.FOLDER_ID AND t2.CAN_READ = 0);
Demo
The existence of a matching record in the above exists subquery would imply that there exist one or more users for that folder who do not have read access rights.

Related

How to find all the opposite combinations between two columns in SQL

I am making a web dating app that needs to match users and let them chat with each other.
I want to figure out how to find all the matches for a particular user.
Right now I have a table called follows that has 2 columns.
UserID | MatchUserID
--------------------
1 | 2
2 | 1
1 | 3
1 | 4
1 | 5
4 | 1
5 | 4
The idea is that for two users to match they need to follow one another. The table above shows which user follows which.
Assuming that the user who is currently logged on is UserID = 1.
I need a query that will return from the MatchUserID table the following results:
2, 4
In a way, I am looking to find all the opposite combinations between the two columns.
This is the code I use to create the table.
CREATE TABLE Match
(
UserID INT NOT NULL,
MatchUserID INT NOT NULL,
PRIMARY KEY (UserID, MatchUserID)
);
You can do it with a self join:
select m.MatchUserID
from `Match` m inner join `Match` mm
on mm.MatchUserID = m.UserId
where
m.UserId = 1
and
m.MatchUserID = mm.UserId
See the demo.
Results:
| MatchUserID |
| ----------- |
| 2 |
| 4 |
The simplest way possibly is to use EXISTS and a correlated subquery that searches for the other match.
SELECT t1.matchuserid
FROM elbat t1
WHERE t1.userid = 1
AND EXISTS (SELECT *
FROM elbat t2
WHERE t2.matchuserid = t1.userid
AND t2.userid = t1.matchuserid);

SQL - select rows that have the same value in two columns

The solution to the topic is evading me.
I have a table looking like (beyond other fields that have nothing to do with my question):
NAME,CARDNUMBER,MEMBERTYPE
Now, I want a view that shows rows where the cardnumber AND membertype is identical. Both of these fields are integers. Name is VARCHAR. Name is not unique, and duplicate cardnumber, membertype should show for the same name, as well.
I.e. if the following was the table:
JOHN | 324 | 2
PETER | 642 | 1
MARK | 324 | 2
DIANNA | 753 | 2
SPIDERMAN | 642 | 1
JAMIE FOXX | 235 | 6
I would want:
JOHN | 324 | 2
MARK | 324 | 2
PETER | 642 | 1
SPIDERMAN | 642 | 1
this could just be sorted by cardnumber to make it useful to humans.
What's the most efficient way of doing this?
What's the most efficient way of doing this?
I believe a JOIN will be more efficient than EXISTS
SELECT t1.* FROM myTable t1
JOIN (
SELECT cardnumber, membertype
FROM myTable
GROUP BY cardnumber, membertype
HAVING COUNT(*) > 1
) t2 ON t1.cardnumber = t2.cardnumber AND t1.membertype = t2.membertype
Query plan: http://www.sqlfiddle.com/#!2/0abe3/1
You can use exists for this:
select *
from yourtable y
where exists (
select 1
from yourtable y2
where y.name <> y2.name
and y.cardnumber = y2.cardnumber
and y.membertype = y2.membertype)
SQL Fiddle Demo
Since you mentioned names can be duplicated, and that a duplicate name still means is a different person and should show up in the result set, we need to use a GROUP BY HAVING COUNT(*) > 1 in order to truly detect dupes. Then join this back to the main table to get your full result list.
Also since from your comments, it sounds like you are wrapping this into a view, you'll need to separate out the subquery.
CREATE VIEW DUP_CARDS
AS
SELECT CARDNUMBER, MEMBERTYPE
FROM mytable t2
GROUP BY CARDNUMBER, MEMBERTYPE
HAVING COUNT(*) > 1
CREATE VIEW DUP_ROWS
AS
SELECT t1.*
FROM mytable AS t1
INNER JOIN DUP_CARDS AS DUP
ON (T1.CARDNUMBER = DUP.CARDNUMBER AND T1.MEMBERTYPE = DUP.MEMBERTYPE )
SQL Fiddle Example
If you just need to know the valuepairs of the 3 fields that are not unique then you could simply do:
SELECT concat(NAME, "|", CARDNUMBER, "|", MEMBERTYPE) AS myIdentifier,
COUNT(*) AS count
FROM myTable
GROUP BY myIdentifier
HAVING count > 1
This will give you all the different pairs of NAME, CARDNUMBER and MEMBERTYPE that are used more than once with a count (how many times they are duplicated). This doesnt give you back the entries, you would have to do that in a second step.

Select from one table but filtering other two

Let's say i've got this database:
book
| idBook | name |
|--------|----------|
| 1 |Book#1 |
category
| idCateg| category |
|--------|----------|
| 1 |Adventures|
| 2 |Science F.|
book_categ
| id | idBook | idCateg | DATA |
|--------|--------|----------|--------|
| 1 | 1 | 1 | (null) |
| 2 | 1 | 2 | (null) |
I'm trying to select only the books which are in category 1 AND category 2 something like this
SELECT book.* FROM book,book_categ
WHERE book_categ.idCateg = 1 AND book_categ.idCateg = 2
Obviously, this giving 0 results becouse each row has only one idCateg it does work width OR but the results are not what I need. I've also tried to use a join, but I just can't get the results I expect.
Here it's the SQLFiddle of my current project, with my current DB, the data at the begining is just a sample. SQLFiddle
Any help will be really appreciated.
Solution using EXISTS:
select *
from book b
where exists (select 'x'
from book_categ x
where x.idbook = b.idbook
and x.idcateg = 1)
and exists (select 'x'
from book_categ x
where x.idbook = b.idbook
and x.idcateg = 2)
Solution using join with an inline view:
select *
from book b
join (select idbook
from book_categ
where idcateg in (1, 2)
group by idbook
having count(*) = 2) x
on b.idbook = x.idbook
You could try using ALL instead of IN (if you only want values that match all criteria to be returned):
SELECT book.*
FROM book, book_categ
WHERE book_categ.idCateg = ALL(1 , 2)
One way to get the result is to do join to the book_categ table twice, something like
SELECT b.*
FROM book b
JOIN book_categ c1
ON c1.book_id = b.id
AND c1.idCateg = 1
JOIN book_categ c2
ON c2.book_id = b.id
AND c2.idCateg = 2
This assumes that (book_id, idCateg) is constrained to be unique in the book_categ table. If it isn't unique, then this query can return duplicate rows. Adding a GROUP BY clause or the DISTINCT keyword will eliminate any generated duplicates.
There are several other queries that can get generate the same result.
For example, another approach to finding book_id that are in two categories is to get all the rows with idCateg values of 1 or 2, and then GROUP BY book_id and get a count of DISTINCT values...
SELECT b.*
FROM book b
JOIN ( SELECT d.book_id
FROM book_categ d
WHERE d.idCateg IN (1,2)
GROUP BY d.book_id
HAVING COUNT(DISTINCT d.idCateg) = 2
) c
ON c.book_id = b.id

mysql small count issue on same table

Please find db structure as following...
| id | account_number | referred_by |
+----+-----------------+--------------+
| 1 | ac203003 | ac203005 |
+----+-----------------+--------------+
| 2 | ac203004 | ac203005 |
+----+-----------------+--------------+
| 3 | ac203005 | ac203004 |
+----+-----------------+--------------+
I want to achieve following results...
id, account_number, total_referred
1, ac203005, 2
2, ac203003m 0
3, ac203004, 1
And i am using following query...
SELECT id, account_number,
(SELECT count(*) FROM `member_tbl` WHERE referred_by = account_number) AS total_referred
FROM `member_tbl`
GROUP BY id, account_number
but its not giving expected results, please help. thanks.
You need to use table aliases to do this correctly:
SELECT id, account_number,
(SELECT count(*)
FROM `member_tbl` t2
WHERE t2.referred_by = t1.account_number
) AS total_referred
FROM `member_tbl` t1;
Your original query had referred_by = account_number. Without aliases, these would come from the same row -- and the value would be 0.
Also, I removed the outer group by. It doesn't seem necessary, unless you want to remove duplicates.
One idea is to join the table on itself. This way you can avoid the subquery. There might be performance gains with this approach.
select b.id, b.account_number, count(a.referred_by)
from member_tbl a inner join member_tbl b
on a.referred_by=b.account_number
group by (a.referred_by);
SQL fiddle: http://sqlfiddle.com/#!2/b1393/2
Another test, with more data: http://sqlfiddle.com/#!2/8d216/1
select t1.account_number, count(t2.referred_by)
from (select account_number from member_tbl) t1
left join member_tbl t2 on
t1.account_number = t2.referred_by
group by t1.account_number;
Fiddle for your data
Fiddle with more data

MySQL - How to display row value as column name using concat and group_concat

Table 1:
id | typeid | available|
0 | 1 | 12 |
0 | 2 | 44 |
Table 2:
typeid | typename |
1 | CL |
2 | ML |
I have a query using concat and group_concat:
select id,concat(group_concat(typename,available)) as types from table1
join table2 on table2.typeid=table1.typeid
I got the result as:
id | types |
0 | CL12,ML44 |
But I want to display it like this:
id | CL | ML |
0 | 12 | 44 |
Is there any way to split the group_concat result to columns heads?
I want dynamically fetch data from table2. Some user can add data to table2. So hard-coding typename is not possible.
You should use table pivoting. There is no PIVOT command in MySQL, so you can use this query -
SELECT
t1.id,
MAX(IF(t2.typename = 'CL', t1.available, NULL)) AS CL,
MAX(IF(t2.typename = 'ML', t1.available, NULL)) AS ML
FROM table1 t1
JOIN table2 t2
ON t1.typeid = t2.typeid
GROUP BY
t1.id;
MySQL pivot tables (transform rows to columns).
Use GROUP_CONCAT function instead of MAX, if multiple available values are possible.
It can be done only if you know all the typenames in advance -- otherwise, you'll need to find another way. In databases that support returning result sets from stored procedures, it could be done with a stored proc. But mysql doesn't support that.
If you know all the typenames, here's how you build the query:
SELECT
id,
SUM(IF(typename = 'CL', available, 0)) AS `CL`,
SUM(IF(typename = 'ML', available, 0)) AS `ML`
FROM table1 join table2 on table1.typeid = table2.typeid
GROUP BY id
Try this:
SELECT a.id, MAX(IF(b.typename = 'CL', a.available, 0)) CL,
MAX(IF(b.typename = 'ML', a.available, 0)) ML
FROM table1 a
INNER JOIN table2 b ON a.typeid=b.typeid
GROUP BY a.id;
Use SUM function if you want to sum of the data from available column for particualr type else use the same query as ii is.