Postgres - Count cross more tables - sql

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"

Related

Select 4 rows at random with minimum values in sqlite

I have the following table:
addition
question answer box
1 + 1 2 0
1 + 2 3 2
1 + 3 4 1
1 + 4 5 2
1 + 5 6 3
1 + 6 7 1
I'm trying to select 4 rows with a minimum box value:
SELECT *, MIN(box) FROM {table} ORDER BY RANDOM() LIMIT 4;
However, it returns only one row.
Sounds like you want a cartesian product (CROSS JOIN) of two tables: the first table being what you presented above, and the second being the minimum value for column box.
Try this
SELECT * FROM {table}
CROSS JOIN
(SELECT MIN(box) from {table})
ORDER BY RANDOM() LIMIT 4;
Notice the subquery in the second half of the CROSS JOIN.

How do I select just mutual rows in 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)

Retrieve unique rows based on id

I have two tables:
Report
ReportId CreatedDate
1 2018-01-12
2 2018-02-12
3 2018-03-12
ReportSpecialty
SpecialtyId ReportId IsPrimarySpecialty
1 1 1
2 2 1
3 3 1
1 2 0
1 3 0
I am trying to write a query that will retrieve me the last 10 reports that were published. However, I need to get 1 report from each specialty. Assume there are 100 specialties, I can pass in as an argument any number of specialties, 10, 20, 5, 2, etc...
I'm trying to figure out a way where if I send it all specialties, it will get me the last 10 reports posted based on the last date created, but it won't give me 2 articles from same specialty. If I send it 10 specialties, then I will get 1 of each. If I send it 5, then I'll get 2 of each. If I send it 3 then I'll get 4 of 1 and 3 of other two.
I may need to write multiple queries for this, I'm trying to see if there is a way to do this on the SQL side of things? If there isn't, then how would I break down to multiple queries to get the result I want?
What I have tried is this, however I get multiple reports with same specialties:
SELECT TOP 10 r.ReportId, rs.SpecialtyId, r.CreatedDate
FROM Report r
INNER JOIN ReportSpecialty rs ON r.ReportId = rs.ReportId AND rs.IsPrimarySpecialty = 1
GROUP BY rs.SpecialtyId, r.AceReportid, r.CreatedDate
ORDER BY r.CreatedDate DESC
with cte as (
SELECT R.ReportId, R.CreatedDate, RS.SpecialtyId,
ROW_NUMBER() OVER (PARTITION BY RS.SpecialtyId
ORDER BY R.CreatedDate DESC) as rn
FROM Report R
JOIN ReportSpecialty RS
ON R.ReportId = RS.ReportId
AND RS.IsPrimarySpecialty = 1
WHERE RS.SpecialtyId IN ( .... ids ... )
)
SELECT TOP 10 *
FROM cte
ORDER BY rn, CreatedDate DESC
row_number will create a id for each speciality, so if you pass 3 speciality you will get something like this.
rn speciality_id
1 1
1 2
1 3
2 1
2 2
2 3
3 1
3 2
3 3

Select Max Access Query - row max for column with WHERE criteria

Microsoft Access 2010
I'm trying to get the Max visit number of a user within a sequence of visits less than or equal to 13 months apart.
So the last visit that was within a sequence of visits less than or equal to 13 months apart. I want to exclude those who only occasionally use it (ie. over 13 months apart)
I'm using this to run my query:
SELECT ID, AppointmentDate, Visit
FROM tblVisitQuestions t1
WHERE t1.Visit =
(SELECT Max(t2.Visit)
FROM tblVisitQuestions t2
WHERE t2.ID=t1.ID AND (DateDiff("m",[t1].[AppointmentDate],[t2].[AppointmentDate]) <= 13)
GROUP BY t2.ID)
Its working except that its returning multiple values for the same ID number.
Not sure if its an error within my data or my query.
Ex: example data in table
CR AppointmentDate Visit
1 15-Apr-05 0
1 15-Jul-05 1
1 16-May-06 2
1 06-Jun-06 3
1 19-Dec-06 4
1 11-Nov-11 5
1 31-Jan-12 6
2 08-Jun-04 0
2 17-Dec-04 1
2 05-Jul-05 2
2 06-Dec-05 3
2 06-Feb-09 4
2 19-Apr-11 5
what I would like after the query (not what I'm getting now)
CR AppointmentDate Visit
1 19-Dec-06 4
2 06-Dec-05 3
what I am actually getting:
CR AppointmentDate Visit
1 19-Dec-06 4
1 31-Jan-12 6
2 06-Feb-09 4
2 19-Apr-11 5
I need a way to select the min value of the max values I am getting
Any help would be greatly appreciated!
Thanks
For each visit, you can get the maximum visit number in the 13 months afterwards by doing:
SELECT ID, AppointmentDate, Visit,
(SELECT Max(t2.Visit)
FROM tblVisitQuestions as t2
WHERE t2.ID = t1.ID AND (DateDiff("m",[t1].[AppointmentDate],[t2].[AppointmentDate]) <= 13)
) as MaxVisit
FROM tblVisitQuestions as t1;
If you want this only where the visit number is one, then add a where clause:
WHERE t1.Visit = 1;

update table with join but with summed values

I've got 2 tables:
-Card
id
points
-History
CardId
points
Now I would like to perform update query which subtracts points in Card table based on points in History with the same cardId
for example I have rows:
-card
1 10
2 30
-History
1 5
1 3
2 10
2 9
and as a result I should have in Card table rows:
-card
1 2
2 11
what is the best way to do that?
This will do it.
update card
set points = points - total
from card
inner join (select cardid, sum(points) as total from history) v
on card.id = v.cardid
But I agree with other comments questioning your database structures