Add some specific value in each digit of number in SQL [closed] - sql-server

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 4 years ago.
Improve this question
I need the formula for calculating some number
(1) Add 3 in each digit of the particular number
(2) If we get the result after addition in two digits like 10,11,12 etc then consider only last digit
For example if number would be 564843530 then enter code here
New number would be 897176023.

Solution:
DECLARE #number int
SET #number = 564843530;
WITH DigitsTable AS
(
SELECT 0 AS Power10, ABS(#Number) AS Number
UNION ALL
SELECT Power10 + 1, Number / 10
FROM DigitsTable
WHERE Number > 10
)
SELECT SUM((((Number % 10) + 3) % 10) * POWER(10, Power10)) AS NewNumber
FROM DigitsTable
OPTION (MAXRECURSION 0);
Output:
897176863
Explanation:
This answer is based on one recursive CTE which returns each current digit, new digit and multiplier for calculation:
DECLARE #number int
SET #number = 564843530;
WITH DigitsTable AS
(
SELECT 0 AS Power10, ABS(#Number) AS Number
UNION ALL
SELECT Power10 + 1, Number / 10
FROM DigitsTable
WHERE Number > 10
)
SELECT
Power10,
Number % 10 AS Digit,
((Number % 10) + 3) % 10 AS NewDigit,
POWER(10, Power10) AS Multiplier
FROM DigitsTable
OPTION (MAXRECURSION 0);
Output from recursive CTE:
Power10 Digit NewDigit Multiplier
0 0 3 1
1 3 6 10
2 5 8 100
3 3 6 1000
4 4 7 10000
5 8 1 100000
6 4 7 1000000
7 6 9 10000000
8 5 8 100000000

Ty this:
SELECT
(
SELECT RIGHT(SUBSTRING(DS.[number], v.[number] + 1, 1) + 3, 1)
FROM
(
SELECT '564843530'
) DS ([number])
INNER JOIN [master]..spt_values v on v.[number] < LEN(DS.[number])
WHERE V.[type] = 'P'
ORDER BY v.[number]
FOR XML PATH(''), TYPE
).value('.', 'VARCHAR(MAX)');
The algorithm is simple:
split the string to chars (in your case numbers)
add 3 to each number
using RIGHT get the first right char only
concatenate the numbers
This should work on older editions. If you are using SQL Server 2016 you can use functions like STRING_SPLIT and STRING_AGG in SQL Server 2017.
For older version you can look for SQL CLR function in order to write your own functions for splitting and concatenating. For example, in my system I can do:
Check the following link if interested in SQL CLR.

Assume that the example value supplied by the OP is wrong (as, per my comment "Shouldn't 564843530 become 897176863? How is 5 + 3 = (1)0? and 3 + 3 = 2?"), then this is my method, which makes use of NGrams8k:
WITH VTE AS (
SELECT 564843530 AS SomeNumber),
Split AS(
SELECT V.SomeNumber,
NG.token,
NG.position
FROM VTE V
CROSS APPLY dbo.NGrams8k(V.SomeNumber,1) NG)
SELECT SomeNumber AS OldNumber,,
CONVERT(bigint,(SELECT token + 3
FROM Split x
WHERE S.SomeNumber = x.SomeNumber
ORDER BY x.position
FOR XML PATH(''))) AS NewNumber
FROM Split S
GROUP BY S.SomeNumber;

Try this
DECLARE #n int, #inc int;
SELECT #n = 564843530, #inc = 3;
WITH t1(n, m, k, p)
AS (
SELECT #n AS n, CAST(0 AS int) AS m, CAST(0 AS int) AS k, CAST(0 AS bigint) AS p
UNION ALL
SELECT n / 10 AS n, n - round(n, -1, 1) AS m, k + 1 AS k, CAST(power(10, k) AS bigint) AS p
FROM t1 WHERE n > 0
)
SELECT SUM((CASE WHEN m > 10 - #inc THEN m - 10 + #inc ELSE m + #inc END) * p)
FROM t1
Result
897176863

Related

Increasing a Column value by a % range

I have a table in SQL Server 2012. The following query works great:
SELECT TOP 300 [ObjectID], [tbh_Objects].Title, [Discount], [tbh_Section].Title
FROM [ECom].[dbo].[tbh_Objects]
INNER JOIN [tbh_Section] ON tbh_Objects.SectionID = tbh_Section.SectionID
ORDER BY tbh_Objects.AddedDate DESC
I want to fire a query which increases the discount value to a random % in the range of 5-10 for all 300 rows at once. So for eg: If DIscount of ObjectID=500 is 30, and the random value between 5 and 10 is "6", I want it to become 30+6%of30 for ObjectID=500.
Similarly for Object ID=230, let's say discount is 20 and the random value is 8, I want it as 20+8%of20.
The end result of the Discount should always be a whole number and not a decimal, so automatically rounds off.
Is this possible in SQL Server? How?
You need random integers and a Modulus (%) operator. A possible approach to generate a random integers is using a combination of NEWID() and CHECKSUM() functions. The following simplified example is a possible solution to your problem:
SELECT
Discount,
RandomPercent,
CONVERT(int, (Discount * (100.0 + RandomPercent) / 100)) AS NewDiscount
FROM (
SELECT Discount, (ABS(CHECKSUM(NEWID()) % 6) + 5) AS RandomPercent
FROM (VALUES (30), (20), (50), (70), (11), (21), (13), (15), (1), (6)) v (Discount)
) t
Result:
Discount RandomPercent NewDiscount
----------------------------------
30 7 32
20 5 21
50 6 53
70 10 77
11 9 11
21 9 22
13 8 14
15 10 16
1 6 1
6 5 6
If you need an UPDATE statement:
;WITH UpdateCTE AS (
SELECT TOP 300 o.[Discount]
FROM [ECom].[dbo].[tbh_Objects] o
INNER JOIN [tbh_Section] s ON o.SectionID = s.SectionID
ORDER BY o.AddedDate DESC
)
UPDATE UpdateCTE
SET [Discount] = CONVERT(int, (o.[Discount] * (100.0 + (ABS(CHECKSUM(NEWID()) % 6) + 5)) / 100))
If you want to round the new discounts before the integer conversion, use ROUND():
SET [Discount] = CONVERT(
int,
ROUND(o.[Discount] * (100.0 + (ABS(CHECKSUM(NEWID()) % 6) + 5)) / 100, 0)
)

Transact-SQL - number rows until condition met

I'm trying to generate the numbers in the "x" column considering the values in field "eq", in a way that it should assign a number for every record until it meets the value "1", and the next row should reset and start counting again. I've tried with row_number, but the problem is that I only have ones and zeros in the column I need to evaluate, and the cases I've seen using row_number were using growing values in a column. Also tried with rank, but I haven't managed to make it work.
nInd Fecha Tipo #Inicio #contador_I #Final #contador_F eq x
1 18/03/2002 I 18/03/2002 1 null null 0 1
2 20/07/2002 F 18/03/2002 1 20/07/2002 1 1 2
3 19/08/2002 I 19/08/2002 2 20/07/2002 1 0 1
4 21/12/2002 F 19/08/2002 2 21/12/2002 2 1 2
5 17/03/2003 I 17/03/2003 3 21/12/2002 2 0 1
6 01/04/2003 I 17/03/2003 4 21/12/2002 2 0 2
7 07/04/2003 I 17/03/2003 5 21/12/2002 2 0 3
8 02/06/2003 F 17/03/2003 5 02/06/2003 3 0 4
9 31/07/2003 F 17/03/2003 5 31/07/2003 4 0 5
10 31/08/2003 F 17/03/2003 5 31/08/2003 5 1 6
11 01/09/2005 I 01/09/2005 6 31/08/2003 5 0 1
12 05/09/2005 I 01/09/2005 7 31/08/2003 5 0 2
13 31/12/2005 F 01/09/2005 7 31/12/2005 6 0 3
14 14/01/2006 F 01/09/2005 7 14/01/2006 7 1 4
There is another solution available:
select
nind, eq, row_number() over (partition by s order by s)
from (
select
nind, eq, coalesce((
select sum(eq) +1 from mytable pre where pre.nInd < mytable.nInd)
,1) s --this is the sum of eq!
from mytable) g
The inner subquery creates groups sequentially for each occurrence of 1 in eq. Then we can use row_number() over partition to get our counter.
Here is an example using Sql Server
I have two answers here. One is based off of the ROW_NUMBER() and the other is based off of what appears to be your index (nInd). I wasn't sure if there would be a gap in your index so I made the ROW_NUMBER() as well.
My table format was as follows -
myIndex int identity(1,1) NOT NULL
number int NOT NULL
First one is ROW_NUMBER()...
WITH rn AS (SELECT *, ROW_NUMBER() OVER (ORDER BY myIndex) AS rn, COUNT(*) AS max
FROM counting c GROUP BY c.myIndex, c.number)
,cte (myIndex, number, level, row) AS (
SELECT r.myIndex, r.number, 1, r.rn + 1 FROM rn r WHERE r.rn = 1
UNION ALL
SELECT r1.myIndex, r1.number,
CASE WHEN r1.number = 0 AND r2.number = 1 THEN 1
ELSE c.level + 1
END,
row + 1
FROM cte c
JOIN rn r1
ON c.row = r1.rn
JOIN rn r2
ON c.row - 1 = r2.rn
)
SELECT c.myIndex, c.number, c.level FROM cte c OPTION (MAXRECURSION 0);
Now the index...
WITH cte (myIndex, number, level) AS (
SELECT c.myIndex + 1, c.number, 1 FROM counting c WHERE c.myIndex = 1
UNION ALL
SELECT c1.myIndex + 1, c1.number,
CASE WHEN c1.number = 0 AND c2.number = 1 THEN 1
ELSE c.level + 1
END
FROM cte c
JOIN counting c1
ON c.myIndex = c1.myIndex
JOIN counting c2
ON c.myIndex - 1 = c2.myIndex
)
SELECT c.myIndex - 1 AS myIndex, c.number, c.level FROM cte c OPTION (MAXRECURSION 0);
The answer that I have now is via using
Cursor
I know if there is another solution without cursor it will be better for performance aspects
here is a quick demo of my solution:
-- Create DBTest
use master
Go
Create Database DBTest
Go
use DBTest
GO
-- Create table
Create table Tabletest
(nInd int , eq int)
Go
-- insert dummy data
insert into Tabletest (nInd,eq)
values (1,0),
(2,1),
(3,0),
(4,1),
(5,0),
(6,0),
(7,0),
(8,0),
(9,1),
(8,0),
(9,1)
Create table #Tabletest (nInd int ,eq int ,x int )
go
DECLARE #nInd int , #eq int , #x int
set #x = 1
DECLARE db_cursor CURSOR FOR
SELECT nInd , eq
FROM Tabletest
order by nInd
OPEN db_cursor
FETCH NEXT FROM db_cursor INTO #nInd , #eq
WHILE ##FETCH_STATUS = 0
BEGIN
if (#eq = 0)
begin
insert into #Tabletest (nInd ,eq ,x) values (#nInd , #eq , #x)
set #x = #x +1
end
else if (#eq = 1)
begin
insert into #Tabletest (nInd ,eq ,x) values (#nInd , #eq , #x)
set #x = 1
end
FETCH NEXT FROM db_cursor INTO #nInd , #eq
END
CLOSE db_cursor
DEALLOCATE db_cursor
select * from #Tabletest
The end result set will be as following:
Hope it helps.
Looking at this a slightly different way (which might not be true, but eliminates the need for cursors of recursive CTEs), it looks like you building ordered groups within your dataset. So, start by finding those groups, then determining the ordering of each of them.
The real key is to determine the rules to find the correcting grouping. Based on your description and comments, I'm guessing the grouping is from the start (ordered by the nInd column) ending at each row with and eq value of 1, so you can do something like:
;with ends(nInd, ord) as (
--Find the ending row for each set
SELECT nInd, row_number() over(order by nInd)
FROM mytable
WHERE eq=1
), ranges(sInd, eInd) as (
--Find the previous ending row for each ending row, forming a range for the group
SELECT coalesce(s.nInd,0), e.nInd
FROM ends s
right join ends e on s.ord=e.ord-1
)
Then, using these group ranges, you can find the final ordering of each:
select t.nInd, t.Fecha, t.eq
,[x] = row_number() over(partition by sInd order by nInd)
from ranges r
join mytable t on r.sInd < t.nInd
and t.nInd <= r.eInd
order by t.nInd

How do I construct a VARBINARY(MAX) from a byte value with LEN = 1?

Please, observe:
DECLARE #b VARBINARY(MAX) = 5
SELECT #b Value, LEN(#b) Length
Yields
Value Length
0x00000005 4
What I need is a VARBINARY(MAX) instance that equals 5, but has the length of 1.
Motivation:
I would like to generate the following sequence of VARBINARY values:
0x00
0x80
0x8080
0x808080
0x80808080
0x8080808080
0x808080808080
...
With an arbitrary depth given as a parameter. Here is my code:
DECLARE #Depth INT = 50
;WITH number AS (
SELECT TOP (#Depth - 1) ROW_NUMBER() OVER (ORDER BY object_id) - 1 n
FROM sys.objects
), mask AS (
SELECT n, CAST(0 AS VARBINARY(MAX)) mask
FROM number
WHERE n = 0
UNION ALL
SELECT mask.n + 1 n, CAST(mask.mask + CAST(0x80 AS VARBINARY) AS VARBINARY(MAX)) mask
FROM number
JOIN mask ON number.n = mask.n
)
SELECT n,mask FROM mask
And I am almost there, only the result is:
n mask
0 0x00000000
1 0x0000000080
2 0x000000008080
3 0x00000000808080
4 0x0000000080808080
5 0x000000008080808080
6 0x00000000808080808080
7 0x0000000080808080808080
8 0x000000008080808080808080
9 0x00000000808080808080808080
10 0x0000000080808080808080808080
...
So, my problem is the length - it is 4 bytes minimum. I need it to start from 1.
EDIT 1
Found the answer to my primary problem:
DECLARE #Depth INT = 50
;WITH number AS (
SELECT TOP (#Depth - 1) ROW_NUMBER() OVER (ORDER BY object_id) - 1 n
FROM sys.objects
), mask AS (
SELECT n, CAST(0x80 AS VARBINARY(MAX)) mask
FROM number
WHERE n = 1
UNION ALL
SELECT mask.n + 1 n, CAST(mask.mask + CAST(0x80 AS VARBINARY) AS VARBINARY(MAX)) mask
FROM number
JOIN mask ON number.n = mask.n
)
SELECT 0 n,CAST(0 AS VARBINARY(1)) mask
UNION ALL
SELECT n,mask FROM mask
But I do not understand why it does not have the same problem as my original solution. Why do the VARBINARY values now have the minimum length of 1 and not 4 as before?
The literal 5 is of type INT, which is 4 bytes. The literal 0x80(mind the 0x!) is of type BINARY(1), which is 1 byte. If you had written
DECLARE #b VARBINARY(MAX) = 0x5;
You would have achieved the desired result.

How to insert seven kind of ids in a column to all values

I have column data. I need to insert ids in another column. Total i have 7 ids. For first 7 values i have to insert these ids and next 7 values, i have to insert same ids and so on.. Can any one please help?
Pay_headID Pay_amount
16414 8000
16415 300
16416 0
16417 200
16418 500
16419 0
16420 0
16414 9000
16415 300
so on ...
you can use CTE and ROW_NUMBER, i have used ordering by Pay_headId:
WITH cte_myTable
AS (SELECT
*,
(ROW_NUMBER() OVER (ORDER BY Pay_headID)) - 1 AS num
FROM myTable)
UPDATE cte_myTable
SET [Pay_headID] =
CASE
WHEN num % 7 = 0 THEN 16414
WHEN num % 7 = 1 THEN 16415
WHEN num % 7 = 2 THEN 16416
WHEN num % 7 = 3 THEN 16417
WHEN num % 7 = 4 THEN 16418
WHEN num % 7 = 5 THEN 16419
WHEN num % 7 = 6 THEN 16420
END
GO
If you want use ordering on how it was inserted, you can set Pay_headIds to null:
update myTable set Pay_headID=null;
You should use RowNum() to give you an artificial incrementing number, divide it by 7 and then Round it.
SELECT FLOOR((ROW_NUMBER() OVER(ORDER BY Pay_HeadID DESC))/7) AS MyID
to get your ids

Split a string without delimiter in TSQl

I have a result set from SELECT Statement, how can i split one column without any delimiter
this is my result
Size TCount TDevice
2 5 E01
4.5 3 W02E01
I want to have this
Size TCount TDevice
2 5 E
2 5 0
2 5 1
4.5 3 W
4.5 6 0 (we have 2 times of 0)
4.5 3 2
4.5 3 1
thank you
You can join onto an auxiliary numbers table. I am using spt_values for demo purposes but you should create a permanent one.
WITH Nums
AS (SELECT number
FROM master..spt_values
WHERE type = 'P'
AND number BETWEEN 1 AND 1000),
Result(Size, TCount, TDevice)
AS (SELECT 2, 5,'E01'
UNION ALL
SELECT 4.5,3,'W02E01')
SELECT Size,
COUNT(*) * TCount AS TCount,
SUBSTRING(TDevice, number, 1) AS TDevice
FROM Result
JOIN Nums
ON Nums.number <= LEN(TDevice)
GROUP BY Size,
TCount,
SUBSTRING(TDevice, number, 1)
;with cte as
(
select Size,TCount,
substring(TDevice, 1, 1) as Chars,
stuff(TDevice, 1, 1, '') as TDevice
from t1
union all
select Size,TCount,
substring(TDevice, 1, 1) as Chars,
stuff(TDevice, 1, 1, '') as TDevice
from cte
where len(TDevice) > 0
)
select distinct Size,sum(TCount),Chars
from cte
group by Size,Chars
SQL Fiddle
Advantage: It doesn't require any User defined function (UDF) to be created.

Resources