SQL - Updating rows in table based on multiple conditions - sql

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

Related

Return Duplicate emails along with User Ids that are different

I'm running into an issue with a duplicate query and I hope you guys can help.
Essentially what I want to do is find and list of the duplicate emails associated with different userids
My query is:
select UserId, acitveid, email, userstatusid
from (select u.UserId, u.acitveid, cd.email, u.userstatusid,
count(*)over (partition by cd.email) as cnt
from ContactDetails cd
join UserContactDetails ucd on ucd.ContactDetailsId = cd.ContactDetailsId
join dbo.[User] u on u.UserId = ucd.UserId ) ua
where cnt >1
The issue I have with the above query is that it is returning the same userids for some of the results so it looks like:
Userid AcitveId email UserStatusid
123 1 abc#123.com 1
123 1 abc#123.com 1
135 1 efg#123.com 1
142 1 efg#123.com 1
The results Im looking for are simply:
Userid AcitveId email UserStatusid
135 1 efg#123.com 1
142 1 efg#123.com 1
WITH base AS (
SELECT DISTINCT u.UserId
,u.acitveid
,cd.email
,u.userstatusid
,
FROM ContactDetails cd
JOIN UserContactDetails ucd ON ucd.ContactDetailsId = cd.ContactDetailsId
JOIN dbo.[User] u ON u.UserId = ucd.UserId
)
,duplicate_emails AS (
SELECT email
,count(userId) AS cnt
FROM base
GROUP BY 1
HAVING count(userId) > 1
)
SELECT b.*
FROM base b
JOIN duplicate_emails de ON b.email = de.email
A self join across Email = email and id <> id would work fine here. That said, your request and lack of sample data means that we are largely guessing based off the query and sample output you have provided. The below should get you pretty close and, if you update your OP, I am sure we can get you exactly what you're after.
SELECT ActiveUser.UserID Active_UserID,
ActiveUser.ActiveID Active_ActiveID,
ContactDetails.email AS Email,
DuplicateUser.UserID AS Dup_UserID,
DuplicateUser.ActiveID As Dup_ActiveID
FROM ContactDetails INNER JOIN
ContactDetails AS Duplicate ON ContactDetails.email = Duplicate.email AND ContactDetails.UserID <> Duplicate.UserID INNER JOIN
UserContactDetails AS ActiveUserContactDetails ON ActiveUserContactDetails.ContactDetailsID = ContactDetails.ContactDetailsID INNER JOIN
dbo.[User] AS ActiveUser ON ActiveUser.UserID = ActiveUserContactDetails.UserID INNER JOIN
UserContactDetails AS DuplicateUserContactDetails ON DuplicateUserContactDetails.ContactDetailsID = Duplicate.ContactDetailsID INNER JOIN
dbo.[User] AS DuplicateUser ON DuplicateUser.UserID = UserContactDetails.UserID

Optimizing query for finding friends

I have a table which represents relations between pairs of people.
user_relating | user_related | relation_type
--------------------------------------------
1 | 2 | 1 --> means user 1 follows user 2
I need to find all friends for a specific user X.
Friendship means that two people follow each other.
So if user A follows users B, C. And users B, C follow A, then B, C are friends of A.
I've written this query :
SELECT users.* -- user's followers
FROM users
JOIN user_relations rel
ON users.id = rel.user_relating AND user_related = 2
INTERSECT
SELECT users.* -- who the user follows
FROM users
JOIN user_relations rel
ON users.id = rel.user_related AND user_relating = 2;
But I think it's inefficient.
Is there a more optimized way to get this done?
I've tried doing something like this :
SELECT DISTINCT f.*
FROM users f
JOIN user_relations u1
on f.id = u1.user_related
JOIN user_relations u2
on f.id = u2.user_relating
WHERE u1.user_related = 2
or u2.user_related = 2;
it seems much more efficient judging by the EXPLAIN ANALYZE (though I only have a really small table, like 10 rows so I'm not sure it's a good measurement).
But the problem here is that it returns the user in question too. Meaning, if I want friends of User B , then this query returns User B together with all his friends. Can I somehow exclude User B from the query result?
And, as stated formerly, I would be glad to receive some ideas for the most optimized and efficient way to do this kind of queries.
Probably the most efficient method is:
select ur.*
from user_relations ur
where ur.user_relating < ur.user_related and
ur.relation_type = 1 and
exists (select 1
from user_relations ur2
where ur2.user_relating = ur.user_related and
ur2.user_related = ur.user_relating and
ur2.relation_type = 1
);
And for performance, you want an index on user_relations(user_relating, user_related, relationship_type).
That said, this is similar to your version with the join, but does not require removing duplicates.
If you have lots of different relationship types, then an index on that column could also help.
EDIT:
If you have a particular user and want their friends:
select (case when user_relating = 2 then user_related else user_relating end) as user_friend
from user_relations ur
where ur.user_relating < ur.user_related and
ur.relation_type = 1 and
exists (select 1
from user_relations ur2
where ur2.user_relating = ur.user_related and
ur2.user_related = ur.user_relating and
ur2.relation_type = 1
) and
2 in (ur.user_relating, user_related)

SQL - Linked information

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
) ;

Compound SQL SELECT

I have the following two tables:
auth_user
id
username
is_active (boolean)
...
useprofile_userprofile
id
user_id
...
How would I find all auth_user objects where is_active=0 and there is no userprofile_userprofile object with that user_id? For example, an entry like this --
auth_user
id = 1
username = hello#gmail.com
is_active = 0
and userprofile_userprofile has no object where user_id = 1
SELECT *
FROM auth_user A
LEFT JOIN userprofile_userprofile B ON A.id=B.user_id
WHERE A.is_active = false and B.user_id IS NULL
when B.user_id is NULL that means it cannot find a row where user_id=1.
This assumes that the id in table userprofile_userprofile are all not NULL.
SELECT
*
FROM
auth_user
WHERE
auth_user.is_active=0
AND NOT EXISTS
(
SELECT
NULL
FROM
userprofile_userprofile
WHERE
userprofile_userprofile.user_id=auth_user.id
)
select * from auth_user au
where au.is_active = 0 and
not exists(select * from userprofile_userprofile uu where uu.user_id = au.user_id)
Apart from the other solutions you can also do it via LEFT JOIN
SELECT
*
FROM
auth_user au
LEFT JOIN useprofile_userprofile uu ON au.id = uu.user_id
WHERE uu.id IS NULL
AND au.is_active = 0
You are looking for Table Joins. Reference this tutorial:
http://www.tizag.com/mysqlTutorial/mysqljoins.php
To answer your question, you are looking for something along the lines of:
"SELECT auth_user.username,auth_user.is_active,useprofile_userprofile.user_id WHERE is_active = 0 AND user_id != 1"
select au.id,au.username,count(up.id) from auth_user au
left outer join useprofile_userprofile up
on au.id=up.user_id
where is_active = 1
group by au.id,au.username
having count(up.id)=0

Using a join query to retrieve absent data from second table

Let's consider these two tables:
TABLE(T_USER)
user_id
TABLE(T_MESSAGE)
msg_type (values = 0, 1, 2, 3)
answered (values = 0, 1)
user_id
As you can see, there can be 0 - n message for any user.
I am trying to retrieve, using a SQL query (on Oracle 10g), the list of all users for which no message of a certain type and not answered exists (i.e. where msgType = 1 and answered = 0 for example). This user could have others messages, if none of them fulfill these criteria, then my request should
What should this query look like?
SELECT *
FROM T_USER u
WHERE NOT EXISTS (SELECT NULL
FROM T_MESSAGE
WHERE user_id = u.user_id
AND msgType = 1
AND answered = 0)
Also want to notice that solution with NOT EXISTS will be more performant in oracle than NOT IN and LEFT JOIN.
select u.user_id
from T_USER u
left join T_MESSAGE m
on u.user_id = m.user_id
and m.msgType = 1
and m.answered = 0
where m.user_id is null
SELECT user_id FROM t_user t where not exists ( select 1 from t_message where user_id = t.user_id and ... )
How about
SELECT * FROM T_USER WHERE user_id NOT IN
(SELECT user_id FROM T_MESSAGE WHERE msgType = 1 AND answered = 0)