How do I select just mutual rows in SQL? - sql

I have a table with a source_id and destination_id and a message and I want to group messages together. There can only be one message between a given source_id and destination_id, but I only want rows that have a mutual response for a given ID (say id 1). In the following examples, I want rows #1, #2, #4 and #5 because there is a mutual response between id 1 and 2 and between id 1 and id 4 (id 1 sent a message to id 2 and id 2 sent a message to id 1, similarly, id 1 sent a message to id 4 and id 4 sent a message to id 1). I don't want id 3 because it has no mutual response.
How do I select this in SQL? (I'm using PostgreSQL btw)
Example:
table messages
# source_id destination_id message
1 1 2 hello
2 2 1 hi
3 1 3 bye
4 1 4 thanks
5 4 1 okay
6 3 5 blablabla
7 5 3 hooray
Preferably, I want my select to return these 4 rows:
1 1 2 hello
2 2 1 hi
3 1 4 thanks
4 4 1 okay
Thanks in advance :)

You seem to be describing exists:
select m.*
from messages m
where exists (select 1
from messages m2
where m2.source_id = m.destination_id and
m2.source_id = m.destination_id
);
In your example, this would also return rows 6 and 7, because those seem to follow the rule you specified.
If you want 1 to be one of the ids, then include a filter for that:
select m.*
from messages m
where 1 in (m.source_id, m.destination_id) and
exists (select 1
from messages m2
where m2.source_id = m.destination_id and
m2.source_id = m.destination_id
);

Do a self join:
select m1.*
from messages m1
join messages m2
on m1.source_id = m2.destination_id
and m2.source_id = m1.destination_id
where 1 in (m1.source_id, m2.source_id)

Related

SQL sum group by column including rows of same table as dependents

I want to see how many messages are sent by users, so I have tables where I store the channel and another table where I store the authors and the actual data. An important part of this is that channels can be subchannels, meaning that they are children of another channel, but also have their own ID.
Table 1
Row ID
Guild ID
Channel ID
Parent Channel ID (foreign key on Table 1 Channel ID)
1
1
2
null
2
1
3
null
3
1
4
3
Table 2
Row ID
Parent Channel ID (foreign key on Table 1 Channel ID)
Author ID
Messages
1
2
1
6
2
3
2
54
3
4
1
3
I have a query that gets the sum of messages from Channel IDs that are not a child of any other channel ID:
SELECT sc."guild_id" guild, sc."channel_id" channel, sum(s."NUM") num
FROM statistic s
INNER JOIN statschannel sc on s.parent_channel_id = sc.channel_id
GROUP BY sc."guild_id", sc."channel_id";
I would like to change the query so that the SUM() of (GUILD ID 1, CHANNEL ID 3) includes the sum of (GUILD ID 1, CHANNEL ID 4), as channel with ID 4 is a child channel. I tried doing an inner join, show below, but it only returns the results for the channels with a parent ID.
SELECT sc."guild_id" guild, sc."channel_id" channel, sum(s."NUM") num
FROM statistic s
INNER JOIN statschannel sc on s.parent_channel_id = sc.channel_id
INNER JOIN statschannel sc2 on sc.parent_channel_id = sc2.channel_id
GROUP BY sc."guild_id", sc."channel_id";
I don't know what to do here. The data that I want returned should look like this:
Guild ID
Channel ID
Messages
1
2
6
1
3
57 ( 54 + 3)
1
4
3
I managed to make a solution that provided the results I wanted.
SELECT "statschannel"."guild_id" "guild_id",
"statschannel"."channel_id" "channel_id",
SUM("statistic2"."NUM") + COALESCE(SUM("statistic"."NUM"), 0) "num"
FROM "statschannel"
LEFT OUTER JOIN "statschannel" "statschannel__parent_channel"
ON "statschannel__parent_channel"."channel_id" =
"statschannel"."parent_channel_id"
LEFT OUTER JOIN "statistic" ON "statschannel__parent_channel"."channel_id" =
"statistic"."parent_channel_id"
LEFT OUTER JOIN "statistic" "statistic2" ON "statschannel".channel_id =
"statistic2".parent_channel_id
GROUP BY statschannel."guild_id", "statschannel"."channel_id"
This will provide the following results:
guild_id
channel_id
messages
1
2
6
1
3
57
1
4
3

Postgres - Count cross more tables

is there a way to do some calculation like this in SQL?
I have 3 tables
Message
ID
Text
1
First question round
2
Second question round
Message Slide
ID
MessageID
Question
1
1
How do you feel?
2
1
Where do you work?
3
2
Skiing or swimming?
Message audience
ID
MessageID
UserID
1
1
1
2
1
2
3
1
3
4
2
1
5
2
2
I need to know how many slides (questions) should I have answered.
The calculation should be: the sum of (each audience * message slide);
First message has 2 slides/question, so it is:
2 (questions) * 3 (users) = 6
Second message has 1 slide / question, so it is:
1 (question) * 2 (users) = 2
Result I'm looking for is 6 + 2 = 8;
Thank you very much
demo:db<>fiddle
Simply join all data sets and count the records?
SELECT
COUNT(*)
FROM message m
JOIN slide s ON s."MessageID" = m."ID"
JOIN audience a ON a."MessageID" = m."ID"
To count the record number for each message separately you need to group by the message ID, which gives exactly what you are expecting: 6 and 2:
SELECT
m."ID",
COUNT(*)
FROM message m
JOIN slide s ON s."MessageID" = m."ID"
JOIN audience a ON a."MessageID" = m."ID"
GROUP BY m."ID"

Get max value from a joined list paired with another column in DB2

I have the following tables:
Table I:
etu | nr |
1 2
2 2
2 3
2 1
3 4
3 9
Table A:
etu | rsp | nr
2 8 2
2 7 3
2 3 1
3 2 4
3 6 9
Now what I want to have as a result table is
etu | nr | rsp
2.. 3 7
3.. 9 6
So etu and nr are linked together and if multiple equal etu entries are available only the one with the highest nr is taken and the rsp value is added in the result table. in addition if more etu entries are available in the table I there are .. added to the etu value.
Explain: For the 3 9 6 row: The last row on table I is 3 9 so 3 is the number that is looked for and 9 is the highest number for the 3 rows. So we take that and add the rsp value for that ( 6 ) and we add that to the result table. For the 2 row it is the same 2 3 being the highest 2 row in table I.
I got something like:
select x.etu, x.rsp, y.nr from(
select i.etu etu, max(i.nr) maxnr, a.rsp from i left join a on
i.etu=a.etu and i.nr=a.nr group by etu)t
inner join a x on x.etu=t.etu and x.nr=t.nr inner join y on y.etu=t.etu
and y.nr=t.nr
or
select i.etu, max(i.nr) a.rsp from i left join a on i.etu=a.etu and
i.nr=a.nr grounp by
None even get me close to get the results that I want less add the .. after the etu when having the right result.
The system is DB10.5 Windows.
Thank you for all your help in advance.
Viking
I would use a CTE here like this:
with tmp as (
select i.etu, max(i.nr) as nt, count(*) as cnt
from i
group by i.etu)
select case
when tmp.cnt = 1 then char(a.etu)
else concat(rtrim(char(a.etu)), '..')
end as etu,
a.nr,
a.rsp
from tmp
left outer join a
on a.etu = tmp.etu
and a.nr = tmp.nr
The CTE provides the information necessary to join with a to get the correct response, and append the .. as necessary.

How to select id when same id has multiple rows but I am looking for id which are missing a particular value

I have this table my_table_c with the below values
SELECT * FROM my_table_c
ID GROUP_ID GROUP_VALUE
1 2 1
3 3 2
3 4 1
5 4 1
5 2 1
2 2 2
2 3 2
2 4 1
I am looking for this output where I get only the ID which do not have group_id 2. Additionally, I don't want to get the ID where group_id 2 is absent but other group ids are present.
If group_id 2 is absent, that's my target id.
So with the values shown in table above, I just expect ID = 3 to be returned as other ids 1, 5 and 2 each have rows where group_id = 2.
Can anyone please help with a query to fetch this result.
You could get all the id's that have group_id = 2 and use NOT IN
select *
from my_table_c
where id not in (select id from my_table_c where group_id = 2)
Another way but using NOT EXISTS
select *
from my_table_c mtcA
where not exists (select *
from my_table_c mtcB
where mtcA.id = mtcB.id and mtcB.group_ID = 2)

Matching two variables to create a new ID

I'm trying to create an SQL statement to match either an id number or a postcode and then assign a new id number
What I want to end up with is ‘newid’ that correctly recognizes that the first four records are the same person (even though the postcode for record 2 is different).
record id postcode newid
--------------------------
1 1 1 1
2 1 2 1
3 1 1 1
4 2 1 1
5 3 3 2
Any suggestions would be appreciated greatly.
Going based on your example:
SELECT RECORD,
(SELECT MIN (ID)
FROM users u2
WHERE users.id IN (u2.id, u2.postcode)
OR users.postcode in (u2.id, u2.postcode)
) AS newid
FROM users
This results with the following data:
RECORD NEWID
------------------
1 1
2 1
3 1
4 1
5 3
Here is the SQLFiddle