How can I select the users which are belonging to group A? - mysql

How can I select the users which are belonging to group A?
My tables are below.
my user table.
ID | name |sex
1 | bob |1
2 | kayo |2
3 | ken |1
my fos_group table
ID | name
1 | student
2 | teacher
my fos_user_user_group
user_id | group_id
1 | 1
2 | 2
3 | 1
Bob and Ken are belonging to group_1(student)
Kayo is belonging to group_2(teacher)
ex) I can select 'Bob' from user table like this
$query = $em->createQuery(
"SELECT p.name,p.sex
FROM UserBundle:User p WHERE
p.id = '1' );
But I would like to select the users which belongs to student group(Bob and Ken)
How should I change the sentence in createQuery?
I just guess I need to join the tables though...
additional....
I have tried like this accroding to Fabio's answer
$query = $em->createQuery(
"SELECT p,p.id,p.username,p.userKey
FROM UserBundle:User p
INNER JOIN fos_user_user_group b
ON a.ID = b.user_id
INNER JOIN fos_group c
ON b.group_id = c.ID
WHERE c.group_id = '1'");
$this->data["teachers"] = $query->getResult();
but it says
[Semantical Error] line 0, col 94 near 'fos_user_user_group': Error: Class 'fos_user_user_group' is not defined.
I guess it means I dont have entity for 'fos_user_user_group'.
I have only entity class for Group and User,other tables were created automatically.
In meanwhile,I used like this in other place in $formmapper.
->add('teacher',
null,
array(
'query_builder' =>
function (\Doctrine\ORM\EntityRepository $rep) {
return $rep->
createQueryBuilder('s')
->join('s.groups', 'g')
->where('g.name = :group')->setParameter('group','TeacherGroup');
})
)
it works well,
how can I change this sentence for createQuery()?

I think you can use a INNER JOIN query
SELECT p.name,p.sex
FROM User p
INNER JOIN fos_user_user_group b
ON a.ID = b.user_id
INNER JOIN fos_group c
ON b.group_id = c.ID
WHERE c.group_id = '1'

Related

COnditional JOIN query in MySQL

I have two mysql tables
user:
|--------------------------------|
| id | name | type | ruser_type |
|--------------------------------|
| 1 | Admin | a | |
| 2 | | r | c |
|--------------------------------|
customer
|-------------------------|
| id | name | user_id |
|-------------------------|
| 1 | Sam | 2 |
|-------------------------|
If user.type is 'a' or 's', then its admin user whose name is in user table.
If user.type is 'r' and ruser_type is 'c', then its regular user which has a relation in customer table where customer.user_id = user.id
I want a query which would run a conditional join.
If user.type is 'a' or 's', then name would be fetched from user table.
If user.type is 'r' and and ruser_type is 'c', then name would be fetched from customer table with the JOIN condition customer.user_id = user.id.
For this, I have written a query like this:-
SELECT users.fname as adminFname, customers.fname as customerFname, users.type FROM users
LEFT JOIN customers ON (customers.user_id = users.id AND
(
(users.type = 'r' AND users.ruser_type = 'c')
OR users.type = 'a'
OR users.type = 's'
)
)
WHERE users.id = 1
Is there any possibility to optimize the query more?
Also, how can I write this query using Laravel eloquent?
FWIW, I find this marginally easier to read...
SELECT u.fname adminFname
, c.fname customerFname
, u.type
FROM users u
LEFT
JOIN customers c
ON c.user_id = u.id
WHERE u.id = 1
AND (
(u.type = 'r' AND u.ruser_type = 'c')
OR (u.type IN('a','s'))
)
I have written two sql query hope this will help you
SELECT CASE CU.type WHEN 'a' OR 's' THEN CU.name END AS name,
CASE WHEN CU.type = 'r' AND CU.ruser_type = 'c' THEN CR.name END AS cust_name, CU.type
FROM
user AS CU
LEFT JOIN customer AS CR ON CR.user_id = CU.id
In this you'll get result like this,
name cust_name type
Sam r
Admin a
and i have wrote another query like this,
SELECT CASE WHEN CU.type = 'a' OR 's' THEN CU.name ELSE CR.name END AS name, CU.type
FROM
user AS CU
LEFT JOIN customer AS CR ON CR.user_id = CU.id
In this you'll get result like this
name type
Sam r
Admin a
DB File Link
https://dbfiddle.uk/?rdbms=mysql_5.7&fiddle=b02d931b68a4f70d8b4a84144c60a572
This will give you required result for all users:
SELECT u.fname adminFname
, c.fname customerFname
, u.type
FROM users u
LEFT JOIN customers c
ON (u.type = 'r' AND u.ruser_type = 'c' AND c.user_id = u.id)
Add where condition as required.
You can even simplify it further to get common firstName column in output:
SELECT COALESCE(u.fname, c.fname) firstName, u.type
FROM users u
LEFT JOIN customers c
ON (u.type = 'r' AND u.ruser_type = 'c' AND c.user_id = u.id)

SQL query on join table

I've the follow SQL schema:
+----------+
| products |
+----------+
| id |
| name |
+----------+
^ 8
|
v 1
+-------------+
| values |
+-------------+
| value |
| product_id |
| property_id |
+-------------+
^ 8
|
v 1
+------------+
| properties |
+------------+
| id |
| name |
+------------+
One product has many properties and a property belongs to many products. The values table is the join table for the many_to_many association between products and properties. And in this table is saved the value of the property for a product.
Now I'm looking for a query to select all products with property x with value a, and property y with value b ecc. My try is this query but return no records:
SELECT DISTINCT
products.*
FROM
products
INNER JOIN
product_values
ON product_values.product_id = products.id
INNER JOIN
properties
ON properties.id = product_values.property_id
WHERE
(properties.name = 'size' AND product_values.value = 'big')
AND (properties.name = 'color' AND product_values.value = 'red')
If possible I need a query with no nested select.
Since a property can not be color and size at the same time you need to use OR in your where clause. Then group the data and check if both are in the group with having
SELECT products.id, products.name
FROM `products`
INNER JOIN `product_values` ON `product_values`.`product_id` = `products`.`id`
INNER JOIN `properties` ON `properties`.`id` = `product_values`.`property_id`
WHERE (properties.name = 'size' AND product_values.value = 'big')
OR (properties.name = 'color' AND product_values.value = 'red')
GROUP BY products.id, products.name
HAVING count(distinct properties.name) = 2
I would do this using group by and having:
select pv.product_id
from product_values pv join
properties p
on pv.property_id = p.id
where (p.name, v.value) in ( ('size', 'big'), ('color', 'red') )
group by pv.product_id
having count(distinct p.name) = 2;
Another approach using sum to filter multiple attributes for an entity
SELECT
`p`.*
FROM
`products` p
INNER JOIN `product_values` v
ON `v`.`product_id` = `p`.`id`
INNER JOIN `properties` pr
ON `pr`.`id` = `v`.`property_id`
GROUP BY p.id
HAVING SUM (pr.name = 'size' AND v.value = 'big')
AND SUM(pr.name = 'color' AND v.value = 'red')

MySQL multiple inner join between 2 tables on different columns for one of the tables

Table transport
Id | FirstLevSubcat | SecondLevSubcat | ThirdLevSubcat
--------------------------------------------------------
8 | 4 | 27 | 1418
Table categories
Id | CategoriesUrl
--------------------
4 | cars
27 | audi
1418 | audi-100
Query if not to use categories table (without inner join) would be like
SELECT count(*) FROM transport
WHERE FirstLevSubcat = 4 AND SecondLevSubcat = 27 AND ThirdLevSubcat = 1418
Trying to get the same result using INNER JOIN
SELECT count(*) FROM transport main_table
INNER JOIN categories cat_table_first ON cat_table_first.IdRows = main_table.FirstLevSubcat
INNER JOIN categories cat_table_second ON cat_table_second.IdRows = main_table.SecondLevSubcat
INNER JOIN categories cat_table_third ON cat_table_third.IdRows = main_table.ThirdLevSubcat
WHERE
cat_table_first.CategoriesUrl = 'cars'
AND cat_table_second.CategoriesUrl = 'audi'
AND cat_table_third.CategoriesUrl = 'audi-100'
At first sight all works
But is such query ok? May be can improve something?
Your query is correct. You can also do it in following way:
SELECT count(*) FROM transport main_table
INNER JOIN categories cat_table_first ON cat_table_first.IdRows = main_table.FirstLevSubcat and cat_table_first.CategoriesUrl = 'cars'
INNER JOIN categories cat_table_second ON cat_table_second.IdRows = main_table.SecondLevSubcat and cat_table_second.CategoriesUrl = 'audi'
INNER JOIN categories cat_table_third ON cat_table_third.IdRows = main_table.ThirdLevSubcat and cat_table_third.CategoriesUrl = 'audi-100'
You can also do it using 3 EXISTS block.
SELECT count(*) FROM transport main_table
WHERE
EXISTS (SELECT NULL FROM categories WHERE main_table.FirstLevSubcat=categories.IdRows AND categories.CategoriesUrl ='cars')
AND
EXISTS (SELECT NULL FROM categories WHERE main_table.SecondLevSubcat=categories.IdRows AND categories.CategoriesUrl ='audi')
AND
EXISTS (SELECT NULL FROM categories WHERE main_table.ThirdLevSubcat=categories.IdRows AND categories.CategoriesUrl ='audi-100')

Why my right join isn't working?

I need to show all categories, even categories with no items.
I have this query.
SELECT
i.id,
incident_active 'Approved',
incident_verified 'Verified',
category_title 'Category',
ParentCategory 'Parent Category'
FROM
incident i
INNER JOIN
incident_category ic ON i.id = ic.incident_id
RIGHT JOIN
incident_person ip ON i.id = ip.incident_id
RIGHT JOIN
(SELECT
c1.id,
c1.parent_id,
c2.category_title ParentCategory,
CONCAT_WS(' -> ', c2.category_title, c1.category_title) category_title
FROM
category c1
left outer join category c2 ON c1.parent_id = c2.id WHERE c1.parent_id != 0) AS c ON c.id = ic.category_id
WHERE incident_dateadd > DATE_SUB(NOW(), INTERVAL 1 MONTH)
which return:
and this query:
SELECT
c1.id,
c1.parent_id,
c2.category_title ParentCategory,
CONCAT_WS(' -> ', c2.category_title, c1.category_title) category_title
FROM
category c1
left outer join category c2 ON c1.parent_id = c2.id WHERE c1.parent_id != 0
which return:
I've read several times this answer but I can not see why my right join isn't working.
The first result set should have 8 more columns, the columns of categories which parent is Protesta
UPDATE
I got it working whith the following query:
SELECT * FROM (SELECT
i.id,
incident_title 'Título',
incident_description 'Descripción',
incident_date 'Fecha',
incident_active 'Aprobado',
incident_verified 'Veficado',
person_first 'Nombres',
person_last 'Apellidos',
person_email 'Email',
category_id
-- category_title 'Categoría',
-- ParentCategory 'Categoría Padre'
FROM
incident i
INNER JOIN
incident_category ic ON i.id = ic.incident_id
RIGHT JOIN
incident_person ip ON i.id = ip.incident_id
WHERE (incident_dateadd > DATE_SUB(NOW(), INTERVAL 1 MONTH) OR incident_dateadd IS NULL)) a
RIGHT JOIN
(SELECT
c1.id,
c1.parent_id,
c2.category_title ParentCategory,
CONCAT_WS(' -> ', c2.category_title, c1.category_title) category_title
FROM
category c1
left outer join category c2 ON c1.parent_id = c2.id WHERE c1.parent_id != 0) b ON a.category_id = b.id
Although I still don't understand why it was not working with the first version, in my mind both queries are equivalent.
If anyone could explain the differences...
It's the location of your final where clause.
In your fist query, you pull all of your categories and associate them with a bunch of data, getting a compilation of rows. You then use a where clause to filter out many of those rows, some of which happen to be category rows.
Let's look at a simple example.
Table A:
X | Y
-----
1 | hi
2 | bye
3 | what
Table B:
Z | X
-----
A | 1
B | 1
C | 2
Given these tables, if I say the following
SELECT * FROM `B` RIGHT JOIN `A` ON A.X = B.X
my result will be:
Z | X | Y
---------
A | 1 | hi
B | 1 | hi
C | 2 | bye
- | 3 | what
If, however, I add a where clause on the end of that so my query becomes
SELECT * FROM `B` RIGHT JOIN `A` ON A.X = B.X WHERE B.Z > 'A'
some of table A is filtered out. Now I have:
Z | X | Y
---------
B | 1 | hi
C | 2 | bye
However, if my query does the filtering before the join, like so:
SELECT * FROM
(SELECT * FROM `B` WHERE B.Z > 'A') AS B
RIGHT JOIN `A` ON A.X = B.X
my table still contains all the rows from A.
Z | X | Y
---------
B | 1 | hi
C | 2 | bye
- | 3 | what
It's just a matter of order. In your original query, you select all the rows then filter out some. In your working query, you first filter, then you get all the category rows you need.

Can I fire two select statements in a single MySQL command and count resulting rows?

Here's my code as is, working, but very slow:
$graded = R::getAll("SELECT posts.id, posts.discussion, rating.rating, rating.itemid
FROM uv_forum_posts posts
JOIN uv_rating rating ON ( posts.id = rating.itemid )
WHERE posts.discussion = :discussion_id
GROUP BY posts.userid",
array(':discussion_id' => $discussion['id']));
$total = R::getAll("SELECT posts.userid
FROM uv_forum_posts posts
WHERE posts.discussion = :discussion_id
GROUP BY userid",
array(':discussion_id' => $discussion['id']));
$percentages[] = count($graded) / count($total) * 100;
$graded represents all rows that have a rating.
$total represents all user participation, regardless of being graded or not.
I'm only interested in the numerical values of the resulting sets, can I combine these two MySQL calls into a single call that returns two numbers graded and total?
If I correctly understand your requirements you can try
SELECT p.userid,
COUNT(*) total,
COUNT(r.itemid) graded
FROM uv_forum_posts p LEFT JOIN
uv_rating r ON p.id = r.itemid
WHERE p.discussion = :discussion_id
GROUP BY p.userid
Output:
| USERID | TOTAL | GRADED |
---------------------------
| 1 | 8 | 2 |
| 2 | 4 | 4 |
SQLFiddle
UPDATE: If you just want grand total for all posts and users then
SELECT COUNT(*) total,
COUNT(r.itemid) graded
FROM uv_forum_posts p LEFT JOIN
uv_rating r ON p.id = r.itemid
WHERE p.discussion = 1
Output:
| TOTAL | GRADED |
------------------
| 12 | 6 |
SQLFiddle
You could simply use a query as in the format :
Select
( SELECT posts.id, posts.discussion, rating.rating, rating.itemid
FROM uv_forum_posts posts
JOIN uv_rating rating ON ( posts.id = rating.itemid )
WHERE posts.discussion = :discussion_id
GROUP BY posts.userid,
array(':discussion_id' => $discussion['id']))) gradeCount,
(SELECT posts.userid
FROM uv_forum_posts posts
WHERE posts.discussion = :discussion_id
GROUP BY userid,
array(':discussion_id' => $discussion['id'])))totalCount