Pick random number within a range - sql

I have a table (TableA) with an integer column(ColumnA) and there is data already in the table. Need to write a select statement to insert into this table with the integer column having random values within 5000. This value should not already be in columnA of TableA
Insert into TableA (columnA,<collist....>)
SELECT <newColId> ,<collist....> from TableB <where clause>

you can create a helper numbers table for this:
-- create helper numbers table, faster than online recursive CTE
-- can use master..spt_values, but actually numbers table will be useful
-- for other tasks too
create table numbers (n int primary key)
;with cte_numbers as (
select 1 as n
union all
select n + 1 from cte_numbers where n < 5000
)
insert into numbers
select n
from cte_numbers
option (maxrecursion 0);
and then insert some numbers you don't have in TableA (using join on row_number() so you can insert multiple rows at once):
;with cte_n as (
select n.n, row_number() over(order by newid()) as rn
from numbers as n
where not exists (select * from tableA as t where t.columnA = n.n)
), cte_b as (
select
columnB, row_number() over(order by newid()) as rn
from tableB
)
insert into TableA(columnA, columnB)
select n.n, b.ColumnB
from cte_b as b
inner join cte_n as n on n.rn = b.rn
If you're sure that there could be only one row from TableB which will be inserted, you can
use this query
insert into TableA(columnA, columnB)
select
a.n, b.columnB
from tableB as b
outer apply (
select top 1 n.n
from numbers as n
where not exists (select * from tableA as t where t.columnA = n.n)
order by newid()
) as a
Note it's better to have index on ColumnA column to check existence faster.
sql fiddle demo

Related

SQL Insert iterative number and Id of select Table 1 into table2

I have a numeric sequence of items that I need to insert into an entire table [Table 2].
Table 2 is made of items related to Table 1.
I need to add a set number of elements (A SET of ITEMS) into Table 2 , each set for each ID of Table 1. ID 1 in Table 1 will have roughly 100 elements referenced in Table 2 items 1-99, 100-199 would be associated with ID 2.
That might not be as clear as the pseudo code below. That is what I am trying to achieve.
Pseudo Code is
For Each Record in Select All Records from Table 1 ***(this is where I need help to know how to do)***
SET #cnt=1;
While (#cnt <100)
Insert Into Table 2, PK=#cnt, Name_#Cnt, Record.Id
SET #cnt =#cnt+1;
End
Maybe there is an easier way - by creating a temp table and insert on Innerjoin ??
I do not know I am rustier than I would like to be..
On the latest version of SQL Server, this is simply a case of using GENERATE_SERIES:
INSERT INTO dbo.Table2 (<Column List>)
SELECT <T1 Column List>,
GS.Value
FROM dbo.Table1 T1
CROSS APPLY GENERATE_SERIES(1,100,1);
On older versions, then you'll want to use a Tally. Ideally you'll want to create a function, but you can do this inline:
WITH N AS (
SELECT N
FROM (VALUES(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL))N(N)),
Tally AS (
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS I
FROM N N1, N N2) --100 rows
INSERT INTO dbo.Table2 (<Column List>)
SELECT <T1 Column List>,
T.I
FROM dbo.Table1 T1
CROSS JOIN Tally T;
If you wanted to create a function, then the one I use is the following:
CREATE FUNCTION fn.Tally (#End bigint, #StartAtOne bit)
RETURNS table
AS RETURN
WITH N AS(
SELECT N
FROM (VALUES(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL))N(N)),
Tally AS(
SELECT 0 AS I
WHERE #StartAtOne = 0
UNION ALL
SELECT TOP (#End)
ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS I
FROM N N1, N N2, N N3, N N4, N N5, N N6, N N7, N N8)
SELECT I
FROM Tally;
Then you could use that in your INSERT in a similar way to GENERATE_SERIES:
INSERT INTO dbo.Table2 (<Column List>)
SELECT <T1 Column List>,
T.I
FROM dbo.Table1 T1
CROSS APPLY fn.Tally(100,1);

Generate Random Test Data with ORDER BY NEWID() , include duplicate rows

I need to select random rows from a table for test data. There may be times I need more rows of test data than there are records in the table. Duplicates are okay. How do I structure my select so that I can get duplicate rows?
CREATE TABLE [Northwind].[dbo].[Persons]
(PersonID int, LastName varchar(255))
INSERT INTO [Northwind].[dbo].[Persons]
VALUES
(1, 'Smith'),
(2, 'Jones'),
(3, 'Washington')
SELECT TOP 5 *
FROM [Northwind].[dbo].[Persons]
ORDER BY NEWID()
How do I get the Select statement to give me five records in random order, with repeats? Currently, it only returns three in random order.
I'd like to be able to extend this to get 100 rows or 1000 rows or however many I need.
Use a recursive CTE to union enough rows so that they are larger than what you desire. Then select from that as you have done before.
declare
#desired int = 5,
#actual int = (select count(*) from persons);
with
persons as (
select personId,
lastName,
batch = 0
from Persons
union all
select personId,
lastName,
batch = batch + 1
from persons
where (batch + 1) * #actual < #desired
)
select
top (#desired) personId, lastName
from persons
order by newid()
As mentioned. You could instead us a tally table and then get the random rows;
WITH N AS(
SELECT N
FROM (VALUES(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL)))N(N)),
Tally AS(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS I
FROM N N1, N N2, N N3, N N4) --Repeat for more
SELECT TOP 500 YT.*
FROM Tally T
CROSS JOIN YourTable YT
ORDER BY NEWID();
I was thinking of how you would solve this without ordering all the records, especially multiple times.
One method is to generate random numbers and to use those for looking up values in your data:
with n as (
select rand(checksum(newid())) as r, 1 as n
union all
select rand(checksum(newid())) as r, n + 1
from n
where n < 10
),
tt as (
select t.*, lag(tile_end, 1, 0) over (order by tile_end) as tile_start
from (select t.*, row_number() over (order by newid()) * 1.0 / count(*) over () as tile_end
from t
) t
)
select tt.*, n.r, (select count(*) from n)
from n left join
tt
on n.r >= tt.tile_start and n.r < tt.tile_end;
Here is a db<>fiddle. The row_number() does not need to use order by newid(). It can order by a key that has an index -- which makes that component much more efficient.
For more than 100 rows, you will need OPTION (MAXRECURSION 0).
I added a temp results table and looped through the query and pushed the results into the temp table.
declare #results table(
SSN varchar(10),
Cusip varchar(10),
...
EndBillingDate varchar(10))
DECLARE #cnt INT = 0;
WHILE #cnt < #trades
BEGIN
INSERT INTO #results
Select ...
set #cnt = #cnt + 10
END
select * from #results

2 rows differences

I would like to get 2 consecutive rows from an SQL table.
One of the columns storing UNIX datestamp and between 2 rows the difference only this value.
For example:
id_int dt_int
1. row 8211721 509794233
2. row 8211722 509794233
I need only those rows where dt_int the same (edited)
Do you want both lines to be shown?
A solution could be this:
with foo as
(
select
*
from (values (8211721),(8211722),(8211728),(8211740),(8211741)) a(id_int)
)
select
id_int
from
(
select
id_int
,id_int-isnull(lag(id_int,1) over (order by id_int) ,id_int-6) prev
,isnull(lead(id_int,1) over (order by id_int) ,id_int+6)-id_int nxt
from foo
) a
where prev<=5 or nxt<=5
We use lead and lag, to find the differences between rows, and keep the rows where there is less than or equal to 5 for the row before or after.
If you use 2008r2, then lag and lead are not available. You could use rownumber in stead:
with foo as
(
select
*
from (values (8211721),(8211722),(8211728),(8211740),(8211741)) a(id_int)
)
, rownums as
(
select
id_int
,row_number() over (order by id_int) rn
from foo
)
select
id_int
from
(
select
cur.id_int
,cur.id_int-prev.id_int prev
,nxt.id_int-cur.id_int nxt
from rownums cur
left join rownums prev
on cur.rn-1=prev.rn
left join rownums nxt
on cur.rn+1=nxt.rn
) a
where isnull(prev,6)<=5 or isnull(nxt,6)<=5
Assuming:
lead() analytical function available.
ID_INT is what we need to sort by to determine table order...
you may need to partition by some value lead(ID_int) over(partition by SomeKeysuchasOrderNumber order by ID_int asc) so that orders and dates don't get mixed together.
.
WITH CTE AS (
SELECT A.*
, lead(ID_int) over ([missing partition info] ORDER BY id_Int asc) - id_int as ID_INT_DIFF
FROM Table A)
SELECT *
FROM CTE
WHERE ID_INT_DIFF < 5;
You can try it. This version works on SQL Server 2000 and above. Today I don not a more recent SQL Server to write on.
declare #t table (id_int int, dt_int int)
INSERT #T SELECT 8211721 , 509794233
INSERT #T SELECT 8211722 , 509794233
INSERT #T SELECT 8211723 , 509794235
INSERT #T SELECT 8211724 , 509794236
INSERT #T SELECT 8211729 , 509794237
INSERT #T SELECT 8211731 , 509794238
;with cte_t as
(SELECT
ROW_NUMBER() OVER (ORDER BY id_int) id
,id_int
,dt_int
FROM #t),
cte_diff as
( SELECT
id_int
,dt_int
,(SELECT TOP 1 dt_int FROM cte_t b WHERE a.id < b.id) dt_int1
,dt_int - (SELECT TOP 1 dt_int FROM cte_t b WHERE a.id < b.id) Difference
FROM cte_t a
)
SELECT DISTINCT id_int , dt_int FROM #t a
WHERE
EXISTS(SELECT 1 FROM cte_diff b where b.Difference =0 and a.dt_int = b.dt_int)

SQL Join doesn't match all elements

I was solving this question: How can I randomly do a partial outer join in SQL At the end didnt work because can assign multiple times the same row.
But I have a behavior I can't explain where the query doesn't return the expected number of rows
SQL DEMO
WITH tableA as (
SELECT T.id
FROM ( VALUES (111), (222), (333), (444), (555) ) T(id)
), tableB as (
SELECT *, row_number() over (order by note) as rn
FROM ( VALUES ('a'), ('b'), ('c'), ('d'), ('e'),
('f'), ('g'), ('h'), ('i'), ('j'),
('k'), ('l'), ('m'), ('n'), ('o')
) T(note)
), parameter as (
SELECT 3 as row_limit, (SELECT MAX(rn) FROM tableB) as max_limit
), Nums AS (
SELECT n = ROW_NUMBER() OVER (ORDER BY [object_id])
FROM sys.all_objects
), random_id as (
SELECT tableA.*, T.n, floor(p.max_limit * RAND(convert(varbinary, newid()))) + 1 magic_number
FROM tableA
CROSS JOIN parameter p
CROSS JOIN (SELECT n
FROM Nums
CROSS JOIN parameter p
WHERE n <= p.row_limit ) T
)
-- SELECT * FROM random_id
SELECT R.*, note
FROM random_id R
JOIN tableB
ON R.magic_number = tableB.rn
ORDER BY id
The setup: tableA 5 rows, tableB 15 rows. 3 random tableB rows for each row in tableA. So in total should return 3 * 5 = 15 rows
I create a row_number() from 1 to 15 to match to the magic number
Create the random_id cte to assing three random number to each row of tableA. Here you can see the 15 rows with a random number, also show the problem when assign same value twice
SELECT * FROM random_id;
But the JOIN return a random number of rows. more and less than 15
SELECT R.*, note
FROM random_id R
JOIN tableB
ON R.magic_number = tableB.rn
ORDER BY id
But if I use LEFT JOIN instead always return 15 rows.
Question: If random_id cte always return 15 rows how JOIN return more rows, and how return less if all rn values are in the tableB.
And how LEFT JOIN always return 15 rows.
I just test another query where I include the n value and the JOIN
Unfortunately you would have to persist the rows that are using the magic_number to temporary table or other similar construct.
rextester demo here: http://rextester.com/ICS74177
Unfortunately persisting it to a temporary table does result in a loss of elegance of the answer you were attempting. I have run into the same situation in the past trying to do the same thing and encountered the same bug. It's both somewhat exciting and ultimately disappointing when you run into it for the first time, so congrats on that at least!
I can not explain it any better, so please upvote Paul White's answer here: https://dba.stackexchange.com/a/30348/43889
Reference:
bug with newid() and table expressions (ctes)
newid() In Joined Virtual Table Causes Unintended Cross Apply Behavior - Answer by Paul White

SQL to get sequence of phone numbers

I have table called PhoneNumbers with columns Phone and Range as below
here in the phone column i have a phone numbers and in range column i have a range of values i need the phone numbers to be included.For the first phone number 9125678463 I need to include the phone numbers till the range 9125678465 ie (9125678463,9125678464,9125678465).Similarly for other phone numbers too.here is the sample destination table should look like
How can i write the sql to get this?
Thanks in advance
I have a solution which goes a classic way BUT: it does not need recursions and it does not need any loops! And it works even if your range has length of 3 or 5, or whatever...
first i create a table with numbers (from 1 to 1 million in this example - you can adopt this in TOP () clause):
SELECT TOP (1000000) n = CONVERT(INT, ROW_NUMBER() OVER (ORDER BY s1.[object_id]))
INTO dbo.Numbers
FROM sys.all_objects AS s1 CROSS JOIN sys.all_objects AS s2
OPTION (MAXDOP 1);
CREATE UNIQUE CLUSTERED INDEX idx_numbers ON dbo.Numbers(n)
;
if you have that table it's pretty simple:
;WITH phonenumbers
AS
(
SELECT phone,
[range],
CAST(RIGHT(phone,LEN([range])) AS INT) AS number_to_increase,
CAST(LEFT(phone,LEN(phone)-LEN([range])) + REPLICATE('0',LEN([range])) AS BIGINT) AS base_number
FROM PhoneNumbers
)
SELECT p.base_number + num.n
FROM phonenumbers p
INNER JOIN dbo.Numbers num ON num.n BETWEEN p.number_to_increase AND p.[range]
You don't have to use a CTE like here - it's just to see a bit clearer what the idea behind this approach is. Maybe this suits for you
You can use CTE like this:
;WITH CTE (PhoneNumbers, [Range], i) AS (
SELECT CAST(Phone AS bigint), [Range], CAST(1 AS bigint)
FROM yourTable
UNION ALL
SELECT CAST(PhoneNumbers + 1 AS bigint), [Range], i + 1
FROM CTE
WHERE (PhoneNumbers + 1) % 10000 <= [Range]
)
SELECT PhoneNumbers
FROM CTE
ORDER BY PhoneNumbers
Here is one example of using a tally table. In my system I have that set of ctes as a view so I never have to write it again.
if OBJECT_ID('tempdb..#PhoneNumbers') is not null
drop table #PhoneNumbers;
create table #PhoneNumbers
(
Phone char(10)
, Range smallint
)
insert #PhoneNumbers
select 9135678463, 8465 union all
select 3279275678, 5679 union all
select 6372938103, 8105;
WITH
E1(N) AS (select 1 from (values (1),(1),(1),(1),(1),(1),(1),(1),(1),(1))dt(n)),
E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max
cteTally(N) AS
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
)
select *
from #PhoneNumbers p
join cteTally t on t.N >= RIGHT(Phone, 4) and t.N <= Range
order by p.Phone
One more approach:
--Creating dummy table
select '9999991234' phone, '1237' rang into #tbl
union
select '9999995689', '5692'
SELECT [phone] low
,(CAST(9999995689/10000 AS bigINT) * 10000 + [Rang]) high
into #tbl1
FROM #tbl
--Creating 'numbrs' to have numbers between 0 & 9999 i.e. max range
select (rn-1)rn
into #numbrs
from
(select row_number() over (partition by null order by A.object_id) rn from sys.objects A
cross join sys.objects B)A
where rn between 0 and 9999
select (low + rn)phn from #numbrs cross join #tbl1
where (low + rn) between low and high