Many to many outer join clause - sql

This is my database schema:
user
| id | firstName |
|----|-----------|
| 1 | Adam |
| 2 | Bob |
permission
| id | title |
|----|-------|
| 1 | Foo |
| 2 | Bar |
| 3 | XYZ |
| 4 | ABC |
user_permissions
| user_id | permission_id |
|---------|---------------|
| 1 | 1 |
| 1 | 2 |
| 2 | 1 |
I want to check which permissions the user with id = 1 has and which he doesn't. I tried with this:
select up.permission_id, up.user_id
from permission a
right outer join user_permissions up on a.id = up.permission_id
right outer join user u on u.id = up.user_id
where u.id = 1
but I got:
| permission_id | user_id |
|---------------|---------|
| 1 | 1 |
| 2 | 1 |
and what I want to get is:
| permission_id | user_id |
|---------------|---------|
| 1 | 1 |
| 2 | 1 |
| 3 | NULL |
| 4 | NULL |
Any ideas whats wrong there?

Your where clause states that where u.id = 1, which will filter away the wanted results. you can add an or clause or coalesce function. where u.id = 1 or u.id is null or with coalesce where coalesce(u.id, 1) = 1. (Coalesce returns the value in second parameter if first parameter is null.)

Related

Match and set child's ID based on condition

I've got a primary incremental ID column and have to find and set all its childs (in ParentID column) based on values from two other columns (Condition1 and Condition2)
Started ParentID always has Condition2 = 1 (and the same value in Condition1 column)
Initial table
+---------------------------------------------
| ID | ParentID | Condition1 | Condition2 |
+---------------------------------------------
| 1 | null | 1000 | 1 |
| 2 | null | 1000 | null |
| 3 | null | 1000 | null |
| 4 | null | 2000 | 1 |
| 5 | null | 2000 | null |
| 6 | null | 2000 | null |
| 7 | null | 3000 | 1 |
| 8 | null | 3000 | null |
| 9 | null | 3000 | null |
+---------------------------------------------
Desired Output
+---------------------------------------------
| ID | ParentID | Condition1 | Condition2 |
+---------------------------------------------
| 1 | 1 | 1000 | 1 |
| 2 | 1 | 1000 | null |
| 3 | 1 | 1000 | null |
| 4 | 4 | 2000 | 1 |
| 5 | 4 | 2000 | null |
| 6 | 4 | 2000 | null |
| 7 | 7 | 3000 | 1 |
| 8 | 7 | 3000 | null |
| 9 | 7 | 3000 | null |
+---------------------------------------------
Current code returns only one row for each new ID
update u
set u.ParentID = u.ID
from [db].[dbo].[tbl] u
inner join [db].[dbo].[tbl] on
u.Condition2 = 1 and u.Condition1 = u.Condition1
I think one intuitive way of doing this is
UPDATE [db].[dbo].[tbl] u
SET u.ParentID = (SELECT id FROM [db].[dbo].[tbl] u2 WHERE u2.condition1 = u.condition1 and u2.condition2 = 1)
I don't mean that this is better than what you're trying to do, just that I think it's very intuitive and easy to understand if you're having issues.
I think the solution you are looking for is:
update u
set u.ParentID = u2.ID
from [db].[dbo].[tbl] u
inner join [db].[dbo].[tbl] u2 on
u2.Condition2 = 1 and u.Condition1 = u2.Condition1
The important differences here are that I have given the both tables in the join-to-self a name (u and u2). You don't want to set u.ParentID = u.ID (as in your question), you want to set u.ParentID = u2.ID (note the 2). Similarly, you don't want to join the tables on u.condition1 = u.condition1 (since that is always true in this example) or u.condition2 = 1 (since you're applying that condition to the table you're updating, not the table you joined). Even though it is a join-to-self, you need to be clear about which table you are referencing. u in your query refers to the table being updated, but not the table on the right-side of the join.

SQL Count depending on certain conditions

I have two tables.
One have userid and email (users table). The other have payments information (payments table) from the userid in users.
users
+--------+------------+
| Userid | Name |
+--------+------------+
| 1 | Alex T |
| 2 | Jeremy T |
| 3 | Frederic A |
+--------+------------+
payments
+--------+-----------+------------+----------+
| Userid | ValuePaid | PaidMonths | Refunded |
+--------+-----------+------------+----------+
| 1 | 1 | 12 | null |
| 1 | 20 | 12 | null |
| 1 | 20 | 12 | null |
| 1 | 20 | 1 | null |
| 2 | 1 | 1 | null |
| 2 | 20 | 12 | 1 |
| 2 | 20 | 12 | null |
| 2 | 20 | 1 | null |
| 3 | 1 | 12 | null |
| 3 | 20 | 1 | 1 |
| 3 | 20 | 1 | null |
+--------+-----------+------------+----------+
I want to count the PaidMonths taking in consideration the following rules:
If ValuePaid < 10 PaidMonths should be = 0.23 (even if in the column the value seen is any other mumber).
If Refund=1 the PaidMonths should be = 0.
Based on this when i join both tables by userid, and sum the PaidMonths based in the previousrules, i expect to see as result:
+--------+------------+------------+
| userid | Name | paidMonths |
+--------+------------+------------+
| 1 | Alex T | 25.23 |
| 2 | Jeremy T | 13.23 |
| 3 | Frederic A | 1.23 |
+--------+------------+------------+
Can you help me to achieve this in the most elegant way? Should a temporary table be used?
The following gives your desired results, using apply with case expression to map your values:
select u.UserID, u.Name, Sum(pm) PaidMonths
from users u join payments p on p.userid=u.userid
cross apply (values(
case
when valuepaid <10 then 0.23
when Refunded=1 then 0
else PaidMonths end
))x(pm)
group by u.UserID, u.Name
See Working Fiddle

Sqlite select from multiple tables

I have five tables such as
Base Table
| group_id | group_name |
|----------|-------------|
| 1 | gn1 |
| 2 | gn2 |
| 3 | gn3 |
"Tags" Table
| tag_id | tag_name |
|--------|----------|
| 1 | tgn1 |
| 2 | tgn2 |
| 3 | tgn3 |
"Theme" Table
| theme_id | theme_name |
|----------|------------|
| 1 | thn1 |
| 2 | thn2 |
| 3 | thn3 |
"Tags" Mapping Table
| rec_id | group_id | tag_id |
|--------|----------|--------|
| 1 | 1 | 2 |
| 2 | 1 | 3 |
| 3 | 2 | 1 |
"Theme" Mapping Table
| rec_id | group_id | theme_id |
|--------|----------|----------|
| 1 | 1 | 2 |
| 2 | 2 | 3 |
| 3 | 2 | 1 |
I am having some trouble creating a SQLite query to get a table like this:
| group_id | group_name | tags | themes |
|----------|------------|------------|------------|
| 1 | gn1 | tgn2, tgn3 | thn2 |
| 2 | gn2 | tgn1 | thn3, thn1 |
| 3 | gn3 | | |
The group_concat function will do the trick - join all the tables, group by the group id and name, and group_concat the other details:
SELECT g.group_id,
g.group_name,
GROUP_CONCAT(t.tag_name, ', ') AS tags
GROUP_CONCAT(th.theme_name, ', ') AS theme
FROM groups g
JOIN tags_map tg ON g.group_id = tm.group_id
JOIN tags t ON t.tag_id = tm.tag_id
JOIN themes_map thm ON g.group_id = thm.group_id
JOIN themes the ON th.theme_id = thm.theme_id
GROUP BY g.group_id, g.group_name
A pretty simple method uses correlated subqueries:
select b.*,
(select group_concat(t.tag_name)
from tag_mapping tm join
tags t
on tm.tag_id = t.tag_id
where tm.group_id = b.group_id
) as tags,
(select group_concat(t.theme_name)
from theme_mapping tm join
themes t
on tm.theme_id = t.theme_id
where tm.group_id = b.group_id
) as themes
from base b;

Merge columns on two left joins

I have 3 tables as shown:
Video
+----+--------+-----------+
| id | name | videoSize |
+----+--------+-----------+
| 1 | video1 | 1MB |
| 2 | video2 | 2MB |
| 3 | video3 | 3MB |
+----+--------+-----------+
Survey
+----+---------+-----------+
| id | name | questions |
+----+---------+-----------+
| 1 | survey1 | 1 |
| 2 | survey2 | 2 |
| 3 | survey3 | 3 |
+----+---------+-----------+
Sequence
+----+---------+-----------+----------+
| id | videoId | surveyId | sequence |
+----+---------+-----------+----------+
| 1 | null | 1 | 1 |
| 2 | 2 | null | 2 |
| 3 | null | 3 | 3 |
+----+---------+-----------+----------+
I would like to query Sequence and join on both of video and survey tables and merge common columns without specifying the column names (in this case name) like this:
Query Result:
+----+---------+-----------+----------+---------+-----------+-----------+
| id | videoId | surveyId | sequence | name | videoSize | questions |
+----+---------+-----------+----------+---------+-----------+-----------+
| 1 | null | 1 | 1 | survey1 | null | 1 |
| 2 | 2 | null | 2 | video2 | 2MB | null |
| 3 | null | 3 | 3 | survey3 | null | 3 |
+----+---------+-----------+----------+---------+-----------+-----------+
Is this possible?
BTW the below sql doesn't work as it doesn't merge on the name field:
SELECT * FROM "Sequence"
LEFT JOIN "Survey" ON "Survey"."id" = "Sequence"."surveyId"
LEFT JOIN "Video" ON "Video"."id" = "Sequence"."videoId"
This query will show what you want:
select
s.*,
coalesce(y.name, v.name) as name, -- picks the right column
v.videoSize,
y.questions
from sequence s
left join survey y on y.id = s.surveyId
left join video v on v.id = s.videoId
However, the SQL standard requires you to name the columns you want. The only exception being * as shown above.

sql inner join where and

tbl user
+--------+--------+
| UserID | Name |
+--------+--------+
| 1 | John |
| 2 | Anny |
| 3 | Andy |
| 4 | Tom |
+--------+--------+
tbl rol
+--------+--------+
| RolID | Name |
+--------+--------+
| 1 | Manager|
| 2 | Lead |
| 3 | Tester |
| 4 | Develop|
+--------+--------+
tbl user_rol
+--------+--------+
| UserID | RolID |
+--------+--------+
| 1 | 1 |
| 1 | 2 |
| 1 | 4 |
| 2 | 1 |
| 3 | 3 |
+--------+--------+
I have a List of parameters 1,2,4
SELECT u.UserID,Name
FROM [user] u
WHERE u.UserID in(
SELECT ur2.UserID
FROM user_rol ur2
WHERE ur2.ROlID IN(1,2,4)
GROUP BY ur2.UserID
HAVING COUNT(DISTINCT ur2.RolID)=3
)
The response will be:
+--------+--------+
| UserID | Name |
+--------+--------+
| 1 | John |
but if my parameters will be 1,2,3,4 will not work, but i need the response to be like previous
The problem is in count I did not know how much exists in the intermediate table user_rol.
It's hard to tell from you question, but it seems to me are looking to output any user that has more than one role listed in the user_rol table. Here is how you would do that:
SELECT user.userid, user.name
FROM user
INNER JOIN user_rol
ON user.userid = user_rol.userid
GROUP BY user.userid, user.name
HAVING COUNT(*) > 1
Tested here: http://sqlfiddle.com/#!9/35288d