postgres - finding unique users which have specific records - sql

I need some help completing the following query with postgres.
I have the following table for example: (this is the essence of what im trying to accomplish)
lets call it - table_user_flags
user_id flag
1 1
1 2
2 1
2 3
3 1
3 2
3 3
I need to find all users which have records with specific flags (under flag column, at least 1 record with each flag)
Example:
all users which have records with flags 1,3 (i.e 1 and 3) answer (user_id 2,3)
(but also would like to make it simple to extend so that i can request flags 1,2,3,4,5 etc)
I tried many things, unfortunately I was not able to articulate the situation well enough to find an answer on google.
My best guess (which is lame and possibly faulty) is (and provided flags array - $flag_arr)
select a.user_id
from table_user_flags a, table_user_flags b
where a.flag = $flag_arr[0] and b.flag = $flag_arr[1] and a.user_id=b.user_id
but this strategy is slow, and also will only work if i need 2 flags, when i need more than 2 flags I will need to write specific queries for each length of the flags_arr.
How can I solve this?
I will appreciate any help!

SELECT user_id
FROM table_user_flags
WHERE flag IN (1,2)
GROUP BY user_id
HAVING COUNT(DISTINCT flag)=2;
Fiddle

select user_id
from table_user_flags as t
inner join (select unnest($flag_arr) as flag) as c on c.flag = t.flag
group by t.user_id
having count(distinct t.flag) = array_length($flag_arr, 1);
or
select user_id
from table_user_flags as t
where t.flag = any($flag_arr)
group by t.user_id
having count(distinct t.flag) = array_length($flag_arr, 1);
sql fiddle demo
you can use count(*) if user_id, flag_id is unique inside the table

Related

SQL - How to get values from multiple tables without being ambiguous

Apologies if this question had been asked before (it probably did). I never used SQL before and the answers I've got only got me more confused.
I need to find out if an ID exists on different tables and get the total number from all tables.
Here is my query:
select * from public.ui1, public.ui2, public.ui3 where id = '123'
So if id 123 doesn't exist in ui1 and ui2 but does exist in ui3, I'd still like to get it. (I would obviously like to get it if it exists in the other tables)
I am currently getting an ambiguous error message as id exists in all tables but I am not sure how to construct this query in the appropriate manner. I tried join but failed miserably. Any help on how to reconstruct it and a stupid proof explanation would be highly appreciated!
EDIT: What I would finally like to find out is if id = 123 exists in any of the tables.
It's a bit unclear what the result is you expect. If you want the count then you can use a UNION ALL
select 'ui1' as source_table,
count(*) as num_rows
from public.ui1
where id = 123
union all
select 'ui2',
count(*)
from public.ui2
where id = 123
union all
select 'ui3',
count(*)
from public.ui3
where id = 123
If you only want to know if the id exists in at least one of the tables (so a true/false) result you can use:
select exists (select id from ui1 where id = 123
union all
select id from ui2 where id = 123
union all
select id from ui3 where id = 123)
What I would finally like to find out is if id = 123 exists in any of the tables.
The best way to do this is probably just using exists:
select v.id,
(exists (select 1 from public.ui1 t where t.id = v.id) or
exists (select 1 from public.ui2 t where t.id = v.id) or
exists (select 1 from public.ui3 t where t.id = v.id)
) as exists_flag
from (values (123)) v(id);
As written, this returns one row per id defined in values(), along with a flag of whether or not the id exists -- the question you are asking.
This can easily be tweaked if you want additional information, such as which tables the id exists in, or the number of times each appears.

Simple sql query to copy fields from another table based on the same key

I'm looking for the simplest query that will allow me to achieve copying some value from table A to table B while they both have a same key to identify what's needed.
I basically have (for example purposes only) 2 tables that represent users.
Table A contains USER_NAME, USER_ID, JOINED_DATE, RANK
Table B contains USER_ID, ACCOUNT_DETAILS, ADDRESS, RANK
I had a little bug with the RANK one, and i now see that in some cases the RANK is updated only in B, meaning in A its always NULL, but in some case its available at B.
I want to run a DB update that will:
check what USERS were created via table A joined last 30 days,
and then take that USER_ID and use this Key on table B
and check "if this ID in table B has a RANK,
copy that RANK to the same USER_ID in table A".
To clarify - All RANK in Table A is empty which is a bug, Some RANK in Table B has data and some is NULL, this is as designed and its OK. what i want is for both RANK columns to be example the same , in some time period, not generally from the beginning of time.
If you take a look at the example image below, you can see that it copied (based on the condition that its not null and joined date is above 2019:
123 - copied RANK since its valid
111 - copied RANK since its valid
121 - wasn't copied since RANK is empty and date is below 2020
141 - wasn't coped since RANK exists but date is below 2020
I hope that's understandable, please ask if there are more questions :)
Many thanks in advance good people!
pretty new and haven't touched SQL in quite a while.
this is Oracle SQL if it matters.
You can use the simple update as follows:
Update tablea a
Set a.rank = (select b.rank
From tableb b where b.user_id = a.user_id)
Where exists (select 1 from
Tableb b where b.user_id = a.user_id
And b.rank is not null)
And a.rank is null;
--And a.joined_date >= add_months(trunc(sysdate), -1);
Just use a join:
select a.*, b.rank
from a left join
b
on a.user_id = b.user_id and
a.joined_date >= sysdate - interval '30' day
If you want to update the rank in A:
update a
set rank = (select b.rank
from b
where a.user_id = b.user_id
)
where a.joined_date >= sysdate - interval '30' day;
The date comparisons would be slightly different if you want only dates in 2020.

Select records from joint tables

Got two tables:
tblJumper
JumperID
JumperName
and
tblWidthScored
ScoreID
fkJumperID
fkScoredWidth
Tables related by tblWidthScored.fkJumperID = tblJumper.JumperID
Table Contains following data:
tblJumper
1 Tom
2 Jerry
3 Bugs
tblWidthScored
1 1 5,72m
2 2 6,13m
3 1 5,80m
4 3 6,40m
5 2 6,30m
6 3 6,20m
What I'm trying to get is a list of each Jumpers personla best:
Tom 5,80m
Jerry 6,30m
Bugs 6,40m
Tried SELECT DISTINCT... in various forms but didn't succeed in any way.
Anyone couldl give a hint, please?
Thanks!
Although I think this is just a Join question and appropriate articles could be found on the web, I'll post one of the answers...
SELECT
J.JumperName,
S.Score
FROM tblJumper as J
INNER JOIN(
SELECT
fkJumperID,
Max(fkScoredWidth) as Score
FROM tblWidthScored
GROUP BY
fkJumperID
) as S on J.JumperID = S.fkJumperID
The hints you're asking for are in your actual question actually:
You're trying to find personal bests! That means some kind of aggregation: Maximum.
You're trying to find personal bests distinctly. Not "the best score amongst whole jumpers" but every person's personal best distinctly. That means some kind of distinction or grouping that you can partition the score datas people by people: Group By Clause
you can try this query. for this type of query, you have to use Group BY Clause with Sub-query.
SELECT JumperName, innertblWidthScored
FROM tblJumper INNER JOIN(SELECT fkJumperID,
Max(fkScoredWidth) as innertblWidthScored
FROM tblWidthScored
GROUP BY fkJumperID) WidthScored
on tblJumper.JumperID = WidthScored.fkJumperID
SQL FIDDLE DEMO
You could as well start with the JOIN, and do the GROUP BY afterwards. And if your sample output does actually suggest a sort order, you need an ORDER BY, too.
SELECT
JumperName
, MAX(fkScoredWidth) PersonalBest
FROM tblJumper
JOIN tblWidthScored
ON JumperID = fkJumperID
GROUP BY JumperName
ORDER BY MAX(fkScoredWidth) DESC;
SQL Fiddle
From result it seems that you want to retrieve jumper with its best score
try this -
select
a.jumperName,
b.bestScore
from tblJumper a, (
select
max(fkScoredWidth) as bestScore,
fkJumperID
from tblWidthScored
group by
fkJumperID
) b on b.fkJumperID = a.JumperID
order by
a.JumperID

SQL Server : Update Flag on Max Date on Foreign key

I'm trying to do this update but for some reason I cannot quite master SQL sub queries.
My table structure is as follows:
id fk date activeFlg
--- -- ------- ---------
1 1 04/10/11 0
2 1 02/05/99 0
3 2 09/10/11 0
4 3 11/28/11 0
5 3 12/25/98 0
Ideally I would like to set the activeFlg to 1 for all of the distinct foreign keys with the most recent date. For instance after running my query id 1,3 and 4 will have an active flag set to one.
The closest thing I came up with was a query returning all of the max dates for each distinct fk:
SELECT MAX(date)
FROM table
GROUP BY fk
But since I cant even come up with the subquery there is no way I can proceed :/
Can somebody please give me some insight on this. I'm trying to really learn more about sub queries so an explanation would be greatly appreciated.
Thank you!
You need to select the fk to and then restrict by that, so
SELECT fk,MAX(date)
FROM table
GROUP BY fk
To
With Ones2update AS
(
SELECT fk,MAX(date)
FROM table
GROUP BY fk
)
Update table
set Active=1
from table t
join Ones2update u ON t.fk = u.fk and t.date = u.date
also I would test first so do this query first
With Ones2update AS
(
SELECT fk,MAX(date)
FROM table
GROUP BY fk
)
selct fk, date, active
from table t
join Ones2update u ON t.fk = u.fk and t.date = u.date
to make sure you are getting what you expect and I did not make any typos.
Additional note: I use a join instead of a sub-query -- they are logically the same but I always find joins to be clearer (once I got used to using joins). Depending on the optimizer they can be faster.
This is the general idea. You can flesh out the details.
update t
set activeFlg = 1
from yourTable t
join (
select id, max([date] maxdate
from TheForeignKeyTable
group by [date]
) sq on t.fk = sq.id and t.[date] = maxdate

Query for exact match of users in a conversation in SQL Server

I have a conversation table, and a user conversation table.
CONVERSATION
Id, Subject, Type
USERCONVERSATION
Id, UserId, ConversationId
I need to do a SQL Query based on a list of UserIds. So, if I have three UserIds for the same ConversationId, I need to perform a query where if I provide the same three userIds, it will return the ConversationId where they match exactly.
Assuming the same user can't be in a UserConversation twice:
SELECT ConversationID
FROM UserConversation
GROUP BY ConversationID
HAVING
Count(UserID) = 3 -- this isn't necessary but might improve performance
AND Sum(CASE WHEN UserID IN (1, 2, 3) THEN 1 ELSE 0 END) = 3
This also works:
SELECT ConversationID
FROM
UserConversation UC
LEFT JOIN (
SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3
) U (UserID) ON UC.UserID = U.UserID
GROUP BY ConversationID
HAVING
Count(U.UserID) = 3
AND Count(UC.UserID) = 3
If you find that performance is poor with either of these queries then a two-step method could help: First find all conversations containing at least the desired parties, then from that set exclude those that contain any others. Indexes of course will make a big difference.
Getting rid of the ID column from UserConversation will improve performance by getting more rows per page, thus more data per read (about 50% more!). If your Id column is not only the PK but also the clustered index, then immediately go change the clustered index to ConversationId, UserId (or vice versa, depending on the most common usage)!
If you need help with performance post a comment and I'll try to help you.
P.S. Here's another wild idea but it may not perform as well (though things can surprise you sometimes):
SELECT
Coalesce(C.ConversationID, UC.ConversationID) ConversationID
-- Or could be Min(C.ConversationID)
FROM
Conversation C
CROSS JOIN (
SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3
) U (UserID)
FULL JOIN UserConversation UC
ON C.ConversationID = UC.ConversationID
AND U.UserID = UC.UserID
GROUP BY Coalesce(C.ConversationID, UC.ConversationID)
HAVING Count(*) = Count(U.UserID)
My solution was wrong, unfortunately...
I strongly suggest to use one of Erik's solutions...
Regards