How to create letter sequence? - sql

How to create letter sequence in SQL Server ? Like A,B,C....Z,AA,AB... I refer this
Given Letter, Get Next Letter in Alphabet link but it will not work after letter Z.How to do this?

You can just use arithmetic. This is going to assume that you have numbers in a table (however you want to generate them):
select (case when (n / 26) % 27 = 0 then ''
else char(ascii('A') + (n / 26) % 27 - 1)
end) +
char(ascii('A') + n % 26)
from numbers n;
For instance:
select (case when (n / 26) % 27 = 0 then ''
else char(ascii('A') + (n / 26) % 27 - 1)
end) +
char(ascii('A') + n % 26)
from (select top 100 row_number() over (order by (select null)) - 1 as n
from master..spt_values
) n;
Here is a SQL Fiddle. Longer sequences can be generated using the same logic.
An alternative method is to put together a sequence of letters and then just cross join to put them together:
with letters as (
select top 26
char(ascii('A') + row_number() over (order by (select null)) - 1) as letter
from master..spt_values
)
select l1.letter as seq
from letters l1
union all
select l1.letter + l2.letter
from letters l1 cross join letters l2
union all
select l1.letter + l2.letter + l3.letter
from letters l1 cross join letters l2 cross join letters l3
order by len(seq), seq;
This is a bit challenging because the sequences have different lengths.
Here is a SQL Fiddle illustrating this approach.

here we use generate_series() function to generate a series of number.Specify numbers from where to where you need to generate sequence of numbers. And chr() fucntion returns the ASCII value of those number
from
select chr(generate_series(65,90));

Related

Find missed max and min value in a sequence of numbers

For example, I have a sequence of numbers: {1, 2, 5, 7}.
I need to find the smallest and the biggest one, which are missed in this sequence (min=3 and max=6 for this example). Values can also be negative.
Here is my solution, but it doesn't pass on extra checking database (Wrong number of records (less by 1)), so I can't say what is exactly wrong. I also tried versions with LEFT OUTER JOIN and EXCEPT predicates - same problem. Please, help me to improve my solution.
WITH AA AS (SELECT MAX(Q_ID) MX
FROM UTQ),
BB AS (SELECT MIN(Q_ID) CODE
FROM UTQ
UNION ALL
SELECT CODE + 1
FROM BB
WHERE CODE < (SELECT MX
FROM AA)
)
SELECT MIN(CODE) MIN_RES, MAX(CODE) MAX_RES
FROM BB
WHERE CODE NOT IN (SELECT Q_ID
FROM UTQ)
One method is not exists:
select min(q_id + 1)
from utq
where not exists (select 1 from utq utq2 where utq2.q_id = utq.id + 1)
union all
select max(q_id - 1)
from utq
where not exists (select 1 from utq utq2 where utq2.q_id = utq.id - 1);
You can also use lead() and lag():
select min(case when next_q_id <> q_id + 1 then q_id + 1 end),
max(case when prev_q_id <> q_id - 1 then q_id - 1 end)
from (select utq.*,
lag(q_id) over (order by q_id) as prev_q_id,
lead(q_id) over (order by q_id) as next_q_id
from utq
) utq;
A tally based method seems like a good approach here. Especially if the sequences are large.
The first CTE summarizes the maximum and minimum q_id's in the test table. The second CTE selects the missing integers by generating the complete sequence (using the fnNumbers tvf) between the minimum and maximum q_id values and comparing WHERE NOT EXISTS to the original sequence. Something like this.
numbers function
create function [dbo].[fnNumbers](
#zero_or_one bit,
#n bigint)
returns table with schemabinding as return
with n(n) as (select null from (values (1),(2),(3),(4)) n(n))
select 0 n where #zero_or_one = 0
union all
select top(#n) row_number() over(order by (select null)) n
from n na, n nb, n nc, n nd, n ne, n nf, n ng, n nh,
n ni, n nj, n nk, n nl, n nm, n np, n nq, n nr;
data and query
drop table if exists #seq;
go
create table #seq(
q_id int unique not null);
insert #seq values (1),(2),(5),(7);
with
max_min_cte(max_q, min_q) as (
select max(q_id), min(q_id)
from #seq),
missing_cte(q_id) as (
select mm.min_q+fn.n
from max_min_cte mm
cross apply dbo.fnNumbers(0, mm.max_q-mm.min_q) fn
where not exists (select 1
from #seq s
where (mm.min_q+fn.n)=s.q_id))
select max(q_id) max_missing, min(q_id) min_missing
from missing_cte;
output
max_missing min_missing
6 3
You can try like following using LEAD
SELECT MIN(Q_ID + 1) AS MinValue
,MAX(Q_ID + 1) AS MaxValue
FROM (
SELECT *,LEAD(Q_ID) OVER (ORDER BY Q_ID) NQ_ID
FROM (VALUES (1),(2),(5),(7)) v(Q_ID)
) t
WHERE NQ_ID - Q_ID <> 1

Converting alphanumeric to numeric and vice versa in oracle

I have a requirement to convert alphanumeric to numeric and vice-versa.
Example: If 'A2' is passed then I have written below query to convert it to numeric:
select sum(val) from (
select power(36, loc - 1) *
case when letter between '0'
and '9'
then to_number(letter)
else 10 + ascii(letter) - ascii('A')
end as val from(
select substr(ip_str, length(ip_str) + 1 - level, 1) letter,
level loc from(select 'A2'
ip_str from dual) connect by level <= length(ip_str)
)
); --sum(val) returns 362
How do I decode 362 back to 'A2'?
Base N Convert - this site describes algorithm. I implemented it as recursive query:
with
t(num) as (select 362 from dual),
r(md, div, lvl) as (
select mod(num, 36), floor(num/36), 1 from t union all
select mod(div, 36), floor(div/36), lvl + 1 from r where div > 0)
select listagg(case when md > 9 then chr(ascii('A') - 10 + md)
else to_char(md)
end) within group (order by lvl desc) b36
from r
dbfiddle demo
Seems to work, I tested several values comparing results with online calculators. Theoretically you can use other bases, not only 36, algorithm is the same, but I did not test it.

Create Custom Sequence

I need to create a Custom Sequence which should create sequence for below ranges
XV00AA-XV99ZZ and many other ranges
Example:
XV00AA, XV01AA, XV02AA, ......XV99AA
The first 2 characters remain the same (example - XV series); the last 2 characters remain the same (example - AA series); but the middle 2 characters should increment from 0 to 99. (Example XV01AA, XV02AA, XV03AA and so on)
Once it reaches 99 (i.e. XV99AA) then it should repeat for AB series
So output should be
XV00AB, XV01AB, XV02AB, .....XV99AB
And then
XV00AC,XV01AC,XV02AC....XV99AC
So sample final output:
XV00AA
XV01AA
XV02AA
....
XV99AA
XV00AB
XV01AB
XV02AB
....
XV99AB
XV00AC
XV01AC
XV02AC
...
XV99AC
XV00AD
XV01AD
XV02AD
...
XV99AD
and so on. Is there any easy way to create these series? Any help would be appreciated
This should do the trick.
DECLARE #how_many_do_you_want INT = 67599; -- 67599 is where it runs out of legit values.
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),
cte_Tally (n) AS (
SELECT TOP (#how_many_do_you_want)
ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM
cte_n3 a CROSS JOIN cte_n3 b
)
SELECT
rn = t.n,
CONCAT('XV', cn.char_num, a.alpha_1, a.alpha_2)
FROM
cte_Tally t
CROSS APPLY ( VALUES (t.n % 100) ) m (mod_100)
CROSS APPLY ( VALUES (((t.n - m.mod_100) / 100) % 26 + 1) ) g1 (group_1)
CROSS APPLY ( VALUES (t.n / 2600 + 1) ) g2 (group_2)
CROSS APPLY ( VALUES (RIGHT(CONCAT('0', m.mod_100), 2)) ) cn (char_num)
CROSS APPLY ( VALUES (CHAR(g1.group_1 + 64), CHAR(g2.group_2 + 64)) ) a (alpha_1, alpha_2);
The following code uses a CTE to generate a table of numbers from 0 to 67,599. The values are then split apart: modulus (%) provides the value for the digits and integer division (/) provides the value for the letters. A little fiddling for formatting and conversion and Bob's your uncle.
with Numbers as (
select 0 as N
union all
select N + 1
from Numbers
where N < 67599 )
select N, N % 100 as DigitsValue, N / 100 as LettersValue,
Right( '0' + Cast( N % 100 as VarChar(2) ), 2 ) as LeftPaddedDigits,
Char( ASCII( 'A' ) + ( N / 100 ) % 26 ) as LeastSignificantLetter,
Char( ASCII( 'A' ) + ( N / 100 ) / 26 ) as MostSignificantLetter
from Numbers
option ( MaxRecursion 0 )
Putting the complete string together with "XV" as a prefix is left to the reader.

How to generate combinations

I have a requirement to create a table with an identifier column. The identifier data will be comprised of 3 parts, the first being a letter [A-Z], the second being a number [1-42] and the third being again a number [1-6].
I was wondering the quickest and best way to go about this as I'm really stuck. The output should look like this:
A-1-1
A-1-2
A-1-3
...
Z-42-6
Thanks for your help
You should use CROSS JOIN with derived tables containing all letters/numbers needed
SELECT letters.let + '-' + numbers.num + '-' + numbers2.num
FROM(SELECT 'A' as let UNION ALL SELECT 'B' .....) letters
CROSS JOIN(SELECT '1' as num UNION ALL SELECT '2' ....) numbers -- up to 42
CROSS JOIN(SELECT '1' as num UNION ALL SELECT '2' ....) numbers2 -- up to 6
Here is a cut-down version using CROSS JOIN acrross 3 valued tables
SELECT v1.val + '-' + CAST(v2.val AS VARCHAR(5)) + '-' + cast(v3.val AS VARCHAR(5))
FROM
(VALUES ('A'),('B'),('C'),('D')) v1(val)
CROSS JOIN
(VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(13),(14),(15),(16)) v2(val)
CROSS JOIN
(VALUES (1),(2),(3),(4),(5),(6)) v3(val)
A Tally table would save you the need to write all the values one by one.
If you don't already have a tally table, read this post on the best way to create one.
SELECT Letter +'-'+ cast(fn as varchar(2)) +'-'+ cast(sn as char(1))
FROM (SELECT CHAR(Number) As Letter FROM Tally WHERE Number BETWEEN 65 AND 90) a
CROSS JOIN (SELECT Number as fn FROM Tally WHERE Number BETWEEN 1 AND 42) b
CROSS JOIN (SELECT Number as sn FROM Tally WHERE Number BETWEEN 1 AND 6) c
Just for fun, a mathematical approach:
with cte as
(
select 0 nr
union all
select nr+1 from cte where nr < 6551 --(26 * 42 * 6 = 6552 , 0 based = 6551)
)
select char(65 + (nr / 252)), 1 + ((nr / 6) % 42), 1 + nr % 6, * from cte -- Letter: divider = 6 * 42 = 252 , 65 = 'A'
option (maxrecursion 10000)
The cte only generated a stream of numbers from 0 to 6551 (could be done with other approaches as well).
After that each segment of the sequence can be calculated.
But for the record, once a sequence is created, I like Zohar's solution best :)
One more way:
;WITH cte AS (
SELECT 1 as digit
UNION ALL
SELECT digit + 1
FROM cte
WHERE digit < 90
)
SELECT CHAR(c1.digit) + '-' +
CAST(c2.digit as nvarchar(2)) + '-' +
CAST(c3.digit as nvarchar(2)) as seq
FROM cte c1
CROSS JOIN (SELECT digit FROM cte WHERE digit between 1 and 42) c2
CROSS JOIN (SELECT digit FROM cte WHERE digit between 1 and 6) c3
WHERE c1.digit between 65 and 90 --65..90 in ASCII is A..Z
Output:
seq
A-1-1
A-1-2
A-1-3
A-1-4
A-1-5
A-1-6
A-2-1
A-2-2
A-2-3
A-2-4
A-2-5
A-2-6
...
Z-42-3
Z-42-4
Z-42-5
Z-42-6

Select Random Numbers from a list

This is my query.
SELECT TOP 2 NUM
FROM QT_PIVOT
WHERE NUM BETWEEN 1 AND 45
ORDER BY NEWID()
I'm selecting 2 random numbers from a list but I don't want that these numbers to be continuous
Sometimes the result is
NUM
----
2
3
And I don't want this
Thanks , and sorry for my English u.u
Basically the same as the 2nd approach Gordon uses except it lacks the use of the lag function and therefor will work on SQL-2008.
WITH Data AS(
SELECT *, RowNum = ROW_NUMBER() OVER (ORDER BY NEWID())
FROM sys.objects AS O
),
r AS(
SELECT TOP 1 *, SkipRow = 0
FROM Data
WHERE Data.RowNum = 1
UNION ALL
SELECT d.*, SkipRow = CASE WHEN d.object_id BETWEEN r.object_id -2 AND r.object_id + 2 THEN 1 ELSE 0 END
FROM r
JOIN Data AS D
ON r.RowNum + 1 = D.RowNum
)
SELECT TOP 2 * FROM R
WHERE R.SkipRow = 0
One approach is to select the first number, and then select an appropriate second number:
WITH r AS (
SELECT TOP 1 num
FROM QT_PIVOT
WHERE NUM BETWEEN 1 AND 45
ORDER BY NEWId()
)
select num
from r
union all
select top 1 q.num
from qt_pivot q join
r
on q.num not in (r.num, r.num - 1, r.num + 1)
where q.num between 1 and 45
order by newid();
Another approach (if you had SQL Server 2012+) would use lag() to remove any possibilities that do not meet the conditions:
WITH r AS (
SELECT num, row_number() over (order by newid()) as seqnum
FROM QT_PIVOT
WHERE NUM BETWEEN 1 AND 45
)
SELECT r.num
FROM (SELECT r.*, LAG(num) OVER (ORDER BY seqnum) as prevnum
FROM r
) r
WHERE prevnum is null or
prevnum not in (num - 1, num + 1);
EDIT:
The first approach doesn't work, because SQL Server always re-evaluates CTEs, and there is not even a hint to fix this problem. Here is an alternative approach, that will ensure that values are not consecutive:
WITH r as (
SELECT (1 + checksum(newid()) * 45) as r1,
(2 + checksum(newid()) * 43) as r2
)
SELECT q.num
FROM QT_PIVOT q
WHERE q.num = r.r1 or
q.num = 1 + (r.r1 + r.r2) % 45;
This calculates a two random numbers. The first is a random position. The second is an allowable offset (hence the "2" and "43") to guarantee that the numbers are not adjacent.