Select Random Numbers from a list - sql

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.

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

SQL counting the number of ones in sequence

I have the following table and as you can see the ids are not the same. So I can't do group by. I need to count all the ones that are in sequence. Like from id 9 to 13, from id 20 to 23. How i do it?
Here's a solution with LAG and LEAD.
;WITH StackValues AS
(
SELECT
T.*,
PreviousStatus = LAG(T.Status, 1, 0) OVER (ORDER BY T.ID ASC),
NextStatus = LEAD(T.Status, 1, 0) OVER (ORDER BY T.ID ASC)
FROM
#YourTable AS T
),
ValuesToSum AS
(
SELECT
L.*,
ValueToSum = CASE
WHEN L.Status = 1 AND L.PreviousStatus = 1 AND L.NextStatus = 0 THEN 1
ELSE 0 END
FROM
StackValues AS L
)
SELECT
Total = SUM(V.ValueToSum)
FROM
ValuesToSum AS V
LAG will give you the N previous row (N = 1 for this example) while LEAD will give you the N next row (N = 1 for this example). The query generates another column (ValueToSum) based on the previous and next values and uses it's result to sum.

select statement to list numbers in range

In DB2, I have this query to list numbers 1-x:
select level from SYSIBM.SYSDUMMY1 connect by level <= "some number"
But this maxes out due to SQL20450N Recursion limit exceeded within a hierarchical query.
How can I generate a list of numbers between 1 and x using a select statement when x is not known at runtime?
I found an answer based on this post:
WITH d AS
(SELECT LEVEL - 1 AS dig FROM SYSIBM.SYSDUMMY1 CONNECT BY LEVEL <= 10)
SELECT t1.n
FROM (SELECT (d7.dig * 1000000) +
(d6.dig * 100000) +
(d5.dig * 10000) +
(d4.dig * 1000) +
(d3.dig * 100) +
(d2.dig * 10) +
d1.dig AS n
FROM d d1
CROSS JOIN d d2
CROSS JOIN d d3
CROSS JOIN d d4
CROSS JOIN d d5
CROSS JOIN d d6
CROSS JOIN d d7) t1
JOIN ("subselect that returns desired value as i") t2
ON t1.n <= t2.i
ORDER BY t1.n
That's how I usually create lists:
For your example
numberlist (num) as
(
select min(1) from anytable
union all
select num + 1 from numberlist
where num <= x
)
I did something like this when I wanted a list of values to correspond with months:
with t1 (mon) as (
values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12)
)
select * from t1
It seems a bit kludgy, but for a small list like 1-12, or even 1-50, it did what I needed it to.
It's nice to see someone else tagging their questions with DB2.
If you have any table known to have more than x rows, you can always do:
select * from (
select row_number() over () num
from my_big_table
) where num <= x
or, per bhamby's suggestion:
select row_number() over () num
from my_big_table
fetch first X rows only
For DB2 you can use recursive common table expressions (cf. IBM documentation on recursive CTE):
with max(num) as (
select 1 from sysibm.sysdummy1
)
,result (num) as (
select num from max
union ALL
select result.num+1
from result
where result.num<=100
)
select * from result;

A query to SELECT a number range

I am having the following problem.
I would like to select a currency value from a database which will act as a default value on the top result of the query (this part is already done and is not a part of my main problem).
I want to use a query that kind of looks like this:
SELECT valkurs, valkurs 'vk'
FROM xx
WHERE valkod='EUR' AND foretagkod=300
UNION
--(My problem is that i can't find out what to write here)
My problem is that I would like to attach a range of values from 1.0 to 20.0 with 0.1 in incremental steps to the original query mentioned above.
An example output can look like this:
8.88, 8.88
1.0, 1.0
1.1, 1.1
1.2, 1.2
...
20.0, 20.0
Is it possible anyhow?
Due to implementation issues this has to be done in a query...
You can use the system table Master..spt_values to generate a sequential list:
SELECT Number = CAST(1 + (Number / 10.0) AS DECIMAL(4, 1)),
Number2 = CAST(1 + (Number / 10.0) AS DECIMAL(4, 1))
FROM Master..spt_values
WHERE Type = 'P'
AND Number BETWEEN 0 AND 200
So to combine in the correct order with your current query I would use:
SELECT valkurs, VK = valkurs
FROM ( SELECT valkurs, SortOrder = 0
FROM xx
WHERE valkod = 'EUR'
AND foretagkod = 300
UNION ALL
SELECT valkurs = CAST(1 + (Number / 10.0) AS DECIMAL(4, 1)), SortOrder = 1
FROM Master..spt_values
WHERE Type = 'P'
AND Number BETWEEN 0 AND 190
) T
ORDER BY T.SortOrder, t.valkurs;
ADDENDUM
There are some that do not advocate the use of Master..spt_values due to the fact that it is not documented, so it could be removed from future versions of sql-server. If this is a major concern you can use ROW_NUMBER() to generate a sequential list (using any table with enough rows as the source, I have gone for sys.all_objects):
SELECT valkurs, VK = valkurs,
FROM ( SELECT valkurs, SortOrder = 0
FROM xx
WHERE valkod = 'EUR'
AND foretagkod = 300
UNION ALL
SELECT TOP 191
valkurs = 1 + ((ROW_NUMBER() OVER(ORDER BY object_id) - 1) / 10.0),
SortOrder = 1
FROM sys.all_objects
) T
ORDER BY T.SortOrder, t.valkurs;
Old, but I think some people will benefit from my answer, which is a much better implementation than the accepted answer
WITH e1(n) AS
(
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
), -- 10
e2(n) AS (SELECT 1 FROM e1 CROSS JOIN e1 AS b), -- 10*10
e3(n) AS (SELECT 1 FROM e1 CROSS JOIN e2), -- 10*100
numbers as (SELECT n = ROW_NUMBER() OVER (ORDER BY n)/10.0
FROM e3)
select n, n from numbers
where n between 1 and 20

How can I get the maximum sequential in number range?

I have the specific result above in a select:
1 2
1 3
1 5
1 6
1 9
1 10
1 11
1 13
1 14
1 16
1 18
1 20
1 23
1 24
1 25
What I want to find is the longest increasing-by-one chain that occurs in the results.
For example, I know that 3 is the maximum length sequence in this number range, coming from the last 3 results (23,24,25 being 3 in a row).
A sequence will have the property that the difference between the number and a sequential ordering will be constant. In most dialects of SQL, you have a function called row_number(), which assigns sequential numbers.
We can use this observation to solve your problem:
select (num - seqnum), count(*) as NumInSequence
from (select t.*, row_number() over (order by num) as seqnum
from t
) t
group by (num - seqnum)
This gives every sequence. To get the max, either use max() with a subquery or some version of limit/top. In SQL Server, for instance, you can do:
select top 1 count(*) as NumInSequence
from (select t.*, row_number() over (order by num) as seqnum
from t
) t
group by (num - seqnum)
order by NumInSQuence desc
Using this article as the main query:
http://www.xaprb.com/blog/2006/03/22/find-contiguous-ranges-with-sql/
Just add a column that calculates the difference and select the MAX().
SELECT MAX(seq.end - seq.start)
FROM (
select l.id as start,
(
select min(a.id) as id
from sequence as a
left outer join sequence as b on a.id = b.id - 1
where b.id is null
and a.id >= l.id
) as end,
from sequence as l
left outer join sequence as r on r.id = l.id - 1
where r.id is null;
) AS seq
#Gordon gave a brilliant and more terse answer. However, I think a recursive implementation may be useful as well. Here's a very useful article on recursive CTEs: http://msdn.microsoft.com/en-us/library/ms186243(v=sql.105).aspx
-- This first CTE is unnecessary because you presumably already have
-- your data. But I wanted to include it to make it easier test.
WITH myNumbers AS (
SELECT *
FROM (
VALUES
(2),
(3),
(5),
(6),
(9),
(10),
(11),
(13),
(14),
(16),
(18),
(20),
(23),
(24),
(25)
) AS x (num)
),
-- To get my sequences I recurse until there is no num + 1 in my set
mySequences AS (
-- Anchor member definition: Create the first invocation
SELECT v.num, 0 AS iteration, v.num AS previous, v.num AS start
FROM myNumbers v
UNION ALL
-- Recursive member definition: Recurse until value + 1 does not exist
SELECT s.num + 1, s.iteration + 1 AS iteration, s.num AS previous, s.start
FROM mySequences s -- Notice that we can reference the CTE within itself
JOIN myNumbers v
ON v.num = s.num + 1
)
-- I must increment by 1 because I chose to start my recursion at 0
SELECT MAX(iteration + 1)
FROM mySequences
That recursive query is similar to writing
public int GetSequenceLength(int start, int iteration, int[] myNumbers)
{
if (myNumbers.Contains(start + 1))
{
return GetSequenceLength(start + 1, iteration + 1, myNumbers);
}
return iteration;
}
foreach (var myNumber in myNumbers)
{
var sequenceLength = GetSequenceLength(myNumber, 0, myNumbers) + 1;
Console.WriteLine(myNumber + " : " + sequenceLength);
}