How to get all numbers between a range - sql-server

I have a table as below
Id RFrom RTo
.... ....... .....
1 10 14
1 22 25
2 100 102
2 176 180
I want to get all numbers between each RFrom and RTo for each Id. My expected result is as follows
Id NUMS
.... ......
1 10
1 11
1 12
1 13
1 14
1 22
1 23
1 24
1 25
2 100
2 101
2 102
2 176
2 177
2 178
2 179
2 180
Do I have to use cursor to achieve this?

Here is your sample table
SELECT * INTO #TEMP FROM
(
SELECT 1 ID, 10 RFROM, 14 RTO
UNION ALL
SELECT 1, 22, 25
UNION ALL
SELECT 2, 100, 102
UNION ALL
SELECT 2, 176, 180
)TAB
You need to use recursion for each Id to get the result
;WITH CTE AS
(
SELECT ID,RFROM RFROM1,RTO RTO1
FROM #TEMP
UNION ALL
SELECT T.ID,RFROM1+1,RTO1
FROM #TEMP T
JOIN CTE ON CTE.ID = T.ID
WHERE RFROM1 < RTO1
)
SELECT DISTINCT ID,RFROM1 NUMS
FROM CTE
SQL FIDDLE

Another option would be to use a numbers table with a join -- recursion can be time consuming.
There are several options to create a numbers table (I'd recommend creating a permanent one), but here's a temp one created with a common-table-expression:
with numberstable as (
select top 10000 row_number() over(order by t1.number) as number
from master..spt_values t1
cross join master..spt_values t2
)
select yt.id,
nt.number
from yourtable yt
join numberstable nt on nt.number between yt.rfrom and yt.rto
SQL Fiddle Demo

Create a tally table using stacked CTE which will have better performance when compared to recursive CTE
declare #min int
select #min= min(RFrom) from yourtable
;WITH e1(n) AS
(
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
), -- 10
e2(n) AS (SELECT 1 FROM e1 CROSS JOIN e1 AS b), -- 10*10
e3(n) AS (SELECT 1 FROM e1 CROSS JOIN e2) -- 10*100
SELECT b.id,
a.n
FROM yourtable b
JOIN (SELECT n = Row_number()OVER (ORDER BY n)+ #min-1
FROM e3)a
ON a.n BETWEEN b.RFrom AND b.RTo
ORDER BY n;
Check here for info

Related

Generate rows within table based on existing rows

Is it possible to generate rows within a table based on existing column in the table in SQL Server? For example, if RunDates value = 31/01/2020 and RunTimes = 3, then there should be 3 rows in the table for RunDate = 31/01/2020
Current table
Desired table
You can do it with a recursive CTE:
with cte as (
select RunDates, RunTimes, 1 nr from tablename
union all
select RunDates, RunTimes, nr + 1
from cte
where nr < RunTimes
)
select RunDates, RunTimes from cte
order by RunDates
See the demo.
Results:
> RunDates | RunTimes
> :--------- | -------:
> 2020-01-31 | 3
> 2020-01-31 | 3
> 2020-01-31 | 3
> 2020-02-29 | 2
> 2020-02-29 | 2
> 2020-03-31 | 1
First you need a tally table (view) e.g.
CREATE View [dbo].[cteTally]
as
WITH
E1(N) AS (select 1 from (values (1),(1),(1),(1),(1),(1),(1),(1),(1),(1))dt(n))
, E2(N) AS (SELECT 1 FROM E1 a, E1 b) --10E+2 or 100 rows
, E4(N) AS (SELECT 1 FROM E2 a, E2 b) --10E+4 or 10,000 rows max
, cteTally(N) AS (
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
)
select N from cteTally
GO
Then you join it on to your table e.g.
declare #MyTable table (MyDate date, RunCount int);
insert into #MyTable (MyDate, RunCount)
values
('31 Jan 2020', 3),
('29 Feb 2020', 2),
('31 Mar 2020', 1);
select MT.*
from #MyTable MT
inner join cteTally T on T.N <= MT.RunCount
order by MyDate, RunCount;
Returns:
MyDate RunCount
--------------------
2020-01-31 3
2020-01-31 3
2020-01-31 3
2020-02-29 2
2020-02-29 2
2020-03-31 1
NOTE: The Tally Table is courtesy of #Larnu but I can't find the original post.
You can try the following inner join by creating a serial number from sys.objects.
Here I have fixed 10 assuming the maximum value of RunTime. You can create a variable and assign the maximum value of the RunTime value and use that variable in place of 10.
Here is an another way to do that.
create table SampleTable (DtDate Date, RunTimes int)
insert into SampleTable Values
('31 Jan 2020', 3),
('29 Feb 2020', 2),
('31 Mar 2020', 1)
SELECT SampleTable.*
FROM SampleTable
INNER JOIN (
SELECT TOP 10 ROW_NUMBER() OVER (
ORDER BY object_id
) AS SrNo
FROM sys.objects
) mst ON RunTimes >= SrNo
ORDER BY DtDate
Live db<>fiddle demo.

How to make Row_number() based on condition?

I have list of sample data. Using this I need new column which having sequence number. But condition of this sequence number is if consecutively InRange column value 1 then only it generate sequence number.In between if InRange value 0 then again sequence number start from 1 and so on.
Below query which I have created but not return expected result.
CREATE TABLE #Result (ID INT,Value INT,InRange BIT)
INSERT INTO #Result
SELECT 1 ,211,0
UNION SELECT 2 ,205,1
UNION SELECT 3 ,214,0
UNION SELECT 4 ,202,1
UNION SELECT 5 ,204,1
UNION SELECT 6 ,203,1
UNION SELECT 7 ,209,0
UNION SELECT 8 ,216,0
UNION SELECT 9 ,205,1
UNION SELECT 10 ,224,0
Query:
SELECT *
,CASE WHEN InRange=1 THEN ROW_NUMBER()OVER(Order by Id asc) ELSE 0 END AS ExpectedColumn
FROM #Result
Expected result.
ID Value InRange ExpectedColumn
1 211 0 0
2 205 1 1
3 214 0 0
4 202 1 1
5 204 1 2
6 203 1 3
7 209 0 0
8 216 0 0
9 205 1 1
10 224 0 0
This is a gaps and islands problem, with the islands being each group of records to which you want to assign its own row number sequence. One straightforward way to handle this uses the difference in row numbers method:
WITH cte1 AS (
SELECT *, ROW_NUMBER() OVER (ORDER BY ID) rn1
FROM #Result
WHERE InRange = 1
),
cte2 AS (
SELECT t1.*,
ROW_NUMBER() OVER (ORDER BY t1.ID) - t2.rn1 AS diff
FROM #Result t1
LEFT JOIN cte1 t2
ON t1.ID = t2.ID
)
SELECT ID, Value, InRange,
CASE WHEN InRange <> 0
THEN ROW_NUMBER() OVER (PARTITION BY diff ORDER BY ID)
ELSE 0 END AS ExpectedColumn
FROM cte2
ORDER BY ID;
Demo
with grouped_data as (
select
*,
count(case when InRange = 0 then 1 else null end) over(order by ID rows between unbounded preceding and current row) as group_number
from #Result
)
select
ID,
Value,
InRange,
row_number() over(partition by group_number order by ID) - 1 as expected_column
from grouped_data
order by ID;

Addition of 1st row and 2nd row then 2nd row and 3rd row.. using SQL

I have a table in SqlServer, which has one column
E.g.:
AvgCall
6.25
6.25
10
12.5
24.5
6
9
3
Now by using this data I want the result as shown below.
AvgCall NewColumn
6.25 6.25
6.25 12.5
10 16.25
12.5 22.5
24.5 37
6 30.5
9 15
3 12
So in the output NewColumn is added by the logic i.e. first row data as first row, then addition of first row + Second row then second row + third row.
how to get my result?
If you are using sqlserver 2012, you also can use lead like below
select col1,col1+lead(col1) over (order by <<some orderring col>>)
from
tbl
For SQL version LOWER than 2012 use this:
; WITH T(Nums) as
(
SELECT 6.25
UNION ALL
SELECT 6.25
UNION ALL
SELECT 10
UNION ALL
SELECT 12.5
UNION ALL
SELECT 24.5
UNION ALL
SELECT 6
UNION ALL
SELECT 9
UNION ALL
SELECT 3
), Main as
(
SELECT ROW_NUMBER () OVER (ORDER BY (SELECT NULL)) Id, *
FROM T
)
SELECT a.Nums , A.Nums + ISNULL((SELECT MAX(b.Nums) FROM Main b WHERE b.Id = a.Id-1 ),0) Col
FROM Main a
you can use LAG
create table #tmp(AvgCall FLOAT)
insert into #tmp
SELECT 6.25
union ALL
SELECT 6.25
union ALL
SELECT 10
union ALL
SELECT 12.5
union ALL
SELECT 24.5
union ALL
SELECT 6
union ALL
SELECT 9
union ALL
SELECT 3
;with cte
AS(
Select ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS rownum, * from #tmp
)
select AvgCall+ISNULL(LAG(AvgCall) OVER(order by rownum),0)
from cte
drop table #tmp

SQL Server 2008 Find row based on 1 unique column and duplicates in a second column

Can you please suggest how I can write a query to retrieve the following?
I need to find any rows where the value in A is duplicated but the value in B is different
A B
4567 2
852125 9
444 8
2547 25
255 4
256 1
2547 25
2547 27
259 4
2547 25
Should return
2547 27
2547 25
As 2547 has two non identical values in column B
Thanks in anticipation
P
with cte1 as (
select distinct A, B from Table1
), cte2 as (
select A, B, count(*) over(partition by A) as cnt from cte1
)
select
A, B
from cte2
where cnt > 1
sql fiddle demo
or
with cte as (
select distinct
A, B, min(B) over(partition by A) as m1, max(B) over(partition by A) as m2
from Table1
)
select
A, B
from cte
where m1 <> m2
sql fiddle demo
Try this
With DemoTable AS
(
Select 4567 A,2 B
Union All Select 852125 ,9
Union All Select 444 ,8
Union All Select 2547 ,25
Union All Select 255 ,4
Union All Select 256 ,1
Union All Select 2547 ,25
Union All Select 2547 ,27
Union All Select 259 ,4
Union All Select 2547 ,25
)
Select Distinct A, B
From DemoTable
Where A In
(
Select A
From DemoTable
Group By A
Having Count (Distinct B) > 1
)
Output
A B
----------- -----------
2547 25
2547 27
(2 row(s) affected)
it's simple just use a nested select statement which one of them has count, group by and having statements.
select distinct tbl.A,tbl.B from table_name tbl
where tbl.A in (select A from (
select tb.A, count(tb.B)
from table_name tb group by tb.A having count(tb.B)>1))
order by tbl.A

how to find previous & forward relation of data in single query

I have table :
ID Person_ID Person_Relative_ID
1 10 20
2 20 30
3 13 15
4 30 40
5 55 56
6 40 50
Here we can see the person & person_relative_id chain is going like 10 - 20 - 30 - 40 - 50
Now if user search Person_Relative_ID of person_id = 20 then result would be like: [All its relation]
Person_Relative_ID
10
30
40
50
Or user want to search Person_Relative_ID for person_id = 40 . then result like
Person_Relative_ID
10
20
30
50
Or user want to search Person_Relative_ID for person_id = 50 . then result like
Person_Relative_ID
10
20
30
40
Any suggestion really appreciated.
Well I'm not sure this is the most performance effective solution, and I'd have liked to deal with a single recursive CTE instead of two, but this works at least.
Just replace the references to the temporary table I used to test this, with your real table. But what it does, is it uses two recursive CTE's to find all references above (CTEUp) and below (CTEDown) your ID, and then shows them in order, except for your searched ID.
Note: This is for SQL Server, not Oracle.
-- Creating dummy variables for testing
DECLARE #PERSONS TABLE (ID INT IDENTITY(1,1), Person_ID INT, Person_Relative_ID INT)
INSERT INTO #PERSONS VALUES (10,20), (20,30), (13,15), (30,40), (55,56), (40,50)
-- Variable for searched ID, the actual script begins here
DECLARE #SEARCHED_ID INT
SET #SEARCHED_ID = 20
;WITH CTEUp AS
-- Fetching all relations above the ID
(SELECT Person_ID
FROM #PERSONS
WHERE Person_ID = #SEARCHED_ID
UNION ALL
SELECT Person_Relative_ID
FROM #PERSONS P
JOIN CTEUp C ON C.Person_ID = P.Person_ID
AND P.Person_Relative_ID > C.Person_ID)
, CTEDown AS
-- Fetching all relations below the ID
(SELECT Person_ID
FROM #PERSONS
WHERE Person_Relative_ID = #SEARCHED_ID
UNION ALL
SELECT P.Person_ID
FROM #PERSONS P
JOIN CTEDown C ON C.Person_ID = P.Person_Relative_ID
AND P.Person_ID < C.Person_ID)
-- Showing results
SELECT Person_ID
FROM
(SELECT *
FROM CTEDown
UNION ALL
SELECT *
FROM CTEUp) SRC
WHERE Person_ID <> #SEARCHED_ID --... minus the ID, as per your example
ORDER BY Person_ID ASC
Just gave a try in Oracle, could be better but still it works
with tab(ID, Person_ID, Person_Relative_ID) as (
SELECT 1, 10, 20 from dual union all
SELECT 2, 20, 30 from dual union all
SELECT 3, 13, 15 from dual union all
SELECT 4, 30, 40 from dual union all
SELECT 5, 55, 56 from dual union all
SELECT 6, 40, 50 from dual),
---------
--End of data preparation
---------
filter_tab as (
select 40 as id from dual), --> Put the search id here
final_tab(person_id) as (
select person_id
from tab
start with person_relative_id = (select id from filter_tab)
connect by prior person_id = person_relative_id
union
select person_relative_id
from tab
start with person_id = (select id from filter_tab)
connect by prior person_relative_id = person_id)
select *
from final_tab
where not exists (select 'x'
from filter_tab
where id = person_id )
order by 1;
Output:
PERSON_ID
---------
10
20
30
50
I just traversed to both ends from the start point, did union of the result and excluded the search id.
In SQL Server 2012 and above, you can use LAG and LEAD function.
Here is a sample:
SELECT TOP 1000 [n], LAG([n]) OVER(ORDER BY [n] ), LEAD([n]) OVER (ORDER BY [n])
FROM [dbo].[Nums]
it generates results:
n LAG LEAD
1 NULL 2
2 1 3
3 2 4
4 3 5
5 4 6
If you are using SQL2005 and above, SQL Recursive CTE structured SQL query can be used for querying hierarchical data models like in your case
Below query uses also multiple CTE queries to prevent usage of unnecessary temp tables or table variables
declare #id smallint = 50
;with cte as (
select Person_ID, Person_Relative_ID
from PersonRelative
where Person_ID = #id OR Person_Relative_ID = #id
union all
select P.Person_ID, P.Person_Relative_ID
from PersonRelative P
inner join CTE on CTE.Person_ID = P.Person_Relative_ID
), cte2 as (
select Person_ID from cte
union
select Person_Relative_ID from cte
), cte3 as (
select Person_ID, Person_Relative_ID
from PersonRelative
where Person_ID = #id OR Person_Relative_ID = #id
union all
select P.Person_ID, P.Person_Relative_ID
from PersonRelative P
inner join CTE3 on CTE3.Person_Relative_ID = P.Person_ID
), cte4 as (
select Person_ID from cte3
union
select Person_Relative_ID from cte3
)
select * from cte4 where Person_ID <> #id
union
select * from cte2 where Person_ID <> #id

Resources