I have below table where column names are Item, Point and IsCorrect.
Item | Point | IsCorrect | Not actual column, just logic
-----+-------+-----------+--------------------
1 | 5 | 0 | 5 >= 6
1 | 8 | 0 | 8 >= 6
1 | 9 | 0 | 9 >= 6
1 | 6 | 1 | 6 >= 6
2 | 8 | 0 | 8 >= 7
2 | 7 | 1 | 7 >= 7
2 | 8 | 0 | 8 >= 7
2 | 9 | 0 | 9 >= 7
3 | 2 | 0 | 2 >= 9
3 | 5 | 0 | 5 >= 9
3 | 8 | 0 | 8 >= 9
3 | 9 | 1 | 9 >= 9
I want to first group by Item, get a Point value where IsCorrect = 1, compare it with other point values (as shown in 4th column). If all condition matched within a group, then select that item. I am expecting following result.
Item | Point | IsCorrect
-----+-------+----------
2 | 8 | 0
2 | 7 | 1
2 | 8 | 0
2 | 9 | 0
I want to use partition, not group by. Thank you so much for your help.
Window functions come to mind:
select t.*
from (select t.*,
max(case when iscorrect = 1 then point end) over (partition by item) as point_correct,
min(point) over (partition by item) as min_point
from t
) t
where min_point >= point_correct;
You could also do this with a subquery. Something like this:
select t.*
from t
where t.item in (select t2.item
from t t2
group by t2.item
having min(t2.point) >= min(case when t2.iscorrect then point end)
);
That is, for each item, compare the minimum point value to the "correct" point value.
Related
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
Here's some sample data from my table:
day_number daily_users_count
1 1
3 1
6 1
7 1
9 2
10 2
I need all day_number values, from 1 to max(day_number), and I want daily_users_count to be zero if it isn't mentioned in this table.
It should look something like this:
day_number daily_users_count
1 1
2 0
3 1
4 0
5 0
6 1
7 1
8 0
9 2
10 2
I think a left join with a table which has a number column with all integers from 1 to max(day_number) would work, if I put a default value for daily_users_count as 0.
What I don't get is how to create such a table where all integers within a certain range are present. Any alternate solutions or any ways to do this would be much appreciated.
You can do it with a recursive CTE which will return all the day_numbers including the missing ones and then a LEFT join to the table:
with cte as (
select min(day_number) day_number from tablename
union all
select day_number + 1 from cte
where day_number < (select max(day_number) from tablename)
)
select c.day_number,
coalesce(t.daily_users_count, 0) daily_users_count
from cte c left join tablename t
on t.day_number = c.day_number
See the demo.
Results:
| day_number | daily_users_count |
| ---------- | ----------------- |
| 1 | 1 |
| 2 | 0 |
| 3 | 1 |
| 4 | 0 |
| 5 | 0 |
| 6 | 1 |
| 7 | 1 |
| 8 | 0 |
| 9 | 2 |
| 10 | 2 |
I have a table [dbo].[UserImages] where user uploads their photos after every 6 day, total 18 records for user 3. 9 records of day 1 and 9 records of day 6. There are 4 columns In this table
[Id, UserId, Image, Day]
Id UserId Image Day
1 3 3_20200408_1.png 1
2 3 3_20200408_2.png 1
3 3 3_20200408_3.png 1
4 3 3_20200408_4.png 1
5 3 3_20200408_5.png 1
6 3 3_20200408_6.png 1
7 3 3_20200408_7.png 1
8 3 3_20200408_8.png 1
9 3 3_20200408_9.png 1
10 3 3_20200410_9.png 6
11 3 3_20200410_2.png 6
12 3 3_20200410_3.png 6
13 3 3_20200410_4.png 6
14 3 3_20200410_5.png 6
15 3 3_20200410_6.png 6
16 3 3_20200410_7.png 6
17 3 3_20200410_8.png 6
18 3 3_20200410_9.png 6
I need something like that
ImgCount UserId ImageDay1 ImageDay6
1 3 3_20200408_1.png 3_20200408_1.png
2 3 3_20200408_2.png 3_20200408_2.png
3 3 3_20200408_3.png 3_20200408_3.png
4 3 3_20200408_4.png 3_20200408_4.png
5 3 3_20200408_5.png 3_20200408_5.png
6 3 3_20200408_6.png 3_20200408_6.png
7 3 3_20200408_7.png 3_20200408_7.png
8 3 3_20200408_8.png 3_20200408_8.png
9 3 3_20200408_9.png 3_20200408_9.png
What should I do for this
You can use row_number() and aggregation:
select
imgCount,
userId,
max(case when day = 1 then image end) ImageDay1,
max(case when day = 6 then image end) ImageDay6
from (
select t.*, row_number() over(partition by userId, day order by image) imgCount
from mytable t
where day in (1, 6)
) t
group by userId, imgCount
order by ImgCount
Demo on DB Fiddle:
ImgCount | userId | ImageDay1 | ImageDay6
:------- | -----: | :--------------- | :---------------
1 | 3 | 3_20200408_1.png | 3_20200410_1.png
2 | 3 | 3_20200408_2.png | 3_20200410_2.png
3 | 3 | 3_20200408_3.png | 3_20200410_3.png
4 | 3 | 3_20200408_4.png | 3_20200410_4.png
5 | 3 | 3_20200408_5.png | 3_20200410_5.png
6 | 3 | 3_20200408_6.png | 3_20200410_6.png
7 | 3 | 3_20200408_7.png | 3_20200410_7.png
8 | 3 | 3_20200408_8.png | 3_20200410_8.png
9 | 3 | 3_20200408_9.png | 3_20200410_9.png
I wish SQL for SUM each column(IPO and UOR) in TOTAL in second last. And GRAND TOTAL(Sum IPO + UOR) in the last one. Thank you so much
No Code IPO UOR
----------------------
1 D173 1 0
2 D176 3 0
3 D184 1 1
4 D185B 1 0
5 D187 1 2
6 F042 3 0
7 ML004 12 3
8 TTPMC 2 0
9 Z00204 1 0
------------------
TOTAL (NOS) 25 6
-------------------------
GRAND TOTAL (NOS) 31
Here is my code, :
SELECT
SUM(CASE WHEN IPOType = 'IPO' THEN 1 ELSE 0 END) as IPO,
SUM(CASE WHEN IPOType = 'UOR' THEN 1 ELSE 0 END) as UOR
FROM IPO2018
GROUP BY OriProjNo
it can show like this
No Code IPO UOR
----------------------
1 D173 1 0
2 D176 3 0
3 D184 1 1
4 D185B 1 0
5 D187 1 2
6 F042 3 0
7 ML004 12 3
8 TTPMC 2 0
9 Z00204 1 0
------------------
Generally speaking, you want to leave totals and sub-totals to whatever tool you are presenting your data in, as they will be able to handle the formatting with significantly more ease. In addition, your desired output does not have the same number of columns (Grand Total row only has one numeric) so even if you did shoehorn this in to the same dataset, the column headings wouldn't make sense.
That said, you can return group totals via the with rollup statement. This will provide an additional row with the aggregate totals for the group. Where there is more than one group in your data, you will get a sub-total row for each group and a total row for the entire dataset:
declare #t table(c nvarchar(10),t nvarchar(3));
insert into #t values ('D173','IPO'),('D176','IPO'),('D176','IPO'),('D176','IPO'),('D184','IPO'),('D184','UOR'),('D185B','IPO'),('D187','IPO'),('D187','UOR'),('D187','UOR'),('F042','IPO'),('F042','IPO'),('F042','IPO'),('TTPMC','IPO'),('TTPMC','IPO'),('Z00204','IPO'),('ML004','UOR'),('ML004','UOR'),('ML004','UOR'),('ML004','IPO'),('ML004','IPO'),('ML004','IPO'),('ML004','IPO'),('ML004','IPO'),('ML004','IPO'),('ML004','IPO'),('ML004','IPO'),('ML004','IPO'),('ML004','IPO'),('ML004','IPO'),('ML004','IPO');
select row_number() over (order by grouping(c),c) as n
,case when grouping(c) = 1 then 'TOTAL (NOS)' else c end as c
,sum(case when t = 'IPO' then 1 else 0 end) as IPO
,sum(case when t = 'UOR' then 1 else 0 end) as UOR
from #t
group by c
with rollup
order by grouping(c)
,c;
Output:
+----+-------------+-----+-----+
| n | c | IPO | UOR |
+----+-------------+-----+-----+
| 1 | D173 | 1 | 0 |
| 2 | D176 | 3 | 0 |
| 3 | D184 | 1 | 1 |
| 4 | D185B | 1 | 0 |
| 5 | D187 | 1 | 2 |
| 6 | F042 | 3 | 0 |
| 7 | ML004 | 12 | 3 |
| 8 | TTPMC | 2 | 0 |
| 9 | Z00204 | 1 | 0 |
| 10 | TOTAL (NOS) | 25 | 6 |
+----+-------------+-----+-----+
I have a theoretical question, so I'm not interested in alternative solutions. Sorry.
Q: Is it possible to get the window running function values for all previous rows, except current?
For example:
with
t(i,x,y) as (
values
(1,1,1),(2,1,3),(3,1,2),
(4,2,4),(5,2,2),(6,2,8)
)
select
t.*,
sum(y) over (partition by x order by i) - y as sum,
max(y) over (partition by x order by i) as max,
count(*) filter (where y > 2) over (partition by x order by i) as cnt
from
t;
Actual result is
i | x | y | sum | max | cnt
---+---+---+-----+-----+-----
1 | 1 | 1 | 0 | 1 | 0
2 | 1 | 3 | 1 | 3 | 1
3 | 1 | 2 | 4 | 3 | 1
4 | 2 | 4 | 0 | 4 | 1
5 | 2 | 2 | 4 | 4 | 1
6 | 2 | 8 | 6 | 8 | 2
(6 rows)
I want to have max and cnt columns behavior like sum column, so, result should be:
i | x | y | sum | max | cnt
---+---+---+-----+-----+-----
1 | 1 | 1 | 0 | | 0
2 | 1 | 3 | 1 | 1 | 0
3 | 1 | 2 | 4 | 3 | 1
4 | 2 | 4 | 0 | | 0
5 | 2 | 2 | 4 | 4 | 1
6 | 2 | 8 | 6 | 4 | 1
(6 rows)
It can be achieved using simple subquery like
select t.*, lag(y,1) over (partition by x order by i) as yy from t
but is it possible using only window function syntax, without subqueries?
Yes, you can. This does the trick:
with
t(i,x,y) as (
values
(1,1,1),(2,1,3),(3,1,2),
(4,2,4),(5,2,2),(6,2,8)
)
select
t.*,
sum(y) over w as sum,
max(y) over w as max,
count(*) filter (where y > 2) over w as cnt
from t
window w as (partition by x order by i
rows between unbounded preceding and 1 preceding);
The frame_clause selects just those rows from the window frame that you are interested in.
Note that in the sum column you'll get null rather than 0 because of the frame clause: the first row in the frame has no row before it. You can coalesce() this away if needed.
SQLFiddle