Escaping x number of rows in Select query - sql

I want rows from the entire table but I only want to select rows 1,5,10,15,20,25
Table
ID Col1 Col2……….
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
I want to select in my result set
Id Col1 Col2……….
1
5
10
15
20
Any suggestions would be appreciated.
Thank you

You can use:
where id = 1 or
id % 5 = 0
The % is the modulo operator. Some databases use a mod() function instead.
If id has gaps or other issues, you may want to do this with row_number():
select t.*
from (select t.*, row_number() over (order by id) as seqnum
from t
) t
where seqnum = 1 or seqnm % 5 = 0;

Related

SQL Ignoring duplicate values if ID difference larger than

Lets say I have a simple table:
ID value
1 15
2 30
3 **10**
4 **10**
5 16
6 20
7 **15**
8 **15**
9 40
10 70
11 **50**
12 **50**
13 19
14 11
15 3
My select should ignore consecutive double values. I know how to do that - I am using lead function
But this eliminates all consecutive duplicates.
SELECT [DetectorParameterValue]
FROM (
SELECT lead(DetectorParameterValue,1) over (partition by runid order by runtime) AS prev_DetectorParameterValue
FROM table_Detector
WHERE RunID = #run_id
) AS [InnerDetector]
WHERE (prev_DetectorParameterValue is null or or prev_DetectorParameterValue <> DetectorParameterValue
But it should ignore them only if ID diff is more than 5.
So my select should be
ID Value
1 15
2 30
3 10
5 16
6 20
7 15
8 15
9 40
10 70
11 50
13 19
14 11
15 3
ID 4 and 12 should be ignored but ID 8 should not because ID 8 - ID 4 is not > 5.
Is there a way to do this?
Thanks in advance.
This is a gaps and islands problem in disguise. One approach uses the difference in row numbers method.
WITH cte AS (
SELECT *, ROW_NUMBER() OVER (ORDER BY ID) rn1,
ROW_NUMBER() OVER (PARTITION BY value ORDER BY ID) rn2
FROM yourTable
),
cte2 AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY value, rn1 - rn2 ORDER BY ID) rn
FROM cte
)
SELECT ID, value
FROM cte2
WHERE rn = 1
ORDER BY ID;

Is it possible to use a aggregate function over partition by as a case condition in SQL?

Problem statement is to calculate median from a table that has two columns. One specifying a number and the other column specifying the frequency of the number.
For e.g.
Table "Numbers":
Num
Freq
1
3
2
3
This median needs to be found for the flattened array with values:
1,1,1,2,2,2
Query:
with ct1 as
(select num,frequency, sum(frequency) over(order by num) as sf from numbers o)
select case when count(num) over(order by num) = 1 then num
when count(num) over (order by num) > 1 then sum(num)/2 end median
from ct1 b where sf <= (select max(sf)/2 from ct1) or (sf-frequency) <= (select max(sf)/2 from ct1)
Is it not possible to use count(num) over(order by num) as the condition in the case statement?
Find the relevant row / 2 rows based of the accumulated frequencies, and take the average of num.
The example and Fiddle will also show you the
computations leading to the result.
If you already know that num is unique, rowid can be removed from the ORDER BY clauses
with
t1 as
(
select t.*
,nvl(sum(freq) over (order by num,rowid rows between unbounded preceding and 1 preceding),0) as freq_acc_sum_1
,sum(freq) over (order by num, rowid) as freq_acc_sum_2
,sum(freq) over () as freq_sum
from t
)
select t1.*
,case
when freq_sum/2 between freq_acc_sum_1 and freq_acc_sum_2
then 'V'
end as relevant_record
from t1
order by num, rowid
Fiddle
Example:
ID
NUM
FREQ
FREQ_ACC_SUM_1
FREQ_ACC_SUM_2
FREQ_SUM
RELEVANT_RECORD
7
8
1
0
1
18
5
10
1
1
2
18
1
29
3
2
5
18
6
31
1
5
6
18
3
33
2
6
8
18
4
41
1
8
9
18
V
9
49
2
9
11
18
V
2
52
1
11
12
18
8
56
3
12
15
18
10
92
3
15
18
18
MEDIAN
45
Fiddle for 1M records
You can find the one (or two) middle value(s) and then average:
SELECT AVG(num) AS median
FROM (
SELECT num,
freq,
SUM(freq) OVER (ORDER BY num) AS cum_freq,
(SUM(freq) OVER () + 1)/2 AS median_freq
FROM table_name
)
WHERE cum_freq - freq < median_freq
AND median_freq < cum_freq + 1
Or, expand the values using a LATERAL join to a hierarchical query and then use the MEDIAN function:
SELECT MEDIAN(num) AS median
FROM table_name t
CROSS JOIN LATERAL (
SELECT LEVEL
FROM DUAL
WHERE freq > 0
CONNECT BY LEVEL <= freq
)
Which, for the sample data:
CREATE TABLE table_name (Num, Freq) AS
SELECT 1, 3 FROM DUAL UNION ALL
SELECT 2, 3 FROM DUAL;
Outputs:
MEDIAN
1.5
(Note: For your sample data, there are 6 items, an even number, so the MEDIAN will be half way between the value of 3rd and 4rd items; so half way between 1 and 2 = 1.5.)
db<>fiddle here

Can I start a new group when value changes from 0 to 1?

Can I somehow assign a new group to a row when a value in a column changes in T-SQL?
I would be grateful if you can provide solution that will work on unlimited repeating numbers without CTE and functions. I made a solution that work in sutuation with 100 consecutive identical numbers(with
coalesce(lag()over(), lag() over(), lag() over() ) - it is too bulky
but can not make a solution for a case with unlimited number of consecutive identical numbers.
Data
id somevalue
1 0
2 1
3 1
4 0
5 0
6 1
7 1
8 1
9 0
10 0
11 1
12 0
13 1
14 1
15 0
16 0
Expected
id somevalue group
1 0 1
2 1 2
3 1 2
4 0 3
5 0 3
6 1 4
7 1 4
8 1 4
9 0 5
10 0 5
11 1 6
12 0 7
13 1 8
14 1 8
15 0 9
16 0 9
If you just want a group identifier, you can use:
select t.*,
min(id) over (partition by some_value, seqnum - seqnum_1) as grp
from (select t.*,
row_number() over (order by id) as seqnum,
row_number() over (partition by somevalue order by id) as sequm_1
from t
) t;
If you want them enumerated . . . well, you can enumerate the id above using dense_rank(). Or you can use lag() and a cumulative sum:
select t.*,
sum(case when some_value = prev_sv then 0 else 1 end) over (order by id) as grp
from (select t.*,
lag(somevalue) over (order by id) as prev_sv
from t
) t;
Here's a different approach:
First I created a view to provide the group increment on each row:
create view increments as
select
n2.id,n2.somevalue,
case when n1.somevalue=n2.somevalue then 0 else 1 end as increment
from
(select 0 as id,1 as somevalue union all select * from mytable) n1
join mytable n2
on n2.id = n1.id+1
Then I used this view to produce the group values as cumulative sums of the increments:
select id, somevalue,
(select sum(increment) from increments i1 where i1.id <= i2.id)
from increments i2

SQL Select to limit results to X of each id value in a specific column

I am trying to figure out how to select up to lets say 2 results per key that I have in my table.
My table looks like the following
uid map
1 11
1 12
1 16
1 21
1 26
2 1
2 11
2 14
2 12
2 22
3 12
3 15
3 16
What Im looking to do is select up to 2 rows of data for each uid
example results:
uid map
1 11
1 12
2 1
2 11
3 12
3 15
Thanks for any help you can provide!
Using ANSI-standard SQL, the simplest way is row_number():
select uid, map
from (select t.*, row_number() over (partition by uid order by map) as seqnum
from t
) t
where seqnum <= 2;
SELECT *
FROM table1 a
WHERE
(
SELECT COUNT(*)
FROM table1 b
WHERE a.uid = b.uid AND
a.map <= b.map
) <= 2

Select Data based on Sum of another columns value

I have a Table with Data as
RowIndex Id TicketCount
1 23 1
2 25 2
3 3 1
4 14 1
5 16 1
6 18 1
7 1 1
8 6 1
9 15 1 ===> at this row the sum of Ticket Count is 10
10 22 1
11 27 1
12 24 1
13 26 2
14 9 1
15 19 1
From this Data I want to Select All Records where The Sum of Ticket Count will be equal to 10(user input value)
In the Given data I want to Select all Records till Row Index 9.
Output should be:
RowIndex Id TicketCount
1 23 1
2 25 2
3 3 1
4 14 1
5 16 1
6 18 1
7 1 1
8 6 1
9 15 1
SQL Server 2008 doesn't have the cumulative sum function. I implement it using a correlated subquery:
select RowIndex, Id, TicketCount
from (select t.*,
(select sum(TicketCount)
from t t2
where t2.RowIndex <= t.RowIndex
) as cumTicketCount
from t
) t
where cumTicketCount <= 10;
In SQL Server 2012, you can phrase this using a window function:
select RowIndex, Id, TicketCount
from (select t.*, sum(TicketCount) over (order by RowIndex) as CumTicketCount
from t
) t
where cumTicketCount <= 10;
You can do it using recursive CTE:
WITH RCTE AS
(
SELECT *, TicketCount AS Total
FROM Table1
WHERE RowIndex = 1
UNION ALL
SELECT t.*, r.Total + t.TicketCount
FROM RCTE r
INNER JOIN Table1 t ON r.RowIndex + 1 = t.RowIndex
WHERE r.Total + t.TicketCount <= 10 --your input value
)
SELECT * FROM RCTE
SQLFiddle DEMO