SQL Query on Aggregating the sequence numbers - sql-server

I have a table which has sequence numbers from 1 to 90000
so i wanted to know how to automatically assign the values to the sequence numbers
say for example from 1 to 1000 i want them to fall under 1000 bucket
from 1001 to 2000 under 2000 bucket
and so on up to 90000 records.

You can divide the number by 1000, floor it, and multiply it back by 1000:
SELECT 1000*FLOOR(num/1000) + 1, COUNT(*)
FROM mytable
GROUP BY FLOOR(num/1000)

The Modulo (%) operator is perfect for something like this...
So easy, it feels like it's cheating.
WITH
cte_n1 (n) AS (SELECT 1 FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) n (n)),
cte_n2 (n) AS (SELECT 1 FROM cte_n1 a CROSS JOIN cte_n1 b),
cte_n3 (n) AS (SELECT 1 FROM cte_n2 a CROSS JOIN cte_n2 b),
Sequense (n) AS (
SELECT TOP 90000
ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM
cte_n3 a CROSS JOIN cte_n3 b
)
SELECT
SequenseNumber = s.n,
GroupNumber = s.n - (s.n % 1000)
FROM
Sequense s;
Results...
SequenseNumber GroupNumber
-------------------- --------------------
1 0
2 0
3 0
4 0
5 0
6 0
.........................
997 0
998 0
999 0
1000 1000
1001 1000
1002 1000
1003 1000
1004 1000
1005 1000
1006 1000
1007 1000
1008 1000
1009 1000
1010 1000
.........................
89990 89000
89991 89000
89992 89000
89993 89000
89994 89000
89995 89000
89996 89000
89997 89000
89998 89000
89999 89000
90000 90000
(90000 rows affected)

The code below does what you want, it uses CEILING and an algorithm that works from 0 to 90K literally consider that the num must be DECIMAL if you use int the decimal round is set to 0.
Test data
declare #tbl table(num decimal)
insert into #tbl
select 1 union
select 999 union
select 1000 union
select 1001 union
select 2001 union
select 3001 union
select 9999 union
select 10000 union
select 10001 union
select 15001 union
select 25001 union
select 77006 union
select 80000 union
select 90000
Query
SELECT distinct
num,
CASE WHEN num <= 10000 THEN 1000*CEILING(num/1000)
WHEN num <= 20000 THEN 10000 + 1000*CEILING((num-10000)/1000)
WHEN num <= 30000 THEN 20000 + 1000*CEILING((num-20000)/1000)
WHEN num <= 40000 THEN 30000 + 1000*CEILING((num-30000)/1000)
WHEN num <= 50000 THEN 40000 + 1000*CEILING((num-40000)/1000)
WHEN num <= 60000 THEN 50000 + 1000*CEILING((num-50000)/1000)
WHEN num <= 70000 THEN 60000 + 1000*CEILING((num-60000)/1000)
WHEN num <= 80000 THEN 70000 + 1000*CEILING((num-70000)/1000)
WHEN num <= 90000 THEN 80000 + 1000*CEILING((num-80000)/1000)
ELSE 0
END
FROM #tbl
Result
1 1000
999 1000
1000 1000
1001 2000
2001 3000
3001 4000
9999 10000
10000 10000
10001 11000
15001 16000
25001 26000
77006 78000
80000 80000
90000 90000

I'm going to assume that what Jason posted is what the OP is looking for. This is a slight variation using getnumsAB which was developed for exactly this type of thing. First we'll use it to create some sample data:
Sample data
if object_id('tempdb..#yourdata') is not null drop table #yourdata;
select SequenceNumber = rn
into #yourdata
from dbo.GetNumsAB(1,90000,1,1);
create unique clustered index uq_cl_yourdata on #yourdata(SequenceNumber);
To understand my solution first note this query:
select rn, n1, n2 from dbo.getnumsAB(0,90000,1000,1);
This returns:
rn n1 n2
----- -------- --------
1 0 1000
2 1000 2000
3 2000 3000
4 3000 4000
....
87 86000 87000
88 87000 88000
89 88000 89000
90 89000 90000
Solution
select y.SequenceNumber, GroupNumber = n1
from #yourdata y
join dbo.getnumsAB(0,90000,1000,1) gn
on y.SequenceNumber >= n1 and y.SequenceNumber < n2;

If i'm not missing something wouldn't you just use a case statement?
select case
when Sequence <= 1000
then '1000'
when Sequence <= 2000 and >= 1001
then '2000'
and so on up to 9000?

Related

How to merge three select queries into one in order to achieve the desired result as below in Oracle?

The purpose is to push 'N' flag on the first negative occurrence of Planned Delta and Actual Delta respectively for each project.
'Y' flag should be pushed in all the rest rows.
Rank column defines the order and Project ID defines the data of one project.
Only select queries can be used
Use below table structure
'''
CREATE TABLE payBackTable (projectID varchar2(10), quarterYear varchar2(10), Rank int, planned_delta int, actual_delta int )
INSERT INTO payBackTable values
( 'P001','Q1-2017', 1, 2000, 900)
( 'P001','Q2-2017', 2, 18000, 800)
( 'P001','Q3-2017', 3, 0, 7000)
( 'P001','Q4-2017', 4, 9000, -90)
( 'P001','Q1-2018', 5, -10, 9000)
( 'P001','Q2-2018', 6, 100, 70)
( 'P001','Q3-2018', 7, -90, -900)
( 'P001','Q4-2018', 8, 200, -8)
( 'P002', 'Q3-2016', 1, 1000, 90 )
( 'P002', 'Q4-2016', 2, -200, 90 )
( 'P002', 'Q1-2017', 3, 4000, -500 )
( 'P002', 'Q2-2017', 4, 10, -90 )
( 'P003', 'Q3-2021', 1, -10, 700 )
( 'P003', 'Q4-2021', 2, 100, -800 )
( 'P003', 'Q1-2022', 3, -100, -900 )
( 'P003', 'Q2-2022', 3, -90, 100 )
'''
Source Table
Project ID
Quarter-Year
Rank
Planned Delta
Actual Delta
P001
Q1-2017
1
2000
900
P001
Q2-2017
2
18000
800
P001
Q3-2017
3
0
7000
P001
Q4-2017
4
9000
-90
P001
Q1-2018
5
-10
9000
P001
Q2-2018
6
100
70
P001
Q3-2018
7
-90
-900
P001
Q4-2018
8
200
-8
P002
Q3-2016
1
1000
90
P002
Q4-2016
2
-200
90
P002
Q1-2017
3
4000
-500
P002
Q2-2017
4
10
-90
P003
Q3-2021
1
-10
700
P003
Q4-2021
2
100
-800
P003
Q1-2022
3
-100
-900
P003
Q2-2022
4
-90
100
Desired Result
Project ID
QuarterYear
Rank
Planned Delta
Actual Delta
Planned Flag
Actual Flag
P001
Q1-2017
1
2000
900
Y
Y
P001
Q2-2017
2
18000
800
Y
Y
P001
Q3-2017
3
0
7000
Y
Y
P001
Q4-2017
4
9000
-90
Y
N
P001
Q1-2018
5
-10
9000
N
Y
P001
Q2-2018
6
100
70
Y
Y
P001
Q3-2018
7
-90
-900
Y
Y
P001
Q4-2018
8
200
-8
Y
Y
P002
Q3-2016
1
1000
90
Y
Y
P002
Q4-2016
2
-200
90
N
Y
P002
Q1-2017
3
4000
-500
Y
N
P002
Q2-2017
4
10
-90
Y
Y
P003
Q3-2021
1
-10
700
N
Y
P003
Q4-2021
2
100
-800
Y
N
P003
Q1-2022
3
-100
-900
Y
Y
P003
Q2-2022
4
-90
100
Y
Y
I have been able to achieve table to push the desired result using three different queries/batch jobs but not sure how to implement this in a single query/batch job.
To push 'N' flag in Planned Flag
select projectID,quarterYear, 'N' from(
select projectID, quarterYear,
Row_number() OVER(PARTITION BY projectID ORDER BY to_number(Rank))
as rownumm from payBackTable
where to_number(planned_delta) < 0) where rownumm = 1
To push 'N' flag in Actual Flag
select projectID,quarterYear, 'N' from(
select projectID, quarterYear,
Row_number() OVER(PARTITION BY projectID ORDER BY to_number(Rank))
as rownumm from payBackTable
where to_number(actual_delta) < 0) where rownumm = 1
To push 'Y' flag in Planned Flag and Actual Flag
select projectID,
quarterYear,
case planned_Flag when NULL then 'Y' ELSE planned_Flag END,
case actual_Flag when NULL then 'Y' ELSE actual_Flag END
from payBackTable
You could get a ranking for each value within the project and based on the sign of each value:
select projectid, quarteryear, rank, planned_delta, actual_delta,
dense_rank() over (partition by projectid, sign(planned_delta) order by rank) as flag1,
dense_rank() over (partition by projectid, sign(actual_delta) order by rank) as flag2
from paybacktable
order by projectid, rank
PROJECTID
QUARTERYEAR
RANK
PLANNED_DELTA
ACTUAL_DELTA
FLAG1
FLAG2
P001
Q1-2017
1
2000
900
1
1
P001
Q2-2017
2
18000
800
2
2
P001
Q3-2017
3
0
7000
1
3
P001
Q4-2017
4
9000
-90
3
1
P001
Q1-2018
5
-10
9000
1
4
P001
Q2-2018
6
100
70
4
5
P001
Q3-2018
7
-90
-900
2
2
P001
Q4-2018
8
200
-8
5
3
...
And then use those as part of case expressions; if that rank i1 and the value is negative, then it's the first negative value:
select projectid, quarteryear, rank, planned_delta, actual_delta,
case
when sign(planned_delta) = -1
and dense_rank() over (partition by projectid, sign(planned_delta) order by rank) = 1
then 'N'
else 'Y'
end as planned_flag,
case
when sign(actual_delta) = -1
and dense_rank() over (partition by projectid, sign(actual_delta) order by rank) = 1
then 'N'
else 'Y'
end as actual_flag
from paybacktable
order by projectid, rank
PROJECTID
QUARTERYEAR
RANK
PLANNED_DELTA
ACTUAL_DELTA
PLANNED_FLAG
ACTUAL_FLAG
P001
Q1-2017
1
2000
900
Y
Y
P001
Q2-2017
2
18000
800
Y
Y
P001
Q3-2017
3
0
7000
Y
Y
P001
Q4-2017
4
9000
-90
Y
N
P001
Q1-2018
5
-10
9000
N
Y
P001
Q2-2018
6
100
70
Y
Y
P001
Q3-2018
7
-90
-900
Y
Y
P001
Q4-2018
8
200
-8
Y
Y
P002
Q3-2016
1
1000
90
Y
Y
P002
Q4-2016
2
-200
90
N
Y
P002
Q1-2017
3
4000
-500
Y
N
P002
Q2-2017
4
10
-90
Y
Y
P003
Q3-2021
1
-10
700
N
Y
P003
Q4-2021
2
100
-800
Y
N
P003
Q2-2022
3
-90
100
Y
Y
P003
Q1-2022
3
-100
-900
Y
Y
db<>fiddle
select P1.projectID, P1.quarterYear, P1.Rank, P1.planned_Delta, P1.actual_Delta,
case when Rank = X1.RankMin then 'N' ELSE 'Y' END plannedFlag ,
case when Rank = X2.RankMin then 'N' ELSE 'Y' END actualFlag
from payBackTable P1
left join (
select projectID, min(Rank) RankMin
from payBackTable P2
where Planned_Delta < 0
group by projectID
) X1 on P1.projectID = X1.projectID
left join (
select projectID, min(Rank) RankMin
from payBackTable P2
where Actual_Delta < 0
group by projectID
) X2 on P1.projectID = X2.projectID

Multiply by largest row and append to list

I have the following in two tables:
(SFR_MAIN)
SQ. FT. AREA 1 2
400 86.6600 86.7300
500 82.3300 82.4000
600 78.9600 79.0200
700 76.2100 76.2700
800 73.9100 73.9700
900 71.9400 71.9900
1000 70.2200 70.2700
1100 68.7000 68.7500
1200 67.3400 67.3900
1300 66.1200 66.1600
1400 65.0000 65.0400
1600 63.0400 63.0800
1800 61.3600 61.3900
2000 59.8900 59.9300
2200 58.6000 58.6300
2400 57.4400 57.4700
2600 56.3900 56.4200
2800 55.4400 55.4700
3000 54.5700 54.6000
And
(MULT)
SQ. FT. AREA MULTIPLIER
3100 0.992
3200 0.986
3300 0.978
3400 0.971
3500 0.964
3600 0.958
3700 0.952
3800 0.946
4000 0.934
The idea would be to create a table with both. The problem is that the second is a multiplier that should be multiplied to that last(Largest) squarfootage in the first.
So 3100 would be .992 * 54.57 for column 1 and .992 * 54.6 for column 2, yes those are the actual names of the columns.
So the desired output would be:
SQ. FT. AREA 1 2
400 86.6600 86.7300
500 82.3300 82.4000
600 78.9600 79.0200
700 76.2100 76.2700
800 73.9100 73.9700
900 71.9400 71.9900
1000 70.2200 70.2700
1100 68.7000 68.7500
1200 67.3400 67.3900
1300 66.1200 66.1600
1400 65.0000 65.0400
1600 63.0400 63.0800
1800 61.3600 61.3900
2000 59.8900 59.9300
2200 58.6000 58.6300
2400 57.4400 57.4700
2600 56.3900 56.4200
2800 55.4400 55.4700
3000 54.5700 54.6000
3100 54.1334 54.1632
3200 53.8060 53.8356
3300 53.3695 53.3988
3400 52.9875 53.0166
3500 52.6055 52.6344
3600 52.2781 52.3068
3700 51.9506 51.9792
3800 51.6232 51.6516
4000 50.9684 50.9964
I can do one column like this:
select
[SQ. FT. AREA],
[1]
from test.dbo.SFR_MAIN
union all
Select
[SQ. FT. AREA],
MULTIPLIER * (select
m.[1]
from test.dbo.SFR_MAIN m
inner join
(
select max([SQ. FT. AREA]) as mmm
from test.dbo.SFR_MAIN
) tt on tt.mmm = m.[SQ. FT. AREA]
) as [1]
from test.dbo.mult
And if I only had the two columns I would just do it twice, but I have 16 columns. Is there a way to iterate through the columns to get them all side by side in a table?
I am using SQL Server 2012
Example
Select * From SFR_MAIN
Union All
Select B.[SQ. FT. AREA]
,[1] = A.[1]*B.MULTIPLIER
,[2] = A.[2]*B.MULTIPLIER
From (Select Top 1 with ties * from SFR_MAIN Order by [SQ. FT. AREA] Desc) A
Join MULT B on B.[SQ. FT. AREA]>A.[SQ. FT. AREA]
Returns
Iteration in databases is generally a bad idea. Databases work better on set based approaches. So, union as you describe but then use cross join on the record from the first set that has the highest sq foot and then just do the math.
SELECT [SQ. Ft. Area], [1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[11],[12],[13],[14],[15],[16]
FROM sfr_Main
UNION ALL
SELECT A.[SQ. FT. Area]
, B.multiplier*[1] as 1
, B.multiplier*[2] as 2
, B.multiplier*[3] as 3
, B.multiplier*[4] as 4
, B.multiplier*[5] as 5
, B.multiplier*[6] as 6
, B.multiplier*[7] as 7
, B.multiplier*[8] as 8
, B.multiplier*[9] as 9
, B.multiplier*[10] as 10
, B.multiplier*[11] as 11
, B.multiplier*[12] as 12
, B.multiplier*[13] as 13
, B.multiplier*[14] as 14
, B.multiplier*[15] as 15
, B.multiplier*[16] as 16
FROM MULT A
CROSS JOIN (SELECT top 1 [SQ. Ft. Area], [1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[11],[12],[13],[14],[15],[16]
FROM sfr_main
ORDER BY [sq.ft.area] desc) B

select data, grouped as Histogram

I have data in this format:
CREATE TABLE data(y int)
INSERT INTO data VALUES ((1))
INSERT INTO data VALUES ((55555))
INSERT INTO data VALUES ((55555))
INSERT INTO data VALUES ((99999))
I want to create a histogram, for to get a rough overview, of how my data is distributed. I am thinking of this format as output:
lowerBoundary upperBoundary y
------------- ------------- -----------
0 9999 1
10000 19999 0
20000 29999 0
30000 39999 0
40000 49999 0
50000 59999 2
60000 69999 0
70000 79999 0
80000 89999 0
90000 99999 1
You will have to create a table of numbers, so that the 0-rows will be displayed correctly. Then you can calculate lower and upper boundary of each "group".
Example SQL:
SELECT lowerBoundary, upperBoundary, COUNT(d.y) AS y
FROM (
SELECT n*10000 AS lowerBoundary, (n+1)*10000-1 AS upperBoundary
FROM (
-- Selects possible groups. Make this big enough for your data.
SELECT ones.n + 10*tens.n + 100*hundreds.n AS n
FROM (VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) ones(n),
(VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) tens(n),
(VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) hundreds(n)
) numbersTable
) boundaries
-- join with data
LEFT JOIN data d
ON d.y BETWEEN lowerBoundary AND upperBoundary
-- avoid trailing '0' rows
WHERE lowerBoundary <= (SELECT MAX(d.y) FROM data d)
GROUP BY lowerBoundary, upperBoundary
ORDER BY 1
Click here to run this skript at SQL-Fiddle
Another option...
I use a TVF to generate dynamic ranges. Being a single-statement function, it is extremely fast. Furthermore, if you can't use a UDF, the logic is easily ported into a cte or sub-query.
Select RetVal1
,RetVaL2
,y = sum(case when y is null then 0 else 1 end)
From [dbo].[udf-Range-Number-Span](0,100000,10000) A
Left Join Data B on y>=RetVal1 and y<RetVal2
Group By RetVal1,RetVal2
Returns
RetVal1 RetVaL2 y
0.00 10000.00 1
10000.00 20000.00 0
20000.00 30000.00 0
30000.00 40000.00 0
40000.00 50000.00 0
50000.00 60000.00 2
60000.00 70000.00 0
70000.00 80000.00 0
80000.00 90000.00 0
90000.00 100000.00 1
The UDF if needed
CREATE FUNCTION [dbo].[udf-Range-Number-Span] (#R1 money,#R2 money,#Incr money)
Returns Table
Return (
with cte0(M) As (Select cast((#R2-#R1)/#Incr as int)),
cte1(N) As (Select 1 From (Values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) N(N)),
cte2(N) As (Select Top (Select M from cte0) Row_Number() over (Order By (Select NULL)) From cte1 a,cte1 b,cte1 c,cte1 d,cte1 e,cte1 f,cte1 g,cte1 h )
Select RetSeq=1,RetVal1=#R1,RetVal2=#R1+#Incr
Union All
Select N+1,(N*#Incr)+#R1,((N*#Incr)+#R1)+#Incr
From cte2,cte0
Where N<cte0.M
)
--Max 100 million observations
--Select * from [dbo].[udf-Range-Number-Span](1,4,.5)

Access Previous Value of Row and use it in Calculations of SQL query

I have a table
ID OpeningBal Type
1 1000 -
2 100 IN
3 200 OUT
4 100 OUT
5 300 IN
I want Output in sql query without lag function like this
ID OpeningBal Type Closing Bal
1 1000 - 0
2 100 IN 1100
3 200 OUT 900
4 100 OUT 800
5 300 IN 1100
This will work with sql-server 2012:
DECLARE #t
table(ID int, OpeningBal int, Type varchar(3))
INSERT #t values
(1,1000,'-'),(2,100,'IN'),(3,200,'OUT'),(4,100,'OUT'),(5,300,'IN')
SELECT
*,
CASE WHEN Type = '-'
THEN 0
ELSE
sum(case when Type = 'Out' THEN -OpeningBal ELSE OpeningBal END) over (order by id)
END ClosingBal
FROM #t
Result:
ID OpeningBal Type ClosingBal
1 1000 - 0
2 100 IN 1100
3 200 OUT 900
4 100 OUT 800
5 300 IN 1100

How to split a numeric field into smaller segments in Sql Server

I have a table in SQL Server with two fields.
Total Group
35645 24
12400 55
30000 41
I want to split each group into smaller segments of fixed size 7000, with the remainder of each group into the last segment. So, the output should look like below.
Segment Total Group
1 7000 24
2 7000 24
3 7000 24
4 7000 24
5 7000 24
6 645 24
1 7000 55
2 5400 55
1 7000 41
2 7000 41
3 7000 41
4 7000 41
5 2000 41
This should do it:
declare #t table (Total int,[Group] int)
insert into #t(Total,[Group]) values
(35645,24 ),
(12400,55 ),
(30000,41 )
;With Numbers as (
select ROW_NUMBER() OVER (ORDER BY number)-1 n
from master..spt_values
)
select
n.n+1 as Segment,
CASE WHEN (n.n+1)*7000 < t.Total THEN 7000
ELSE t.Total - (n.n*7000) END as Total,
t.[Group]
from
#t t inner join
Numbers n on n.n*7000 < t.Total
(If you already have a Numbers table you can eliminate that part. I'm using spt_values just as a table that I know has plenty of rows in it, so that the ROW_NUMBER() expression should generate all of the necessary numbers)
Results:
Segment Total Group
-------------------- -------------------- -----------
1 7000 24
2 7000 24
3 7000 24
4 7000 24
5 7000 24
6 645 24
1 7000 55
2 5400 55
1 7000 41
2 7000 41
3 7000 41
4 7000 41
5 2000 41
I prepared following SELECT statement using SQL CTE expression and SQL numbers table function
declare #divisor int = 7000
;with CTE as (
select
Total,
[Group],
#divisor divisor,
(Total / #divisor) quotient,
(Total % #divisor) reminder
from t
), NT as (
SELECT i FROM dbo.NumbersTable(1, (select max(quotient) from CTE) ,1)
)
select
case when i = 0 then reminder else divisor end as Total,
[Group]
from (
select *
from CTE, NT
where quotient >= i
union all
select *, 0 as i
from CTE
where reminder >= 0
) t
order by [Group], i desc

Resources