Select inner join with n_m table - mysql

I have three tables as following:
USERS TABLE
id_user| name |
---------------
1 | ...
2 | ...
SERVICES TABLE
id_service | name |
-------------------
1 | ...
2 | ...
3 | ...
USER_SERVICES TABLE (n-m)
id_user | id_service
--------------------
1 | 1
1 | 2
2 | 1
And I need to do a SELECT starting from "SELECT * FROM users" and then, getting the users by services. Ex. I need to get every user with services = 1 and services = 2 (and maybe he has other more services, but 1 and 2 for sure).
I did the following:
SELECT *
FROM `users`
INNER JOIN user_services ON users.id_user = user_services.id_user
WHERE id_service=1 AND id_service=2
But this, of course dont works since there is not a single record matching service = 1 and service = 2.
What can I do?

Add an extra join for the other service you want to check:-
SELECT *
FROM `users`
INNER JOIN user_services us1 ON users.id_user = us1.id_user AND us1.id_service=1
INNER JOIN user_services us2 ON users.id_user = us2.id_user AND us2.id_service=2

select t.*,
(select count(*) from user_services where id_user = t.id_user) how_much
from users t;
Is this what you want???
It shows the data of the users and how much services are in the services table. Other possibility is this:
select t.*,
(case when (select count(*)
from user_services where id_user = 1) > 0
then 'service1'
else 'null'
end) has_service_1
from users t;
The problem with this select is that you have to repeat this case...end as much times as id_services you have, so it doesn't make sense if the number of services is increasing over time. On the contrary, if it is a somewhat fixed number, and it is not a big number, this could be a solution.

Related

getting data from multiple tables and applying arithmatic operation on the result

I want to fetch data from two table and apply arithmetic operation on the column.
This is wha I tried :
String sql = "SELECT SUM(S.san_recover-C.amount) as total
FROM sanction S
LEFT JOIN collection C ON S.client_id = C.client_id
WHERE S.client_id=?";
This code is working only when there is value in both tables, but if there is no value in one of two tables there is no result.
SELECT SUM(S.san_recover - C.amount) as total
FROM sanction S
LEFT JOIN collection C ON S.client_id = C.client_id
WHERE S.client_id = ?
The problem with your query lies in the SUM() function. When the left join does not bring back records, then c.amount is NULL. When substracting NULL from something, you get a NULL result, which then propagates across the computation, and you end up with a NULL result for the SUM().
You probably want COALESCE(), like so:
SELECT SUM(S.san_recover - COALESCE(C.amount, 0)) as total
FROM sanction S
LEFT JOIN collection C ON S.client_id = C.client_id
WHERE S.client_id = ?
Where there is a possibility that a client may exist in one table but no another a full join would be appropriate but since mysql does not have such a thing then a union in a sub query will do
drop table if exists sanctions,collections;
create table sanctions(client_id int, amount int);
create table collections(client_id int, amount int);
insert into sanctions values
(1,10),(1,10),(2,10);
insert into collections values
(1,5),(3,10);
Select sum(Samount - camount)
From
(Select sum(amount) Samount, 0 as camount from sanctions where client_id =3
Union all
Select 0,sum(amount) as camount from collections where client_id =3
) s
;
+------------------------+
| sum(Samount - camount) |
+------------------------+
| -10 |
+------------------------+
1 row in set (0.00 sec)
If you want to do this for all clients
Select client_id,sum(Samount - camount) net
From
(Select client_id,sum(amount) Samount, 0 as camount from sanctions group by client_id
Union all
Select client_id,0,sum(amount) as camount from collections group by client_id
) s
group by client_id
;
+-----------+------+
| client_id | net |
+-----------+------+
| 1 | 15 |
| 2 | 10 |
| 3 | -10 |
+-----------+------+
3 rows in set (0.00 sec)

Query: I have 4 rows, need to add the results from 3 rows into one, and leave the last row untouched

I have a kind of tricky question for this query. First the code:
SELECT user_type.user_type_description,COUNT(incident.user_id) as Quantity
FROM incident
INNER JOIN user ON incident.user_id=user.user_id
INNER JOIN user_type ON user.user_type=user_type.user_type
WHERE incident.code=2
GROUP BY user.user_type
What Am I doing?
For example, I am counting police reports of robbery, made from different kind of users. In my example, "admin" users reported 6 incidents of code "2" (robbery) and so on, as is showed in 'where' clause (incident must be robbery, also code 2).
this brings the following result:
+-----------------------+----------+
| user_type_description | Quantity |
+-----------------------+----------+
| Admin | 6 |
| Moderator | 8 |
| Fully_registered_user | 8 |
| anonymous_user | 9 |
+-----------------------+----------+
Basically Admin,Moderator and Fully_registered_user are appropriately registered users. I need to add them in a result where it shows like:
+--------------+------------+
| Proper_users | Anonymous |
+--------------+------------+
| 22 | 9 |
+--------------+------------+
I am not good with sql. Any help is appreciated. Thanks.
You can try to use condition aggregate function base on your current result set.
SUM with CASE WHEN expression.
SELECT SUM(CASE WHEN user_type_description IN ('Admin','Moderator','Fully_registered_user') THEN Quantity END) Proper_users,
SUM(CASE WHEN user_type_description = 'anonymous_user' THEN Quantity END) Anonymous
FROM (
SELECT user_type.user_type_description,COUNT(incident.user_id) as Quantity
FROM incident
INNER JOIN user ON incident.user_id=user.user_id
INNER JOIN user_type ON user.user_type=user_type.user_type
WHERE incident.code=2
GROUP BY user.user_type
) t1
You just need conditional aggregation:
SELECT SUM( ut.user_type_description IN ('Admin', 'Moderator', 'Fully_registered_user') ) as Proper_users,
SUM( ut.user_type_description IN ('anonymous_user') as anonymous
FROM incident i INNER JOIN
user u
ON i.user_id = u.user_id INNER JOIN
user_type ut
ON u.user_type = ut.user_type
WHERE i.code = 2;
Notes:
Table aliases make the query easier to write and to read.
This uses a MySQL shortcut for adding values -- just just adding the booelean expressions.
I would solve it with a CTE, but it would be better to have this association in a table.
WITH
user_type_categories
AS
(
SELECT 'Admin' AS [user_type_description] , 'Proper_users' AS [user_type_category]
UNION SELECT 'Moderator' AS [user_type_description] , 'Proper_users' AS [user_type_category]
UNION SELECT 'Fully_registered_user' AS [user_type_description] , 'Proper_users' AS [user_type_category]
UNION SELECT 'anonymous_user' AS [user_type_description] , 'Anonymous' AS [user_type_category]
)
SELECT
CASE WHEN utc.[user_type_category] = 'Proper_users' THEN
SUM(incident.user_id)
END AS [Proper_Users_Quantity]
, CASE WHEN utc.[user_type_category] = 'Anonymous' THEN
SUM(incident.user_id)
END AS [Anonymous_Quantity]
FROM
[incident]
INNER JOIN [user] ON [incident].[user_id] = [user].[user_id]
INNER JOIN [user_type] ON [user].[user_type] = [user_type].[user_type]
LEFT JOIN user_type_categories AS utc ON utc.[user_type_description] = [user_type].[user_type_description]
WHERE
[incident].[code] = 2

Mysql, left join and count conditions

I have fallowing SQL query (pseudo query):
SELECT
some columns [...]
COUNT(clicks.id) AS clicks,
COUNT(transactions.id) AS transactions
FROM
campaign
LEFT JOIN
clicks ON clicks.key = campaign.key
LEFT JOIN (
SELECT
id, key
FROM
transactions
GROUP BY
userkey
) transactions ON clicks.key = transactions.key;
GROUP BY
campaign.id
Query return good results. On example:
column | columns [..] | 34 | 10
column | columns [..] | 22 | 1
column | columns [..] | 34 | 17
So, records in table clicks they have transactions or a few transactions or they do not.
Haw Can I retun COUNT() clicks who have COUNT(transactions.id) = 0 and COUNT(transactions.id) > 0 ? On example:
column | columns [..] | 34 | 10 | 4 (count data from clicks table which have related transactions) | 30 (count data from clicks table which not have related transactions)
column | columns [..] | 22 | 1 | 6 | 16
column | columns [..] | 34 | 17 | 10 | 24
Tahnks for help.
#UPDATE:
I solved my problem adding second table. Now my SQL query looks like:
SELECT
some columns [...]
COUNT(clicks.id) AS clicks,
COUNT(transactions.id) AS transactions,
COUNT(clicks_count.id) as witchout_transactions,
(COUNT(clicks.id) - COUNT(clicks_count.id)) as witch_transactions
FROM
campaign
LEFT JOIN
clicks ON clicks.key = campaign.key
LEFT JOIN (
SELECT
id, key
FROM
transactions
GROUP BY
userkey
) transactions ON clicks.key = transactions.key
LEFT JOIN (
SELECT
clicks.id,
COUNT(transactions.id) AS transactions
FROM
clicks
LEFT JOIN transactions ON clicks.key = transactions.key
GROUP BY clicks.id
HAVING transactions = 0
) clicks_count ON clicks_count.id = clicks.id
GROUP BY
campaign.id
If I understand correctly, you can try to use CASE WHEN expression and COUNT
Because you didn' provide any sample data and expected result, so I can only provide pseudo-query.
SELECT...,
COUNT(CASE WHEN [have transactions condition] then 1 end),
COUNT(CASE WHEN [not have related transactions condition] then 1 end)
If that didn't help you, you can provide some data and expect result, I will edit my answer.
What about adding a second join with the clicks table
JOIN (
SELECT id
FROM clicks
LEFT JOIN transactions ON clicks.key = transactions.key AND transactions.id != 0) clicks2 ON clicks.id = clicks2.id
And in the select clause use this to for the two columns
SELECT
some columns [...]
COUNT(clicks.id) AS clicks,
COUNT(transactions.id) AS transactions
COUNT(clicks2.id) as clicks_with
clicks - clicks_with AS clicks_without

sql join with foreign key table

I want to select all users from my database with emails ending #gmail.com which are not already in the group with the groupID 4.
The problem is my user_to_group table looks like this:
userID | groupID
--------------------
1 | 5
1 | 4
1 | 3
2 | 3
2 | 6
Users with the groupID 4 are excluded, but because they are also in other groups, they will be selected anyway. In this example I just need the user with the userID 2.
Is it possible to exclude users which are in group 4 regardless of their other groups?
SELECT * FROM wcf13_user user_table
RIGHT JOIN wcf13_user_to_group ON (wcf13_user_to_group.userID = user_table.userID && groupID != 4 )
WHERE user_table.email LIKE "%#gmail.com"
Yes, you can do it with an EXISTS subquery:
SELECT *
FROM wcf13_user user_table u
WHERE user_table.email LIKE "%#gmail.com" -- Has a gmail account
AND NOT EXISTS ( -- Is not a member of group #4
SELECT *
FROM wcf13_user_to_group g
WHERE u.userID=g.userID AND groupID = 4
)
This is a good place to use the not exists clause:
SELECT ut.*
FROM wcf13_user ut
WHERE not exists (select 1
from wcf13_user_to_group utg
where utg.userID = ut.userID and utggroupID = 4
) and
ut.email LIKE '%#gmail.com';

Select those that HAVE NEVER had certain entry

I have a table of services that have been provided to clients. I'm trying to make a query that selects all the clients who received a service that WEREN'T provided by a certain user.
So consider this table...
id_client | id_service | id_user
--------------------------------
5 | 3 | 2
7 | 4 | 2
7 | 4 | 1
9 | 4 | 2
8 | 4 | 1
If I write the query like this:
SELECT id_client FROM table WHERE id_service=4 AND id_user<>1
I still end up getting id_client 7. But I don't want to get client 7 because that client HAS received that service from user 1. (They're showing up because they've also received that service from user 2)
In the example above I would only want to be returned with client 9
How can I write the query to make sure that clients that have EVER received service 4 from user 1 don't show up?
Try this:
SELECT DISTINCT id_client
FROM yourtable t
WHERE id_service = 4 AND id_client NOT IN
(SELECT DISTINCT id_client
FROM yourtable t
WHERE id_user = 1
)
I'd write it like this:
SELECT DISTINCT id_client
FROM mytable t1
LEFT OUTER JOIN mytable t2
ON t1.id_client = t2.id_client AND t2.id_user = 1
WHERE t2.id_client IS NULL
When the conditions of a left outer join are not met, the row on the left side is still returned, and all the columns for the row on the right side are null. So if you search for cases of null in a column that would be certain to be non-null if there were a match, you know the outer join found no match.
SELECT id_client
FROM table
WHERE id_service = 4
GROUP BY id_client
HAVING MAX(CASE
WHEN id_user = 1 THEN 2
ELSE 1
END) = 1