Sum and distinct in acces SQL - 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

Related

How to get columns when using buckets (width_bucket)

I would like to know which row were moved to a bucket.
SELECT
width_bucket(s.score, sl.mins, sl.maxs, 9) as buckets,
COUNT(*)
FROM scores s
CROSS JOIN scores_limits sl
GROUP BY 1
ORDER BY 1;
My actual return:
buckets | count
---------+-------
1 | 182
2 | 37
3 | 46
4 | 15
5 | 29
7 | 18
8 | 22
10 | 11
| 20
What I expect to return:
SELECT buckets FROM buckets_table [...] WHERE scores.id = 1;
How can I get, for example, the column 'id' of table scores?
I believe you can include the id in an array with array_agg. If I recreate your case with
create table test (id serial, score int);
insert into test(score) values (10),(9),(5),(4),(10),(2),(5),(7),(8),(10);
The data is
id | score
----+-------
1 | 10
2 | 9
3 | 5
4 | 4
5 | 10
6 | 2
7 | 5
8 | 7
9 | 8
10 | 10
(10 rows)
Using the following and aggregating the id with array_agg
SELECT
width_bucket(score, 0, 10, 11) as buckets,
COUNT(*) nr_ids,
array_agg(id) agg_ids
FROM test s
GROUP BY 1
ORDER BY 1;
You get
buckets | nr_ids | agg_ids
---------+--------+----------
3 | 1 | {6}
5 | 1 | {4}
6 | 2 | {3,7}
8 | 1 | {8}
9 | 1 | {9}
10 | 1 | {2}
12 | 3 | {1,5,10}

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 |
+------+------+--------+

RANK data by value in the column

I'd like to divide the data into separate groups (chunks) based on the value in the column. If the value increase above certain threshold, the value in the "group" should increase by 1.
This would be easy to achieve in MySQL, by doing CASE WHEN #val > 30 THEN #row_no + 1 ELSE #row_no END however I am using Amazon Redshift where this is not allowed.
Sample fiddle: http://sqlfiddle.com/#!15/00b3aa/6
Suggested output:
ID
Value
Group
1
11
1
2
11
1
3
22
1
4
11
1
5
35
2
6
11
2
7
11
2
8
11
2
9
66
3
10
11
3
A cumulative sum should do what you want:
SELECT *, sum((val>=30)::INTEGER) OVER (ORDER BY id BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) FROM mydata ORDER BY id;
id | val | sum
----+-----+-----
1 | 11 | 0
2 | 11 | 0
3 | 22 | 0
4 | 11 | 0
5 | 35 | 1
6 | 11 | 1
7 | 11 | 1
8 | 11 | 1
9 | 66 | 2
10 | 11 | 2

Issue doing LEFT JOIN twice, (multiplying rows)

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.

Can I order by multiple columns and somehow keep the ordering related between columns in MySQL?

I know the title doesn't explain my question very well (if someone can come up with a better title then please edit it). Here's what I want to do, say I have the following table:
id | a | b | c
------------------
1 | 3 | 3 | 3
2 | 20 | 40 | 30
3 | 40 | 30 | 10
4 | 30 | 10 | 15
5 | 10 | 15 | 6
6 | 15 | 6 | 20
This is slightly truncated version, I have a few more columns to sort by, but the principle behind the data & my question is the same.
What I would like is to get the data ordered in the following way:
The row with the highest value in col a
The row with the highest value in col b
The row with the highest value in col c
Followed by all remaining rows ordered by their value in col c
So, the result set would look like:
id | a | b | c
------------------
3 | 40 | 30 | 10
2 | 20 | 40 | 30
6 | 15 | 6 | 20
4 | 30 | 10 | 15
5 | 10 | 15 | 6
1 | 3 | 3 | 3
Doing a
SELECT id, a, b, c
FROM table
ORDER BY a DESC, b DESC, c DESC
Obviously gives me a ordered first, then b and finally c, so the following (which is not what I need):
id | a | b | c
------------------
3 | 40 | 30 | 10
4 | 30 | 10 | 15
2 | 20 | 40 | 30
6 | 15 | 6 | 20
5 | 10 | 15 | 6
1 | 3 | 3 | 3
I'm not familiar with the MySQL TSQL dialect but you would have to first SELECT the row with the highest 'A' value, perform a UNION ALL (i.e. no distinct via sorting) with the row with the highest 'B' value, perform a UNION ALL with the row with the highest 'C' value and then a UNION ALL with the remaining rows ordered by 'C' and excluding the 3 rows (by id) already selected.
I've just tested the following which appears to work (does involve 3 subqueries however):
SELECT id, a, b, c
FROM test
ORDER BY FIELD(a,(SELECT MAX(a) FROM test)) DESC,
FIELD(b,(SELECT MAX(b) FROM test)) DESC,
FIELD(c,(SELECT MAX(c) FROM test)) DESC,
c DESC