SQL Loop to increment numbers - sql

I'm struggling coming up with a way to solve this answer. I want to start at a specific value and keep increasing it by 1 every time a new line.
For example, if I have a table like so.
90
93
110
87
130
Etc..
I want to select the number 87 and then keep incrementing up from there but also read if the incremented number is there and skip it.
I am just struggling with trying to put the right logic together in my head. I know I need a while loop to keep reading through the table but I can't think of the proper way to go about it. Just looking for some suggestions to push me in the right direction.
Edit: I am using T-SQL for MSFT SQL Server 2012.
Here is an example of what the output should look like
90
93
110
87
130
88
89
91
92
94
It would skip over adding 90 and 93 because they already exist in the table.
I hope that makes sense to you guys.

I do it all in one recursive CTE and I make it so you can use order by and guarantee your results are returned in the correct order.
For the recursion, you can either choose and start and end number or #desiredNumberOfNewValues(keep in mind, it doesn't account for repeats). Let me know if you have any questions or need anything else.
DECLARE #yourTable TABLE (nums INT);
INSERT INTO #yourTable
VALUES (90),(93),(110),(87),(130);
DECLARE #Specific_Number INT = 87;
DECLARE #Last_Number INT = 94;
DECLARE #DesiredNumberOfNewValues INT = 7;
WITH CTE_Numbers
AS
(
SELECT 1 AS order_id,nums, 1 AS cnt
FROM #yourTable
UNION ALL
SELECT 2,
CASE
WHEN #Specific_Number + cnt NOT IN (SELECT * FROM #yourTable) --if it's not already in the table, return it
THEN #Specific_Number + cnt
ELSE NULL -- if it is in the table, return NULL
END,
cnt + 1
FROM CTE_Numbers
WHERE nums = #Specific_Number
--OR (cnt > 1 AND #Specific_Number + cnt < #Last_Number) --beginning and end(option 1)
OR (cnt > 1 AND cnt <= #DesiredNumberOfNewValues) --number of new values(option 2)
)
SELECT order_id,nums
FROM CTE_Numbers
WHERE nums IS NOT NULL
ORDER BY order_id,nums
Results:
order_id nums
----------- -----------
1 87
1 90
1 93
1 110
1 130
2 88
2 89
2 91
2 92
2 94

You might be able to do this with some modifications by using Numbers table. In SQL Server, you could do the following. Assuming Test has the data. To create Numbers table, please refer to the following link.
http://dataeducation.com/you-require-a-numbers-table/
SELECT n.Number,t.ColB
FROM Numbers n
LEFT JOIN Test t
ON n.Number = t.ColA
Where n.Number < (SELECT MAX(COLA) FROM Test t)

WITH Numbers_CTE (Number)
AS
(
SELECT 1 AS Number
UNION ALL
SELECT Number+1 FROM Numbers_CTE
)
SELECT top 10 *
FROM Numbers_CTE n
LEFT JOIN <TABLE> t ON t.ID=n.Number
WHERE t.ID IS NULL
AND n.Number BETWEEN 5 AND 100

There is a system table you can use for numbers called master..spt_values.
DECLARE sample TABLE
DECLARE #tbl TABLE(Id INT)
INSERT statement
INSERT INTO #tbl VALUES (90)
,(93)
,(110)
,(87)
,(130)
You can easily make a UNION to remove duplicates value you have in your table and ORDER it within a CTE
DECLARE #number INT = 87
;WITH C AS(
SELECT Id, Row_Id, ROW_NUMBER() OVER(PARTITION BY Id ORDER BY Id) AS Rn FROM (
SELECT 1 AS Row_Id, Id FROM #tbl
UNION
SELECT 2 AS Row_Id, number
FROM master..spt_values
WHERE [type] = 'P'
AND number >= #number
) t
)
SELECT Id FROM C
WHERE Rn = 1
ORDER BY Row_Id, Id
Output
Id
----
87
90
93
110
130
88
89
91
92
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
111
112
113
114
.
.
.
SQLFiddle

Related

duplicate values in DB2

I am working on DB2.
There are duplicate rows in the table. I need to extract data where there is different value in 2nd column for same value in 1st column.
there are column other than these in table but only need to consider these.
for example:
NBR
SE_NBR
123
56
123
56
123
56
248
75
248
75
To extract
NBR
SE_NBR
456
12
456
34
456
11
135
77
135
88
here is one way using window function:
select * from
(
select *, min(SE_NBR) over (partition by NBR) minnbr, max(SE_NBR) over (partition by NBR) maxnbr
from nbrtable
) t
where minnbr <> maxnbr
if window function is not supported:
select * from tableName t1
where exists (
select 1 from tablename t2
where t1.NBR = t2.NBR
group by t2.nbr
having count(distinct SE_NBR) > 1
)
db<>fiddle here
You can do:
select *
from t
where nbr in (
select nbr from t group by nbr having count(distinct se_nbr) > 1
)

Update the Column values as grouped Sequence number based on input number provided in sql server

I want to update batch column here
ID CustId Batch
1 100 NULL
2 101 NULL
3 102 NULL
4 103 NULL
5 104 NULL
6 105 NULL
7 106 NULL
8 107 NULL
9 108 NULL
10 109 NULL
11 110 NULL
Based on the input number provided, lets say I have input 2.
Logic here is Divide the Number of records by input number. In this case it becomes 11/2 =5.5
The result should look like below
ID CustId Batch
1 100 1
2 101 1
3 102 2
4 103 2
5 104 3
6 105 3
7 106 4
8 107 4
9 108 5
10 109 5
11 110 6
Please suggest an sql query for this.
The below code will generate the required result.
The reason for taking #input as DECIMAL is, it will consider the decimal values and not just round of to the INT value.
DECLARE #input DECIMAL = 2;
WITH CTE AS (
SELECT t.ID, ROW_NUMBER() OVER (ORDER BY ID) AS rowNo
FROM Table t
)
UPDATE t
SET Batch = CEILING( c.rowNo / #input)
FROM Table t
INNER JOIN CTE c
ON t.ID = c.ID
To check the output before running Update
-- DECLARE #input DECIMAL = 2
-- SELECt t.*, CEILING( ROW_NUMBER() OVER (ORDER BY ID) / #input) as NewBatch
-- FROM
-- Table t
One method is:
select t.*,
ntile(ceiling(cnt / #n)) over (order by id) as batch
from (select t.*, count(*) over () as cnt
from t
) t;
Another method is:
select t.*,
floor( (row_number() over (order by id) - 1) / n) as batch
from t;

Removing pairs of transactions

I am attempting to remove transactions that have been reversed from a table. the table has Account, Date, Amount and Row. If a transaction has been reversed Account will match and Amount will be inverse of each other.
Example Table
Account Date Amount Row
12 1/1/18 45 72 -- Case 1
12 1/2/18 50 73
12 1/2/18 -50 74
12 1/3/18 52 75
15 1/1/18 51 76 -- Case 2
15 1/2/18 51 77
15 1/2/18 -51 78
15 1/2/18 51 79
18 1/2/18 50 80 -- Case 3
18 1/2/18 50 81
18 1/2/18 -50 82
18 1/2/18 -50 83
18 1/3/18 50 84
18 1/3/18 50 85
20 1/1/18 57 88 -- Case 4
20 1/2/18 57 89
20 1/4/18 -57 90
20 1/5/18 57 91
Desired Results Table
Account Date Amount Row
12 1/1/18 45 72 -- Case 1
12 1/3/18 52 75
15 1/1/18 51 76 -- Case 2
15 1/2/18 51 79
18 1/3/18 50 84 -- Case 3
18 1/3/18 50 85
20 1/1/18 57 88 -- Case 4
20 1/5/18 57 91
Removing all instances of inverse transactions does not work when there are multiple transactions when all other columns are the same. My attempt was to count all duplicate transactions, count of all inverse duplicate transactions, subtracting those to get the number of rows I needed from each transactions group. I was going to pull the first X rows but found in most cases I want the last X rows of each group, or even a mix (the first and last in Case 2).
I either need a method of removing pairs from the original table, or working from what I have so far, a method of distinguishing which transactions to pull.
Code so far:
--adding row Numbers
with a as (
select
account a,
date d,
amount f,
row_number() over(order by account, date) r
from table),
--counting Duplicates
b as (
select a.a, a.f, Dups
from a join (
select a, f, count(*) Dups
from a
group by a.a, a.f
having count(*)>1
) b
on a.a=b.a and
b.f=a.f
where a.f>0
),
--counting inverse duplicates
c as (
select a.a, a.f, InvDups
from a join (
select a, f, count(*) InvDups
from a
group by a.a, a.f
having count(*)>1
) b
on a.a=b.a and
-b.f=a.f
where a.f>0
),
--combining c and d to get desired number of rows of each transaction group
d as (
select
b.a, b.f, dups, InvDups, Dups-InvDups TotalDups
from b join c
on b.a=c.a and
b.f=c.f
),
--getting the number of rows from the beginning of each transaction group
select d.a, d.d, d.f
from
(select
a, d, f, row_number() over (group by a, d, f) r2
from a) e
join d
on e.a=d.a and
TotalDups<=r2
You can try this.
SELECT T_P.* FROM
( SELECT *, ROW_NUMBER() OVER(PARTITION BY Account, Amount ORDER BY [Row] ) RN from #MyTable WHere Amount > 0 ) T_P
LEFT JOIN
( SELECT *, ROW_NUMBER() OVER(PARTITION BY Account, Amount ORDER BY [Row] ) RN from #MyTable WHere Amount < 0 ) T_N
ON T_P.Account = T_N.Account
AND T_P.Amount = ABS(T_N.Amount)
AND T_P.RN = T_N.RN
WHERE
T_N.Account IS NULL
The following handles your three cases:
with t as (
select t.*,
row_number() over (partition by account, date, amount order by row) as seqnum
from table t
)
select t.*
from t
where not exists (select 1
from t t2
where t2.account = t.account and t2.date = t.date and
t2.amount = -t.amount and t2.seqnum = t.seqnum
);
Use This
;WITH CTE
AS
(
SELECT
[Row]
FROM YourTable YT
WHERE Amount > 0
AND EXISTS
(
SELECT 1 FROM YourTable WHERE Account = YT.Account
AND [Date] = YT.[Date]
AND (Amount+YT.Amount)=0
)
UNION ALL
SELECT
[Row]
FROM YourTable YT
WHERE Amount < 0
AND EXISTS
(
SELECT 1 FROM YourTable WHERE Account = YT.Account
AND [Date] = YT.[Date]
AND (Amount+YT.Amount)>0
)
)
SELECT * FROM YourTable
WHERE EXISTS
(
SELECT 1 FROM CTE WHERE [Row] = YourTable.[Row]
)

How to compute the diff between records?

My table records is like below
ym cnt
200901 57
200902 62
200903 67
...
201001 84
201002 75
201003 75
...
201101 79
201102 77
201103 80
...
I want to computer the diff between current month and per month .
the result would like below ...
ym cnt diff
200901 57 57
200902 62 5 (62 - 57)
200903 67 5 (67 - 62)
...
201001 84 ...
201002 75
201003 75
...
201101 79
201102 77
201103 80
...
Can anyone told me how to wrote a sql to got the result and with a good performance ?
UPDATE:
sorry for simple words
my solution is
step1: input the currentmonth data into temp table1
step2: input the permonth data into temp table2
step3: left join 2 tables to compute the result
Temp_Table1
SELECT (ym - 1) as ym , COUNT( item_cnt ) as cnt
FROM _table
GROUP BY (ym - 1 )
order by ym
Temp_Table2
SELECT ym , COUNT( item_cnt ) as cnt
FROM _table
GROUP BY ym
order by ym
select ym , (b.cnt - a.cnt) as diff from Temp_Table2 a
left join Temp_Table1 b
on a.ym = b.ym
*If i want to compare the diff between the month in this year and last year
I can only change the ym - 1 to ym - 100*
but , actually , the group by key is not only ym
there is max 15 keys and max 100 millions records
so , I wonder a good solution can easy to manager the source
and good performance.
For MSSQL, this has one reference to the table, so potentially it can be faster (maybe not) than left join which has two references to the table:
-- ================
-- sample data
-- ================
declare #t table
(
ym varchar(6),
cnt int
)
insert into #t values ('200901', 57)
insert into #t values ('200902', 62)
insert into #t values ('200903', 67)
insert into #t values ('201001', 84)
insert into #t values ('201002', 75)
insert into #t values ('201003', 75)
-- ===========================
-- solution
-- ===========================
select
ym2,
diff = case when cnt1 is null then cnt2
when cnt2 is null then cnt1
else cnt2 - cnt1
end
from
(
select
ym1 = max(case when k = 2 then ym end),
cnt1 = max(case when k = 2 then cnt end),
ym2 = max(case when k = 1 then ym end),
cnt2 = max(case when k = 1 then cnt end)
from
(
select
*,
rn = row_number() over(order by ym)
from #t
) t1
cross join
(
select k = 1 union all select k = 2
) t2
group by rn + k
) t
where ym2 is not null
Can anyone told me how to wrote a sql to got the result
Absolutely. Simply get the row with the next highest date, and subtract.
and with a good performance ?
No. Relational databases are not really meant to be traversed linearly, and even using indexes appropriately would require a virtual linear traversal.

SQL Server query for finding the sum of 4 consecutive values

Can somebody help me in finding the sum of 4 consecutive values i.e rolling sum of last 4 values.
Like:
VALUE SUM
1 NULL
2 NULL
3 NULL
4 10
5 14
6 18
7 22
8 26
9 30
10 34
11 38
12 42
13 46
14 50
15 54
16 58
17 62
18 66
19 70
20 74
21 78
22 82
23 86
24 90
25 94
26 98
27 102
28 106
29 110
30 114
31 118
32 122
33 126
34 130
35 134
36 138
37 142
38 146
Thanks,
select sum(select top 4 Value from [table] order by Value Desc)
or, perhaps
select sum(value)
from [Table]
where Value >= (Max(Value) - 4)
I haven't actually tried either of those- and can't at the moment, but they should get you pretty close.
Quick attempt, which gets the results you've posted in your question (except the 1st 3 rows are not NULL). Assumes that VALUE field is unique and in ascending order:
-- Create test TABLE with 38 values in
DECLARE #T TABLE (Value INTEGER)
DECLARE #Counter INTEGER
SET #Counter = 1
WHILE (#Counter <= 38)
BEGIN
INSERT #T VALUES(#Counter)
SET #Counter = #Counter + 1
END
-- This gives the results
SELECT t1.VALUE, x.Val
FROM #T t1
OUTER APPLY(SELECT SUM(VALUE) FROM (SELECT TOP 4 VALUE FROM #T t2 WHERE t2.VALUE <= t1.VALUE ORDER BY t2.VALUE DESC) x) AS x(Val)
ORDER BY VALUE
At the very least, you should see the kind of direction I was heading in.
Assuming ID can give you the last 4 rows.
SELECT SUM([SUM])
FROM
(
SELECT TOP 4 [SUM] FROM myTable ORDER BY ID DESC
) foo
Each time you query it, it will read the last 4 rows.
If this is wrong (e.g. you want the sum of each consecutive 4 rows), then please give sample output
Following would work if your Value column is sequential
;WITH q (Value) AS (
SELECT 1
UNION ALL
SELECT q.Value + 1
FROM q
WHERE q.Value < 38
)
SELECT q.Value
, CASE WHEN q.Value >= 4 THEN q.Value * 4 - 6 ELSE NULL END
FROM q
otherwise you might use something like this
;WITH q (Value) AS (
SELECT 1
UNION ALL
SELECT q.Value + 1
FROM q
WHERE q.Value < 38
)
, Sequential (ID, Value) AS (
SELECT ID = ROW_NUMBER() OVER (ORDER BY Value)
, Value
FROM q
)
SELECT s1.Value
, [SUM] = s1.Value + s2.Value + s3.Value + s4.Value
FROM Sequential s1
LEFT OUTER JOIN Sequential s2 ON s2.ID = s1.ID - 1
LEFT OUTER JOIN Sequential s3 ON s3.ID = s2.ID - 1
LEFT OUTER JOIN Sequential s4 ON s4.ID = s3.ID - 1
Note that the table qin the examples is a stub for your actual table. The actual statement then becomes
;WITH Sequential (ID, Value) AS (
SELECT ID = ROW_NUMBER() OVER (ORDER BY Value)
, Value
FROM YourTable
)
SELECT s1.Value
, [SUM] = s1.Value + s2.Value + s3.Value + s4.Value
FROM Sequential s1
LEFT OUTER JOIN Sequential s2 ON s2.ID = s1.ID - 1
LEFT OUTER JOIN Sequential s3 ON s3.ID = s2.ID - 1
LEFT OUTER JOIN Sequential s4 ON s4.ID = s3.ID - 1