Issue doing LEFT JOIN twice, (multiplying rows) - sql

Let's suppose I have 3 tables right now, One table with lessons, one table with the ratings of those lessons, and one table with the users of those lessons.
Lessons is a quite regular table, and the other 2 tables are relation tables like this:
TABLE LESSONS
ID | NAME | DESCRIPTION | CREATED BY | APPROVED BY | LEVEL | DATE CREATED | LAST EDIT
1 les1 desc1 10 12 1 12-12-2000 12-12-2000
2 les2 desc2 23 12 2 12-12-2000 12-12-2000
3 les3 desc3 12 12 3 12-12-2000 12-12-2000
TABLE RATINGS
ID | LESSON | USER | RATING | COMMENT
1 1 60 5 very good
2 2 30 4 nice
3 2 62 4 my comment
4 3 65 3 nice
5 3 78 5 very good
6 1 26 1 very bad
6 1 45 3 other comment
TABLE LESSONSXUSERS
ID | LESSON | USER | STATUS
1 1 60 2
2 1 26 2
2 1 45 2
3 2 30 2
4 2 62 2
5 3 65 2
6 3 78 2
7 1 22 1
8 1 19 1
And I'm trying to generate a view that shows me only approved lessons, with some info extracted from the other two tables:
CREATE OR REPLACE VIEW `skn_approved_lessons` AS
select
`l`.`id_skn_lessons` AS `id_skn_lessons`,
`l`.`name` AS `name`,
`l`.`description` AS `description`,
`l`.`createdBy` AS `createdBy`,
`l`.`approvedBy` AS `approvedBy`,
`l`.`id_skn_lessonsLevels` AS `id_skn_lessonsLevels`,
`l`.`dateCreated` AS `dateCreated`,
`l`.`lastEdit` AS `lastEdit`,
AVG(`lr`.`rating`) AS `avgScore`,
COUNT(`lxu`.`id_skn_users`) AS `students`
from ((`skn_lessons` AS `l`
left join `skn_lessonsRatings` AS `lr` on `l`.`id_skn_lessons` = `lr`.`id_skn_lessons`) left join `skn_lessonsXusers` AS `lxu` on `lxu`.`id_skn_lessons` = `l`.`id_skn_lessons`)
where ((`l`.`approvedBy` is not null) and
(`l`.`approvedBy` <> `l`.`createdBy`))
group by `l`.`id_skn_lessons`;
It's "kinda" working, since it shows me what I want, but the amount of users is wrong. I know why this happens, but I don't know how to fix it.
Issue is, I should get this:
VIEW APPROVED
ID | NAME | DESCRIPTION | CREATED BY | APPROVED BY | LEVEL | DATE CREATED | LAST EDIT | AVG RATING | STUDENTS
1 les1 desc1 10 12 1 12-12-2000 12-12-2000 3 4
2 les2 desc2 23 12 2 12-12-2000 12-12-2000 4 2
3 les3 desc3 12 12 3 12-12-2000 12-12-2000 4 2
But I'm getting this:
VIEW APPROVED
ID | NAME | DESCRIPTION | CREATED BY | APPROVED BY | LEVEL | DATE CREATED | LAST EDIT | AVG RATING | STUDENTS
1 les1 desc1 10 12 1 12-12-2000 12-12-2000 3 15
2 les2 desc2 23 12 2 12-12-2000 12-12-2000 4 4
3 les3 desc3 12 12 3 12-12-2000 12-12-2000 4 4
Notice users column is wrong, what I'm really getting is the product of usersxamountOfRatings. It's making the query with each registry of user by each registry of rating, so I'll always get the usersxratings as the amount of users, which is NOT what I want.
I don't quite understand how to do the second join after grouping by lesson.id, that I guess will solve the issue.
Thanks in advance.

I believe all you need to do is to add the DISTINCT argument to the COUNT aggregate function as follows:
COUNT(DISTINCT lxu.id_skn_users)
Adding the DISTINCT argument will return the number of unique nonnull values from that column.
I see Barmar already put that into the comments above.

Related

Select all the records in the first table that match each of the records in the second

I'm working with an Access database and have two tables:
ID_1
Number
Some other data
1
1
Data
2
2
Data
3
3
Data
4
4
Data
5
3
Data
6
1
Data
7
2
Data
8
3
Data
9
1
Data
10
1
Data
11
2
Data
12
3
Data
13
4
Data
14
1
Data
15
2
Data
16
3
Data
17
4
Data
18
3
Data
19
3
Data
ID_2
Number
Some other data
1
3
Data
2
1
Data
3
2
Data
4
3
Data
5
2
Data
As you see, both tables have duplicate data. I need a query that would select all the records in the first table that match each of the records in the second, they are related by Number field. It's also necessary that these records aren't repeated (that is, that the query doesn't repeat values when selecting). For the given example I should get this result:
ID
ID_1
Number
Some other data
1
3
3
Data
2
5
3
Data
3
8
3
Data
4
12
3
Data
5
16
3
Data
6
18
3
Data
7
19
3
Data
8
1
1
Data
9
6
1
Data
10
9
1
Data
11
10
1
Data
12
14
1
Data
13
2
2
Data
14
7
2
Data
15
11
2
Data
16
15
2
Data
I was thinking that maybe I could use Join, but I still don't know how; tried Where, but also didn't find a use for it. Could you please help me with that?
I don't see where you're generating your output ID field from - or where you're picking your Data field from so here's the best guess.
SELECT Table1.ID_1, Table1.Number, Table1.[Some other data]
FROM Table1
WHERE (Table1.Number In (SELECT Number From Table2))
ORDER BY Table1.Number, Table1.ID_1;
Looks like this:
MySql DB data structure
create table tbl1(ID_1 serial, Number int);
create table tbl2(ID_2 serial, Number int);
insert into tbl1(Number) values (1),(2),(3),(4),(3),(1),(2),(3),(1),(1),(2),(3),(4),(1),(2),(3),(4),(3),(3);
insert into tbl2(Number) values (3),(1),(2),(3),(2);
query (with s), needed to remove duplicates
the window function count(tbl1.Number) OVER(PARTITION BY Number) sorts the result for us by the count of matched numbers
the #rownum variable is needed to count rows
with s as (select distinct Number from tbl2),
f as (select ID_1,tbl1.Number from tbl1 left join s on
(tbl1.Number=s.Number) where s.Number is not null order by
count(tbl1.Number) OVER(PARTITION BY Number) desc)
select #rownum := #rownum + 1 AS ID,ID_1,Number from f, (SELECT #rownum := 0) r;
results
+------+------+--------+
| ID | ID_1 | Number |
+------+------+--------+
| 1 | 3 | 3 |
| 2 | 5 | 3 |
| 3 | 8 | 3 |
| 4 | 12 | 3 |
| 5 | 16 | 3 |
| 6 | 18 | 3 |
| 7 | 19 | 3 |
| 8 | 1 | 1 |
| 9 | 6 | 1 |
| 10 | 9 | 1 |
| 11 | 10 | 1 |
| 12 | 14 | 1 |
| 13 | 2 | 2 |
| 14 | 7 | 2 |
| 15 | 11 | 2 |
| 16 | 15 | 2 |
+------+------+--------+

full outer join in redshift

I have 2 tables A and B with columns, containing some details of students (all columns are integer):
A:
st_id,
st_subject_id,
B:
st_id,
st_subject_id,
st_count1,
st_count2
st_id means student id, st_subject_id is subject id.
For student id 15, there are following entries:
A:
15 | 1
15 | 2
15 | 3
B:
15 | 1 | 31 | 11
15 | 2 | 30 | 14
15 | 4 | 21 | 6
15 | 5 | 26 | 9
3 subjects in table A and 4 subjects(2 matching with table A and 2 extra) in table B.
I want to display the final result as:
15 | 1 | 31 | 11
15 | 2 | 30 | 14
15 | 3 | null | null
15 | 4 | 21 | 6
15 | 5 | 26 | 9
Can this be done using full outer join in SQL, or by another method?
I think something like this would suffice, but I can't test right now.
Coalesce means that the first non-null value will be selected from both tables.
select
coalesce(A.st_id, B.st_id) st_id,
coalesce(A.st_subject_id, B.st_subject_id) st_subject_id,
B.st_count1,
B.st_count2
from A
full outer join B
on A.st_id = B.st_id and A.st_subject_id = B.st_subject_id

Sum and distinct in acces SQL

I already made a query that this was it result :
7 | 3
8 | 4
8 | 2
8 | 1
10 | 3
12 | 4
12 | 1
13 | 3
I need new query that take this result and return this :
7 | 3
8 | **7**
10 | 3
12 | **5**
13 | 3
In the left column I need that evry number will appears only once,
and in the right column sum the numbers according to the value in the left column as I showed before.
how to do it?
SELECT leftField, SUM(rigthField) as rigthField
FROM YourResult
GROUP BY leftField

aggregate values in SQL columns

I am SQL beginner and I have the following question:
I have the following data (article sales by week)
COUNT WEEK ART
4 1 A
9 1 B
5 2 A
4 2 B
6 3 A
5 3 B
7 4 A
2 4 B
I would like to have the following output
ODD_WEEK EVEN_WEEK ART
10 12 A
14 6 B
in other words, I would like to group the elements by a criteria in a column (ODD_WEEK) and by an other criteria in another column (EVEN_WEEK)
Is it possible in T-SQL?
Try this query
select
art,
sum(case when week%2=0 then count else 0 end) even,
sum(case when week%2<>0 then count else 0 end) odd
from
tbl
group by
art
FIDDLE
| ART | EVEN | ODD |
|-----|------|-----|
| A | 12 | 10 |
| B | 6 | 14 |

An alternative to self-join

I have a table that has a reference to itself like this:
Id Total Prev Session
1 | 10 | NULL | 1
2 | 15 | 1 | 1
3 | 11 | NULL | 2
4 | 29 | 2 | 1
5 | 19 | 3 | 2
6 | 47 | 4 | 1
And I need to get the differences for the specific sessions.
Like this for session 1:
1. 10 -- None to 10
2. 5 -- 10 to 15
3. 14 -- 15 to 29
4. 18 -- 29 to 47
To do this, I use:
SELECT F.Total - P.Total AS Difference
FROM Foo F LEFT OUTER JOIN
Foo P ON F.Prev = P.Id
WHERE Session = #Session
Which is extremely slow.
How can I retrieve these differences faster without altering the table?
You cannot. This is the fastest query possible, although it may become a lot faster if you add an index on Session, Prev and Id.