SQL - Linked information - sql

I have a database table, which a program is about to update. I have taken a snapshot of the table before the program ran and a snapshot afterwards. Here is a sample of the data:
Before update:
JoinReference User
1 User 1
1 User 2
1 User 3
2 User 4
2 User 5
2 User 6
After update:
JoinReference User
3 User 1
3 User 2
3 User 3
4 User 4
4 User 5
5 User 6
I am trying to find all the users that are not linked together after the update. For example, user 1; user 2 and user 3 are linked before and after the update (even though they have a different join reference). User 4 and five are still linked after the update. User 6 is not linked after the update. I am trying to return user 6 after the update. I have tried using derived tables but it has not worked.
This is for a very large database. The example I have given uses fields I have made up to explain the problem. The database structure for the example would be like this:
CREATE TABLE JoinedUsers (JoinReference int, User)

select UnpairedBefore.userid
from
(select main.*
from tableBefore main
left join tableBefore ref
on main.joinreference = ref.joinreference
and main.userid <> ref.userid
where ref.userid null) UnpairedBefore
inner join
(select main.*
from tableAfter main
left join tableAfter ref
on main.joinreference = ref.joinreference
and main.userid <> ref.userid
where ref.userid null) UnpairedAfter
on UnpairedBefore.userid = UnpairedAfter.userid
or
select [user]
from tblBefore
where JoinReference in
(
select JoinReference
from tblBefore
group by JoinReference
having COUNT(*) = 1
)
and [user] in
(
select [user]
from tblAfter
where JoinReference in
(
select JoinReference
from tblAfter
group by JoinReference
having COUNT(*) = 1
)
)

You want something along the lines of
SELECT *
FROM myTable
WHERE (row_was_updated)
AND JoinReference IN
(SELECT JoinReference
FROM myTable
GROUP BY JoinReference
HAVING COUNT(*) = 1)
You may or may not need WHERE (row_was_updated) and you'll have to fill in that logic yourself if you need it.
It's likely there would be a more efficient way of doing that, but without schema it's only possible to give general guidance.

SELECT b1.user AS user1
, b2.user AS user2
FROM BeforeTable AS b1 --- user1 and
JOIN BeforeTable AS b2 --- user2 were linked
ON b1.JoinReference = b2.JoinReference --- before the update
WHERE b1.user < b2.user
AND NOT EXISTS --- and are not linked
( SELECT *
FROM AfterTable AS a1 --- after the update
JOIN AfterTable AS a2
ON a1.JoinReference = a2.JoinReference
WHERE a1.user = b1.user
AND a2.user = b2.user
) ;

Related

Subquery SQL for link name and firstname

Hello I would like to retrieve the name and the first name in the user table thanks to the id contained in the message table (id_receive and id_send) in sql via a subquery
SELECT user.nom FROM user
WHERE user.id IN (
SELECT message.id_send, message.id_receive FROM message WHERE message.id=1
)
```
I would recommend using EXISTS, twice:
SELECT u.nom
FROM user u
WHERE EXISTS (SELECT 1 FROM message m WHERE m.id = 1 AND u.id = id_send) OR
EXISTS (SELECT 1 FROM message m WHERE m.id = 1 AND u.id = id_receive) ;
However, a JOIN might also be appropriate:
SELECT u.nom
FROM user u JOIN
message m
ON u.id IN (m.id_send, id_receive)
WHERE m.id = 1;
I suspect it isn't actually what you want but it looks like this is what you're trying to do:
SELECT user.nom FROM user
WHERE user.id IN (
SELECT message.id_send FROM message WHERE message.id=1
UNION ALL
SELECT message.id_receive FROM message WHERE message.id=1
)
The query that drives the IN should return a single column of values
Try and conceive that in works like this:
SELECT * FROM t WHERE c IN(
1
2
3
)
Not like this:
SELECT * FROM t WHERE c IN(
1 2 3
)
Nor like this:
SELECT * FROM t WHERE c IN(
1 2 3
4 5 6
)
It might help you reember that the query inside it must return a single column, but multiple rows, all of qhich are searched for a matching value c by IN
Small addition to your original query to make it working:
SELECT user.nom FROM user
WHERE user.id IN (
SELECT unnest(array[message.id_send, message.id_receive])
FROM message
WHERE message.id=1
)

SQL - Updating rows in table based on multiple conditions

I'm kinda newbie in SQL and I have to create a request to update multiple rows in table based on multiple conditions.
From this example:
id email organisationid principaluserid role
1 john#smith.com MULT null 100
2 john#smith.C-100.com C-100 1 25
3 john#doe.com MULT null 100
4 john#doe.C-101.com C-101 3 50
5 john#doe.C-102.com C-102 3 25
6 jessica#smith.com C-102 null 25
The goal is to update all the entries from the User table where organisationid equals 'MULT' and who have only 1 principaleuserid match.
From the example above, the first 2 entries match my conditions.
I need then to replace the id=2 email (john#smith.C-100.com) with the one from id=1 email (john#smith.com).
To do the job step by step, I tried to retrieve all the entries that match my condition with this request:
Edit from #The_impaler answer:
SELECT * FROM User a1 WHERE a1.organisationid = 'MULT' AND (
SELECT COUNT(*) FROM User a2 WHERE a2.principaluserid = a1.id
) = 1;
But i'm still bugging on the way to update the entries. Any help is appreciated!
If I understand correctly, an update should do the trick:
update user u
set u.email = um.email
from user um
where um.id = u.principaluserid and
um.organizationid = 'MULT' and
not exists (select 1
from user up2
where up2.principaluserid = u.principaluserid and
up2.id <> u.id
);
Based on #The_impaler advice, I did this query that seems to answer my need:
UPDATE user u1
SET organisationid = (SELECT u2.organisationid FROM user u2 WHERE u1.id = u2.principaluserid),
WHERE u1.organisationid = 'MULT' AND
(SELECT COUNT(*) FROM user u2 WHERE u2.principaluserid = u1.id) = 1;
You could use an update based on join
UPDATE user u1
SET u1.email = u2.email
FROM user u2
WHERE u2.organisationid = 'MULT'
AND u1.id = u2.principaluserid
and if you need only the value that have only a single principaluserid
the you could use
UPDATE user u1
SET u1.email = u2.email
FROM user u2
INNER JOIN
(
select principaluserid , count(*)
from user
group by principaluserid
having count(*) =1
) t2 ON t2.principaluserid = u2.principaluserid
AND u2.organisationid = 'MULT'
AND u1.id = u2.principaluserid

select distinct from join

My Tables look like this
Table 1 Table 2
Users Options
id name id user_id option
------- --- -------- --------
1 Donald 1 1 access1
2 John 2 1 access2
3 Bruce 3 1 access3
4 Paul 4 2 access1
5 Ronald 5 2 access3
6 Steve 6 3 access1
Now, i want to select join these to find a user which has only access1
If i do something like
select t1.id,t1.name,t2.id,t2.user_id,t2.option
from table1 t1, table2 t2
where t1.id=t2.user_id
and option='access1';
This does not give me unique results, as in the example i need only user_id=3 my data has has these in hundreds
I also tried something like
select user_id from table2 where option='access1'
and user_id not in (select user_id from table2 where option<>'access1')
There have been other unsuccessful attempts too but i am stuck here
You can do this using a EXISTS subquery (technically, a left semijoin):
SELECT id, name
FROM table1
WHERE EXISTS(
SELECT * FROM table2
WHERE table1.id = table2.user_id
AND table2.option = 'access1'
)
If you want only users that have access1 and not any other access, add NOT EXISTS (a left anti-semi-join; there's a term to impress your colleagues!):
AND NOT EXISTS (
SELECT * FROM table2
WHERE table1.id = table2.user_id
AND table2.option <> 'access1'
)
bool_and makes it simple
with users (id,name) as ( values
(1,'donald'),
(2,'john'),
(3,'bruce'),
(4,'paul'),
(5,'ronald'),
(6,'steve')
), options (id,user_id,option) as ( values
(1,1,'access1'),
(2,1,'access2'),
(3,1,'access3'),
(4,2,'access1'),
(5,2,'access3'),
(6,3,'access1')
)
select u.id, u.name
from
users u
inner join
options o on o.user_id = u.id
group by 1, 2
having bool_and(o.option = 'access1')
;
id | name
----+-------
3 | bruce
If you want the user that has only access1, I would use aggregation:
select user_id
from table2
group by user_id
having min(option) = max(option) and min(option) = 'access1';
WITH users(id,name) AS ( VALUES
(1,'Donald'),
(2,'John'),
(3,'Bruce'),
(4,'Paul'),
(5,'Ronald'),
(6,'Steve')
), options(id,user_id,option) AS ( VALUES
(1,1,'access1'),
(2,1,'access2'),
(3,1,'access3'),
(4,2,'access1'),
(5,2,'access3'),
(6,3,'access1')
), user_access_count AS (
SELECT op.user_id,count(op.option) AS access_count
FROM options op
WHERE EXISTS(
SELECT 1 FROM options
WHERE option = 'access1'
)
GROUP BY op.user_id
)
SELECT u.id,u.name
FROM users u
INNER JOIN user_access_count uac ON uac.user_id = u.id
WHERE uac.access_count = 1;

Applying SQL WHERE Conditionally

TABLE A
-------
USER ACCESS RECORD_IDN
1 ALL NULL
2 PARTIAL 1
2 PARTIAL 2
3 PARTIAL 5
TABLE B
-------
NAME FOLDER_ACCESS R_IDN
FOLDER1 ALL
FOLDER2 ALL
FOLDER3 PARTIAL 5
FOLDER4 PARTIAL 1
FOLDER5 PARTIAL 2
FOLDER5 ALL
WHEN USER 1 Logs IN he has no restriction I should SHOW Folder1-5 .
WHEN USER 2 Logs IN he should NOT see FOLDER 3 (ALL AND 1,2 is fine for view)
WHEN USER 3 Logs IN he should see FOLDER 1, FOLDER 2 , FOLDER3, FOLDER5
The thing is I need to have a WHERE clause conditionally like this :
SELECT DISTINCT NAME * FROM TABLE B
PSEUDO CODE
IFF USER_LOGGED IN HAVING 'ALL' in TABLE A
DO NOTHING
ELSE IFF ITS PARTIAL
WHERE FOLDER_ACESS IS "ALL" OR R_IDN in (TABLEA.R_IDN FOR HIM) .
Is such construcuts possible in SQL ? Its more for an academic understanding of what is possible and what is not . I know to solve this query by breaking it seperately , however want to see if there are tricky solutions for this . And if its worth pursing single hit tricky solutions .
Targetted platforms are ORACLE and MSSQL . However any SQL platform solutions are welcome .
This would do it - you just need to supply the user you want the results for in the where clause. E.g. for user 2:
SELECT distinct a.user, b.name
FROM TABLEA a
INNER JOIN TABLEB b ON a.record_idn = b.r_idn
OR a.access = 'ALL'
OR b.folder_access = 'ALL'
WHERE a.user = 2
ORDER BY a.user, b.name
Is this what you're trying to achieve?
SELECT NAME
FROM B
WHERE EXISTS (
SELECT *
FROM A
WHERE A.USER = #userId
AND (A.ACCESS = 'ALL'
OR B.FOLDER_ACCESS = 'ALL'
OR (A.ACCESS = 'PARTIAL' AND A.RECORD_IDN = B.R_IDN)
)
)
GROUP BY NAME
For MS SQL there's an option to use CROSS APPLY
SELECT NAME
FROM B
CROSS APPLY (
SELECT 1
FROM A
WHERE A.USER = #userId
AND (A.ACCESS = 'ALL'
OR B.FOLDER_ACCESS = 'ALL'
OR (A.ACCESS = 'PARTIAL' AND A.RECORD_IDN = B.R_IDN)
)
)
GROUP BY NAME

SQL select column equivalence

I have a table that saves the possible states of other tables (entities).
But now I need to find equivalence of states between two entities.
The table structure is something like this
ID TableID StateValue StateDefinition StateDescription
================================================================
1 1 1 Created Just created
2 1 2 Dropped Just Dropped
3 2 1 Created Just Created
4 2 2 Aproved Passed the revision
5 2 3 Dropped Just dropped
I want to get equivalent (comparing text of state) which as a result get this:
TableID1 StateValue1 TableID2 StateValue2 StateDefinition
=============================================================================
1 1 2 1 Created
1 2 2 3 Dropped
My question is, how can it be done??
Do a self join on the table.
A general case might look like:
SELECT A.TableID as TableId1,
A.StateValue as StateValue1,
B.TableId as TableId2,
B.StateValue as StateValue2,
A.StateDefinition
FROM
Table A
INNER JOIN Table B
ON (A.TableId <> B.TableId and A.StateDefiniton = B.StateDefinition)
select t1.TableID as TableID1,
t1.StateValue as StateValue1,
t2.TableID as TableID2,
t2.StateValue as StateValue2,
t1.StateDefinition
from MyTable t1
inner join MyTable t2 on t1.TableID = 1 and t2.TableID = 2
where t1.StateValue = t2.StateValue
and t1.StateDefinition = t2.StateDefinition