How to select only armstrong numbers from the list? - sql

I want is to select Armstrong numbers from the list below list I have searched of solution of this question bu unable to find in SQL-Server:
Numbers
121
113
423
153
541
371
I am sure most of you know what's the Armstrong number and how to calculate though I am describing is for the simplicity : sum of the cubes of its digits is equal to the number itself i.e.
1*1*1 + 5*5*5 + 3*3*3 = 153
3*3*3 + 7*7*7 + 1*1*1 = 371
Please help me on this as I am also trying but seeking for quick solution. It will be very helpful to me. Thanks in advance.

Obviously static processing during each query is not correct approach but we can create function like this and
create function dbo.IsArmstrongNumber(#n int)
returns int as
begin
declare #retValue int = 0
declare #sum int = 0
declare #num int = #n
while #num > 0
begin
set #sum += (#num%10) * (#num%10) * (#num%10)
set #num = #num/10
end
IF #sum = #n
set #retValue = 1
return #retValue
end
Pre-processing and selecting in IN clause is better
select * from #Numbers where dbo.IsArmstrongNumber(n) = 1

select 153 x into #temp;
insert #temp values(371);
insert #temp values(541);
with cte as (select x, substring(cast(x as nvarchar(40)) ,1,1) as u, 1 as N FROM #temp
union all
select x, substring(cast(x as nvarchar(40)),n+1,1) as u , n+1 from cte where len(cast(x as nvarchar(40))) > n
)
select x from cte group by x having SUM(POWER(cast(u as int),3)) = x
drop table #temp;
here is the mark 2 - you can change the #ORDER to explore power of 4,5 etc
declare #order int = 3;
declare #limit int = 50000;
with nos as (select 1 no
union all
select no + 1 from nos where no < #limit),
cte as (select no as x, substring(cast(no as nvarchar(40)) ,1,1) as u, 1 as N FROM nos
union all
select x, substring(cast(x as nvarchar(40)),n+1,1) as u , n+1 from cte where len(cast(x as nvarchar(40))) > n
)
select x from cte group by x having SUM(POWER(cast(u as int),#order)) = x
option (maxrecursion 0);

This is a quick mod to my sum of digits UDF
Declare #Table table (Numbers int)
Insert into #Table values
(121),
(113),
(423),
(153),
(541),
(371)
Select * from #Table where [dbo].[udf-Stat-Is-Armstrong](Numbers)=1
Returns
Numbers
153
371
The UDF
CREATE Function [dbo].[udf-Stat-Is-Armstrong](#Val bigint)
Returns Bit
As
Begin
Declare #RetVal as bigint
Declare #LenInp as bigint = len(cast(#Val as varchar(25)))
;with i AS (
Select #Val / 10 n, #Val % 10 d
Union ALL
Select n / 10, n % 10
From i
Where n > 0
)
Select #RetVal = IIF(SUM(power(d,#LenInp))=#Val,1,0) FROM i;
Return #RetVal
End

You can use the following to find Armstrong numbers using Sql functions:
WITH Numbers AS(
SELECT 0 AS number UNION ALL SELECT number + 1 FROM Numbers WHERE number < 10000)
SELECT number AS ArmstrongNumber FROM Numbers
WHERE
number = POWER(COALESCE(SUBSTRING(CAST(number AS VARCHAR(10)),1,1),0),3)
+ POWER(COALESCE(SUBSTRING(CAST(number AS VARCHAR(10)),2,1),0),3)
+ POWER(COALESCE(SUBSTRING(CAST(number AS VARCHAR(10)),3,1),0),3)
OPTION(MAXRECURSION 0)

Related

Generating a list of random numbers, summing to a fixed amount using SQL

This question is about generating N random numbers whose sum is M(constant) using SQL.
For example we have a number M=9.754. We need 10 random numbers whose sum is 9.754.
It should be done in SQL Server environment.
Can anybody help me?
While the #Squirrel answer is interesting but numbers here is more random
here is the code:
DECLARE #s INT=1,
#k FLOAT=0,
#final FLOAT=9.917,
#sum FLOAT =0,
#min FLOAT=1,
#max FLOAT=9.917
BEGIN
WHILE (#sum <> #final)
BEGIN
WHILE (#s <= 10)
BEGIN
SET #k =
(
SELECT ROUND(RAND(CHECKSUM(NEWID())) * (#max - #min) + #min,3)
);
PRINT (CONCAT('random: ',#k));
IF(#sum+#k <=#final)
SET #sum+=#k;
SET #max=#final-#sum;
PRINT (CONCAT('computed sum: ',#k));
IF(#max>1) SET #min=1 ELSE SET #min=0;
IF(#sum=#final)
BREAK;
SET #s = #s + 1;
SET #k = #k + 0;
END;
PRINT (CONCAT('final', #final))
PRINT (CONCAT('sum', #sum))
IF(#sum<>#final)--force stop if after 10 try the sum not match with final
BEGIN
PRINT(CONCAT('final random number:',#final-#sum))
SET #sum=#final;
END;
SET #s=0;
IF(#sum=#final)
BEGIN
PRINT('****************************DONE****************************')
BREAK;
END
END;
PRINT ('end');
END;
Interesting requirement.
Query below uses tally table / number table to generate 10 random numbers after that find the ratio. Final query check for case where sum of the numbers is not equal to #m and make the adjustment on the biggest number.
declare #m decimal(10,3) = 9.754,
#n int = 10;
with
-- using recursive CTE to generate a number table
numbers as
(
select n = 1
union all
select n = n + 1
from numbers
where n < #n
),
-- generate random positive numbers using newid()
-- Note : 100 is chosen arbitrary
num as
(
select n = abs(checksum(newid())) % 100
from numbers
),
-- calculate the ratio
ratio as
(
select r,
rn = row_number() over (order by r desc),
sum_r = sum(r) over()
from
(
select r = convert(decimal(10,3), n * 1.0 / sum(n) over() * #m)
from num
) r
)
-- sum(r) may not equal to #m due to rounding
-- find the difference and adjust it to the biggest r
select r, rn, sum_r,
adj_r = r + case when rn = 1 then #m - sum_r else 0 end,
sum_adj_r = sum(r + case when rn = 1 then #m - sum_r else 0 end) over()
from ratio
dbfiddle demo

What is the easiest way to print all prime numbers until n, in SQL?

What kind of algorithm should I use, for printing all prime numbers until let's say 1000?
SQL Server is preferred.
Thanks
You could use this to list all primes that smaller than 1000 in SQL
;WITH temp AS
(
SELECT 2 AS Value
UNION ALL
SELECT t.Value+1 AS VAlue
FROM temp t
WHERE t.Value < 1000
)
SELECT *
FROM temp t
WHERE NOT EXISTS
( SELECT 1 FROM temp t2
WHERE t.Value % t2.Value = 0
AND t.Value != t2. Value
)
OPTION (MAXRECURSION 0)
Demo link: Rextester
This code I've written for SQL Server is very fast, it will find all the primes less than 10 million in about 12 seconds:
DECLARE #Min int = 2, #Max int = 100000
--
IF OBJECT_ID('tempdb..#N','U') IS NOT NULL DROP TABLE #N
--
CREATE TABLE #N(N int NOT NULL, SqrtN int NOT NULL);
--
WITH L0 AS (SELECT 'Anything' N FROM (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) AS T(N)), -- 16 values
L1 AS (SELECT A.N FROM L0 A, L0 B, L0 C, L0 D, L0 E, L0 F, L0 G, L0 H), -- 15^8 values (2562890625 more than enough for max value of int (2^31-1)
L2 AS (SELECT TOP(#Max/6) CONVERT(int,6*ROW_NUMBER() OVER (ORDER BY (SELECT NULL))) RowNum FROM L1)
INSERT INTO #N(N, SqrtN)
SELECT T.N, SQRT(N)
FROM L2
CROSS APPLY(VALUES(L2.RowNum-1),(L2.RowNum+1)) T(N)
WHERE T.N BETWEEN #Min AND #Max
AND 0 NOT IN (N%5,N%7,N%11,N%13,N%17,N%19,N%23,N%29,N%31,N%37,N%41,N%43,N%47,N%53,N%59) -- Not interested in anything dividable by these low primes
--
ALTER TABLE #N ADD PRIMARY KEY CLUSTERED(N) WITH FILLFACTOR = 100
--
IF OBJECT_ID('tempdb..#Primes','U') IS NOT NULL DROP TABLE #Primes
--
SELECT Z.N Prime
FROM (SELECT N FROM (VALUES(2),(3),(5),(7),(11),(13),(17),(19),(23),(29),(31),(37),(41),(43),(47),(53),(59)) T(N)
WHERE T.N BETWEEN #Min AND #Max
UNION ALL
SELECT X.N
FROM #N AS X
WHERE NOT EXISTS(SELECT *
FROM #N AS C
WHERE C.N <= X.SqrtN
AND 0 = X.N%C.N)) Z
ORDER BY 1
The answer is simple:
Assuming you already have following table populated with data:
CREATE TABLE dbo.PrimeNumber(Num INT NOT NULL PRIMARY KEY);
you'll need only a simple SELECT:
SELECT * FROM dbo.PrimeNumber
This is my answer
I did it in SQL Server
DECLARE #number INT = 4
DECLARE #isprime INT = 0
DECLARE #counter INT
DECLARE #result VARCHAR(MAX) = '2&3&'
WHILE (#number <= 1000)
BEGIN
SET #counter = 2
WHILE (#counter <= CAST(SQRT(#number) as INT))
BEGIN
IF (#number % #counter = 0)
BEGIN
SET #isprime = 0
Break
END
ELSE
BEGIN
SET #isprime = 1
SET #counter += 1
END
END
IF #isprime = 1
SET #result += CAST(#number as VARCHAR(6)) + '&'
SET #number += 1
END
PRINT(LEFT(#result, len(#result)-1))
This is the output
2&3&5&7&11&13&17&19&23&29&31&37&41&43&47&53&59&61&67&71&73&79&83&89&97&101&103&107&109&113&127&131&137&139&149&151&157&163&167&173&179&181&191&193&197&199&211&223&227&229&233&239&241&251&257&263&269&271&277&281&283&293&307&311&313&317&331&337&347&349&353&359&367&373&379&383&389&397&401&409&419&421&431&433&439&443&449&457&461&463&467&479&487&491&499&503&509&521&523&541&547&557&563&569&571&577&587&593&599&601&607&613&617&619&631&641&643&647&653&659&661&673&677&683&691&701&709&719&727&733&739&743&751&757&761&769&773&787&797&809&811&821&823&827&829&839&853&857&859&863&877&881&883&887&907&911&919&929&937&941&947&953&967&971&977&983&991&997
with prime
as
(
select 1 as 'start'
union all
select start+1 'start'
from prime where start<100
)
select e as prime_value from
(select a.start%b.start as w, a.start as e from prime A , Prime B
where --a.start% b.start<>0 and
b.start<a.start
--and a.start between 1 and 100
)A
where w=0
group by A.e
having count(w) <=1
BY Nagaraj M-BE
DECLARE #range int = 1000, #x INT = 2, #y INT = 2
While (#y <= #range)
BEGIN
while (#x <= #y)
begin
IF ((#y%#x) =0)
BEGIN
IF (#x = #y)
PRINT #y
break
END
IF ((#y%#x)<>0)
set #x = #x+1
end
set #x = 2
set #y = #y+1
end

Complicated SQL while loop

I am trying to create a while loop in SQL and it seems kind of complex. Here's what I need it to achieve:
Iterate through a single VARCHAR string (ex. '123')
If the nth character is in an even position in the string (ex. 2nd, 4th .... letter in the string), it must be added(SUM) to a base variable (Let's assume #z)
If the nth character is in an odd position in the string (ex. 1st, 3rd .... letter in the string), it must be multiplied by 2. If this newly generated value (Let's assume #y) is less than 10, it must be added(SUM) to the base variable (Still the same assumed #z). If #y is greater than 10, we need to subtract 9 from #y before adding(SUM) it to #z
After iterating through the entire string, this should return a numeric value generated by the above process.
Here is what I've done so far, but I'm stuck now (Needless to say, this code does not work yet, but I think I'm heading in the right direction):
DECLARE #x varchar(20) = '12345'
DECLARE #p int = len(#x)
WHILE #p > 0
SELECT #x =
stuff(#x, #p, 1,
case when CONVERT(INT,substring(#x, #p, 1)) % 2 = 0 then CONVERT(INT, #x) + CONVERT(INT,substring(#x, #p, 1))
end), #p -= 1
RETURN #x;
PS. The input will always be 100% numeric values, but it is formatted as VARCHAR when I recieve it.
UPDATE
The expected result for the sample string is 15
You can do this without using a loop. Here is a solution using Tally Table:
DECLARE #x VARCHAR(20) = '12345'
DECLARE #z INT = 0 -- base value
;WITH E1(N) AS( -- 10 ^ 1 = 10 rows
SELECT 1 FROM(VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1))t(N)
),
E2(N) AS(SELECT 1 FROM E1 a CROSS JOIN E1 b), -- 10 ^ 2 = 100 rows
E4(N) AS(SELECT 1 FROM E2 a CROSS JOIN E2 b), -- 10 ^ 4 = 10,000 rows
CteTally(N) AS(
SELECT TOP(LEN(#x)) ROW_NUMBER() OVER(ORDER BY(SELECT NULL))
FROM E4
),
CteChars(N, num) AS(
SELECT
t.N, CAST(SUBSTRING(#x, t.N, 1) AS INT)
FROM CteTally t
WHERE t.N <= LEN(#x)
)
SELECT
SUM(
CASE
WHEN N % 2 = 0 THEN num
WHEN num * 2 < 10 THEN num * 2
ELSE (num * 2) - 9
END
) + #z
FROM CteChars
The CTEs up to CteTally generates a list of number from 1 to LEN(#x). CteChars breaks #x character by character into separate rows. Then the final SELECT does a SUM based on the conditions.
OUTPUT : 15
Check below if it helps you
DECLARE #x varchar(20) = '12345'
DECLARE #p int = 1
DECLARE #result bigint=0;
DECLARE #tempval int =0;
WHILE #p <= len(#x)
BEGIN
SET #tempval = CONVERT(INT,substring(#x, #p, 1));
if(#p%2 = 1)
BEGIN
SET #tempval = #tempval * 2;
IF(#tempval >= 10) SET #tempval = #tempval - 9;
END
SET #result = #result + #tempval;
SET #p = #p + 1;
END;
PRINT #result;--This is the result
RETURN #x;
DECLARE #x INT = 12345
DECLARE #p int = len(#x)
DECLARE #z INT =0
PRINT #p%2
SET #x=#x/10
PRINT #x
WHILE #p > 0
BEGIN
IF(#p%2 = 0)
BEGIN
SET #z=#z+#x%10
SET #p=#p-1
SET #x=#x/10
END
ELSE
BEGIN
SET #z=#z+(2*(#x%10))
SET #p=#p-1
SET #x=#x/10
IF(#x>=10)
BEGIN
SET #x=(#x/10+#x%10)
END
END
END
SELECT #z
The while loop does not seem necessary here.
This can be achieved with a CTE that will split the string and a case statement:
DECLARE #x varchar(20) = '12345';
with split(id, v) as (
select 0, cast(0 as tinyint)
union all
select id+1, cast(SUBSTRING(#x, id+1, 1) as tinyint)
From split
Where id+1 <= len(#x)
)
Select Result = SUM(
Case When id % 2 = 0 then v
When v < 5 then v*2
Else (v*2)-9
End
)
From split
output = 15

In SQL , how to build a loop that copies a row number of times

could someone please help? My starting table looks like this with 2 fields:
Name Counter
dave 2
Joe 3
I want my result to look like this:
Name Counter
dave 1
dave 2
joe 1
joe 2
joe 3
Essentially creating n number of records base on the counter and starts at 1. I tried to do a loop using counter as a variable, but the code just runs nonstop.. could someone help?
A procedural SQL Server solution:
declare #input table
(
name nvarchar(100)
,wantedrows int
,processed bit
,id uniqueidentifier
);
declare #output table
(
name nvarchar(100)
,rownum int
);
insert into #input
select 'Dave',3,0,newid()
union
select 'Joe',2,0,newid();
while exists(select * from #input where processed = 0)
begin
declare #currentid uniqueidentifier = (select top 1 id from #input where processed = 0);
declare #currentwantedrows int = (select wantedrows from #input where id = #currentid);
declare #i int = 0;
while #i < #currentwantedrows
begin
insert into #output
select name,#i+1
from #input
where id = #currentid;
set #i = #i + 1;
end;
update #input set processed = 1 where id = #currentid;
end
select name,wantedrows from #input;
select * from #output;
You can use a number-table or following trick using a system view to build a sequence:
WITH Nums AS
(
SELECT n = ROW_NUMBER() OVER (ORDER BY [object_id])
FROM sys.all_objects
)
SELECT Name, Counter = n
FROM Nums n CROSS JOIN Table1 t1
WHERE n BETWEEN 1 AND Counter
ORDER BY Name, Counter;
Demo
This view has only about 2000 rows, so if you need more you could use a number-table.
http://sqlperformance.com/2013/01/t-sql-queries/generate-a-set-1
( presuming SQL-Server )
Is a hundred copies enough?
create table #c (num)
insert into #c (num)
select 0 union
select 1 union
select 2 union
select 3 union
select 4 union
select 5 union
select 6 union
select 7 union
select 8 union
select 9
select T.Name, c1.num * 10 + c0.num + 1
from T, #c c1, #c c0
where c1.num * 10 + c0.num < T.Counter
drop table #c
You didn't say which version of Sybase. The old ones I've worked on didn't allow derived tables so I had to throw the values into a temp table. But you can see how to extend the idea. This may not be the best approach if this is something you need to do more than once though.

SQL Server solution to simple recursive function

I am looking for a SQL Server solution for a simple recursive formula. In the example, X is my column of numbers and Y is the column I am trying to create with a SQL Query.
I have a list of numbers, denoted X, and wish to produce a special kind of running sum that is not allowed to go less than 0, denoted Y.
Base Case
Y1 = MAX(X1,0)
Recursive Rule
Yi = MAX(Xi+Yi-1,0)
EXAMPLE:
id X(Input) Y(Output)
1 15 15
2 -87 0
3 26 26
4 -87 0
5 4 4
6 -19 0
7 34 34
8 -4 30
9 40 70
10 -14 56
Assuming you have an id column that specifies the ordering, I am pretty sure you have to do this with a recursive CTE. The problem is that the "set negative numbers to zero" complicates the situation.
Let me assume that the id identifies the ordering.
with t as (
select t.*, row_number() over (order by id) as seqnum
from table t
),
cte as (
select X,
(case when X < 0 then 0 else X end) as Y
from t
where id = 1
union all
select tnext.X,
(case when tnext.X + cte.Y < 0 then 0 else tnext.X + cte.Y end) as Y
from cte join
t tnext
on t.id + 1 = tnext.id
)
select *
from cte;
Using a cursor and a table variable to catch the calculated values might be good for performance.
declare #T table
(
id int,
X int,
Y int
);
declare #id int;
declare #X int;
declare #Y int;
set #Y = 0;
declare C cursor local static forward_only read_only for
select T.id, T.X
from T
order by T.id;
open C;
fetch next from C into #id, #X;
while ##fetch_status = 0
begin
set #Y = case when #X + #Y < 0 then 0 else #X + #Y end;
insert into #T(id, X, Y) values (#id, #X, #Y);
fetch next from C into #id, #X;
end
close C;
deallocate C;
select T.id, T.X, T.Y
from #T as T
order by T.id;
SQL Fiddle
Have a look at Best approaches for running totals by Aaron Bertrand