SQL: select all depending nodes from a list of parent - sql

let's take a table called users like this
id
name
...
path
22
John
...
2/8/11/22/
23
Mark
...
1/3/9/15/21/23/
where the path rapresents the hierarchy parent-child.
Now, I have a list of "special users" which we can call them "board-users" and, for semplicity, let's say they have id 1 to 10.
I would like to select all the rows which derive from the board users (including board users rows itself) and I would like to add a column related to the board users parent; something like this:
id
name
...
path
board_parent_id
1
Stephany
...
1/
1
2
Karl
...
2/
2
...
...
...
...
...
83
Lucy
...
4/11/43/51/69/73/83/
4
I have tried something like
SELECT u1.id as board_parent_id, u2.*
FROM USERS AS u1
CROSS JOIN USERS AS u2
WHERE u1.id = '1'
AND u2.path LIKE '%1%'
UNION
SELECT u1.id as board_parent_id, u2.*
FROM USERS AS u1
CROSS JOIN USERS AS u2
WHERE u1.id = '2'
AND u2.path LIKE '%2%'
UNION
...
but honestly I believe this is a very stupid way to do this

First observation:
your current query should search for '%/1/%' not '%1%'
the latter will match '63/41/999/', when you don't want it to
But that also means that your path should start with '/'
or concat '/' to the start in your query
If you know the list of golden id's, then it's just a join?
WITH
golden(id) AS
(
VALUES
(1),(2),(3)...,(n)
)
SELECT
golden.id AS board_parent_id,
users.*
FROM
golden
INNER JOIN
users
ON CONCAT('/', user.path) LIKE CONCAT('%/', golden.id, '/%')

Thank to the comments and other answers I finally got my query done.
I post here an example
WITH BOARD AS(
SELECT * FROM users
WHERE ID IN ('id_1', 'id_2', 'id_3', 'id_4'...)
)
SELECT BOARD.id AS board_id,
u.*
FROM users AS u
INNER JOIN BOARD ON u.path LIKE CONCAT('%',BOARD.id.'%')
This way, it seems to work the way I wanted

Related

Get only users who do not have the id equal to a condition (SQL)

I have two tables, user:
id
full_name
1
Beatriz
2
Mauro
3
Jose
4
fran
approver :
id
subordinate_id
approver_id
1
1
2
2
3
4
I would like to bring up the names of people who are not registered in the subordinate_id column
I did the following query:
SELECT
U.full_name
FROM user AS U
INNER JOIN approver as A
ON U.id <> A.subordinate_id ;
enter image description here
and still users are coming in that are in the subordinate_id column of the approver table.
I would like to get the result only for user names that are not subordinate_id, can someone help with this?
I would like a result with only the users that are not subordinate_id
This is simple to accomplish an ansi-sql with a not exists semi join:
Select full_name
from user u
where not exists (
select * from approver a
where a.approver_id = u.id <-- or (subordinate_id, whichever it should be)
);

select a column from another table mulipletimes problem

User
USER_ID USERNAME
1 -
2 Chris
3 Dave
4 Vlad
Issue
Creator RESOLVER VERIFIER
2 3 4
2 3 1
3 1 1
expected output:
Creator RESOLVER VERIFIER
Chris Dave Vlad
Chris Dave -
Dave - -
current code I have:
SELECT creatorid.username, resolverid.username, verifierid.username
FROM issue
JOIN user creatorid ON issue.creator = creatorid.user_id
JOIN user resolverid ON issue.resolver = resolverid.user_id
JOIN user verifierid ON issue.verifier = verifierid.user_id
do i have to join the table 3 times to get the corresponding username of the user_id in issue table or is there is a simpler way of doing this? Asking as this is a simplified version of the tables, the User and Issue table contains a lot of other columns. Thanks
Because of the join, you will see each issue three times which is not what you want. You could use three scalar subqueries to get around that:
select i.id,
(select username from users u1 where u1.user_id = i.creator) as creator,
(select username from users u2 where u2.user_id = i.resolver) as resolver,
(select username from users u3 where u3.user_id = i.verifier) as verifier
from issue i;
This isn't going to be fast though.
Another option is to aggregate all user_id / username pairs into a JSON object, then use that in a sub-query:
select i.id, -- other columns from the issue table
u.names ->> i.creator::text as creator,
u.names ->> i.resolver::text as resolver,
u.names ->> i.verifier::text as verifier
from issue i
join lateral (
select jsonb_object_agg(user_id, username) as names
from users u
where u.user_id in (i.creator, i.resolver, i.verifier)
) u on true;
The traditional way to do this is:
select i.*, uc.username, ur.username, uv.username
from issue i left join
users uc
on uc.user_id = i.creator left join
users ur
on ur.user_id = i.resolver left join
users uv
on uv.user_id = i.verifier;

SQL query (Join without duplicates)

I have tables users and topics. Every user can have from 0 to several topics (one-to-many relationship).
How I can get only those users which have at least one topic?
I need all columns from users (without columns from topics) and without duplicates in table users. In last column I need number of topics.
UPDATED:
Should be like this:
SELECT user.*, count(topic.id)
FROM ad
LEFT JOIN topic ON user.id = topic.ad
GROUP BY user.id
HAVING count(topic.id) > 0;
but it takes 0 result. But it should not be 0.
Firstly you need to have your two tables, because you have left limited information about your table structure I will use an example to explain how this works, you should then be able to easily apply this to your own tables.
Firstly you need to have two tables (which you do)
Table "user"
id | name
1 | Joe Bloggs
2 | Eddy Ready
Table "topic"
topicid | userid | topic
1 | 1 | Breakfast
2 | 1 | Lunch
3 | 1 | Dinner
Now asking for a count against each user is done using the follwing;
SELECT user.name, count(topic.topicid)
FROM user
INNER JOIN topic ON user.id = topic.userid
GROUP BY user.name
If you use a left join, this will include records from the "user" table which does not have any rows in the "topic" table, however if you use an INNER JOIN this will ONLY include users who have a matching value in both tables.
I.e. because the user id "2" (which we use to join) is not listed in the topic table you will not get any results for this user.
Hope that helps!
use inner join and distinct
select distinct user_table.id
from user_table
inner join topics_table on topic_table.user_id = user_table.id
select u.id
, u.name
, count(b.topicName)
from user u
left join topic t on t.userid = u.id
group by u.id, u.name
You can select topic number per user and then join it with user data. Something like this:
with t as
(
select userid, count(*) as n
from topic
group by userid
)
SELECT user.*, t.n
FROM user
JOIN t ON user.id = t.userid

SQL - Select all skills

it's been a while since I used SQL so I'm asking sorry if it's too easy.
I have to select all the skills that a user has, so I have three tables.
User (id, name)
Skills (id, name)
User_skills (id_user, id_skill)
If the user1 has 2 skills; for example Hibernate (id 1) and Java (id 2)
and the user2 has 1 skill; Java (id 1)
Passing 1 and 2, I want to retrieve users that have both.
With the IN() function I get all the users that have at least one of the skills, but I want to filter them out!
Thanks to all in advance
If one skill can only be assigned exactly once to a user (i.e. (id_user, id_skill) is the PK for the user_skills table), then the following will do what you want:
SELECT id_user
FROM user_skills
WHERE id_skill IN (1,2)
GROUP BY id_user
HAVING count(*) = 2
Join to the association table user_skills twice, putting the skill ID in the on clause of each join:
select u.*
from user u
join user_skills us1 on us1.id_user = u.id and us1.id_skill = 1
join user_skills us2 on us2.id_user = u.id and us2.id_skill = 2
By using join (and not left join) this query requires the user have both skills
SELECT name FROM user as u
WHERE
EXISTS( SELECT 1 FROM User_skills WHERE id_user=u.id AND id_skill=1 )
AND EXISTS( SELECT 1 FROM User_skills WHERE id_user=u.id AND id_skill=2 )

Replace id with string in SQL view

I have two tables...
groupid membership_list managerid
-------------------------------------
0 /0//1//2/ 2
1 /2/ 2
userid username
------------------
0 ben
1 tom
2 dan
I'd like to display a table to for example the user 'ben' that is a list of the groups they are a member of that looks like this...
groupid membership_list managername
---------------------------------------
0 /0//1//2/ dan
.. so basically replacing 'manager_id' with the username for that id. I've been hacking away at this but I can't work it out - my SQL skills are clearly a bit lacking - how can I do this?
SELECT groupid, membership_list, managerid FROM blah WHERE membership_list LIKE '%/?/%'
... is about as far as I've got.
SELECT t1.groupid, t1.membership_list, t2.username
FROM table1 t1
INNER JOIN table2 t2 ON t1.managerid = t2.userid
That should do it. Or am I missing something here??
SELECT A.groupid, A.membership_list, B.managername FROM table1 A, table2 B WHERE A.managerid = B.userid and membership_list LIKE '%/?/%'
you need to break out membership_list column into a new table:
changed table: Groups
groupid
managerid
table users
userid
username
new table: UserGroups
groupid
userid
you can then do this:
SELECT
*
FROM Users u
INNER JOIN UserGroups ug On u.userid=ug.userid
INNER JOIN Groups g ON ug.groupid=g.groupid
WHERE u.Name='Ben'
to find all of Ben's groups.
If you don't want to modify your tables, you will need a split function that will convert the multiple values in membership_list into rows. You have not mentioned the actual database you are working on and a split function is dependent on knowing that.