How to find missing number from single column of table using SQL? - sql

There are 999 rows and they have distinct 0-1000 number with one number missing in it. How to find that number using a SQL query?

Use something like this:
SELECT Min(ID) As firstMissedID
FROM (
SELECT *, ROW_NUMBER() OVER (ORDER BY ID) rn
FROM std) dt
WHERE rn < ID

I am assuming that number column will have only numbers from 0 to 1000.
declare #minnumber int
declare #missingnumber int
select #minnumber=min(number) from Test
if(#minnumber=0)
begin
--500500 is the sum of 1 to 1000 number
select #missingnumber=500500-sum(number) from Test
end
else
begin
set #missingnumber=0
end

Use a tally table to get the list of 1000 rows then use LEFT OUTER JOIN or NOT EXISTS to find the missing Number
WITH Tally (n) AS
(
-- 1000 rows
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) a(n)
CROSS JOIN (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) b(n)
CROSS JOIN (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) c(n)
)
SELECT Min(n)
FROM Tally T
LEFT OUTER JOIN YOURTABLE Y
ON T.N = Y.MISSING_NUM_COLUMN
WHERE Y.MISSING_NUM_COLUMN IS NULL
If you want to find the list of missing Numbers then remove the MIN operator from the last select query.
SELECT Missing_Numbers = n
FROM Tally T
LEFT OUTER JOIN YOURTABLE Y
ON T.N = Y.MISSING_NUM_COLUMN
WHERE Y.MISSING_NUM_COLUMN IS NULL

not sure if this is the best approach but it'll work.
Create a temp table with a column to hold integer values
=#temptbl1
fill the table with values 1 to 1000 then run this query which will return an entry only if it's not contained in your datatable.
select value from #temptable1 where value not in (select Othervalue from othertable)
the select statement will return all missing values.

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);

MS SQL Server how to instantly insert a 1 to 10 number column to table (virtual column)?

any idea about to instantly add a column for number (1 to 10) for each rows value on existing table?
You can use a CROSS JOIN in concert with an ad-hoc tally table
Example
Select A.*
,B.Code
From YourTable A
Cross Join ( Select Top 10 Code=row_number() Over (Order By (Select NULL)) From master..spt_values n1 ) B
You can generate the rows with a recurisve query, then cross join that with your table.
with codes as (
select 1 code
union all select code + 1 from cte where code < 10
)
select t.*, c.code
from mytable t
cross join codes c
For a small number of rows, I would expect the recusive query to be faster than top 10 against a large table.

SQL Server Create rows in 3rd table based on columns in 2 tables

I am a SQL Server newbie. I am trying to create test data.
I have a table that contains 10,000 part numbers(Table1).
I have another table that contains warehouses(Table2).
I have a third table(Table3) that will contain a row for every part number/warehouse combination.
Table1 will contain the part numbers, Table2 will contain the 6 warehouses and Table 3 will have a row for each part number/warehouse. That means I will end up with 60,000 rows in Table3. I have looked through all the JOINs and can't seem to find one that does the work. What I want to do is load Table3 with all the part number/warehouse rows with a starting value of 100 in a column called On_Hand. Thank you for your assistance.
INSERT INTO TABLE_3 ( Part_No, Location)
SELECT Part_No, Location from InventoryTable CROSS JOIN LocationTable WHERE
order by Part_No
You want cross join, used like this:
insert into inventory (part_no, warehouse_id, on_hand) -- table_3
select p.Part_No, w.warehouse_id, 500
from Parts it cross join -- table_1
Warehouses w; -- table_2
Generate some data:
DECLARE #partNumbers TABLE
(
PartNumber INT
)
DECLARE #warehouses TABLE
(
Warehouse VARCHAR(20)
)
DECLARE #partNumberStart INT = 100
-- partnumbers 10000 starting at #partNumberStart -- for testing.
INSERT INTO #partNumbers
SELECT #partNumberStart + ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS ID FROM
(VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) x1(x),
(VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) x2(x),
(VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) x3(x),
(VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) x4(x)
--Warehouses 6 generated for testing
INSERT INTO #warehouses
SELECT x1.x + CAST(ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS VARCHAR(20)) AS ID FROM
(VALUES('Warehouse ')) x1(x),
(VALUES(0),(1),(2),(3),(4),(5)) x2(x) --6
--(VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) x2(x), --10
Query the data:
SELECT * FROM #warehouses LEFT JOIN #partNumbers on 1=1
--OR
SELECT * FROM #warehouses CROSS JOIN #partNumbers
--OR
SELECT * FROM #warehouses, #partNumbers

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

Finding missing numbers in a table

I'd like to generate a query that will produce a list of index values that are missing from a SQL table. So far, what I have is
SELECT index - 1
FROM table
WHERE index - 1 NOT IN (
SELECT DISTINCT index
FROM table)
AND
index != 1;
This only finds the first missing index of a sequence of missing indices. I'd like to produce a query which outputs every number between 1 and MAX(index) which does not appear as an index in the table. Is there a way to do that?
You need a numbers table to find the missing index values
Recursive CTE approach
DECLARE #max_num INT = (SELECT Max(index)
FROM table);
WITH cte
AS (SELECT 1 AS num
UNION ALL
SELECT num + 1
FROM cte
WHERE num < #max_num)
SELECT *
FROM cte c
WHERE NOT EXISTS (SELECT 1
FROM table t
WHERE c.num = t.index)
ORDER BY c.num
OPTION (maxrecursion 0)
Another approach using tally table to generate numbers.
;WITH E1(N)
AS (SELECT *
FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) tc (N)), --10E+1 or 10 rows
E2(N)
AS (SELECT 1 FROM E1 a CROSS JOIN E1 b), --10E+2 or 100 rows
E4(N)
AS (SELECT 1 FROM E2 a CROSS JOIN E2 b), --10E+4 or 10,000 rows max
--E5(N) AS (SELECT 1 FROM E4 a CROSS JOIN E1 b), -- 10000 * 10 = 100000
cte(NUM)
AS (SELECT ItemNumber = Row_number()OVER(ORDER BY N) FROM E4)
SELECT *
FROM cte c
WHERE NOT EXISTS (SELECT 1
FROM table t
WHERE c.num = t.index)
ORDER BY c.num
Currently it generates 10000 sequential row numbers an if your max value is more than 10000 then you add another cross join to increase the numbers
My preference for this type of query is to show ranges. A simple way uses lead():
select (index + 1) as first_missing, (next_index - 1) as last_missing,
(next_index - index - 1) as num_missing
from (select t.*, lead(index) over (order by index) as next_index
from t
) t
where next_index <> index + 1;
This shows every "internal" missing range. Because it does not use a table of numbers, it works efficiently regardless of the size of the table or the number of missing values.
It does not show "missing" values at the beginning and end of the table. Your question does not specify that as an issue.