SQL Server Data Query on Possible Rows - sql

SQL Server 2000
I have a table which lists out the months where a particular product can be sold.
ProductCode MonthNum MonthName
XXX 1 January
XXX 2 February
YYY 1 January
YYY 3 March
YYY 5 May
ZZZ 6 June
ZZZ 7 July
I need to construct a query that would allow me to pass some parameters:
ProductCode
LatestMonthNum
LatestYear
FutureForecast
that would allow me to construct a set of data list with the total rows based on the number of FutureForecast value and the rows' value would be beyond the passed LatestMonthNum and LatestYear parameter values.
For example, if I pass the following values to the query:
ProductCode YYY
LatestMonthNum 5
LatestYear 2012
FutureForecast 5
I would have the data as follows:
ProductCode MonthNum Year
YYY 1 2013
YYY 3 2013
YYY 5 2013
YYY 1 2014
YYY 3 2014

I would create a table of months and years in the future (something like a numbers table):
CREATE TABLE future (month int, year int);
DECLARE #m int;
SET #m = 1;
DECLARE #y int;
SET #y = 2000;
WHILE (#y < 2200) -- will my brain be in a jar by now?
BEGIN
WHILE (#m < 13)
BEGIN
INSERT INTO future (month, year) VALUES (#m, #y);
SET #m += 1;
END
SET #m = 1;
SET #y += 1;
END;
Then you can join your products table with the future!
SELECT TOP #FutureForecast
p.ProductCode, p.MonthNum, f.[year]
FROM Products AS p
JOIN future AS f ON f.[month] = p.MonthNum
WHERE
p.ProductCode = #ProductCode
AND (p.MonthNum > #LatestMonthNum OR f.[year] > #LatestYear)
ORDER BY f.[year], p.MonthNum

Like this?
Select * From
(SELECT ProductCode, MonthNum, Year, RANK() OVER
(PARTITION BY ProductCode ORDER BY
CAST('1/' + CAST(MonthNum AS varchar) + '/' + CAST(Year AS varchar)
AS datetime)) AS 'RANK'
FROM dbo.MyTable
WHERE (ProductCode = #productcode) AND (MonthNum > #monthnum) AND
(Year > #latestyear)) tbl Where tbl.Rank <= #futureforcast
Where is your year column?

I believe this should work. You could turn the loop into a CTE (only for SQL 2005+), but I am not sure if that gains you that much in this case.
CREATE TABLE #TempOrder
(Order INT IDENTITY (1,1), ProductCode VARCHAR(MAX), MonthNum INT)
INSERT INTO #TempOrder
SELECT ProductCode, MonthNum
FROM ProductTable
WHERE ProductCode = #ProductCode AND MonthNum > #LatestMonthNum
ORDER BY MonthNum DESC
--Increment the year from the start if it is already over the max stored
IF NOT EXISTS (SELECT 1 FROM #TempOrder)
SET #LatestYear = #LatestYear + 1
INSERT INTO #TempOrder
SELECT ProductCode, MonthNum
FROM ProductTable
WHERE ProductCode = #ProductCode AND MonthNum <= #LatestMonthNum
ORDER BY MonthNum DESC
DECLARE #MaxId
SELECT #MaxId = MAX(Id) FROM #TempOrder
CREATE TABLE #ForecastData (ProductCode VARCHAR(MAX), MonthNum INT, Year INT)
DECLARE #CurrentId INT
SET #CurrentId = 1
DECLARE #CurrentCount INT
SET #CurrentCount = 0
WHILE(#CurrentCount < #FutureForeCast)
BEGIN
INSERT INTO #ForecastData
SELECT ProductCode, MonthNum, #LatestYear
FROM #TempOrder
WHERE Id = #CurrentId
--Increment the Id, and if it's over the max in the table
--Reset to 1 and increment for a new year
SET #CurrentId = #CurrentId + 1
IF #CurrentId > #MaxId
BEGIN
SET #CurrentId = 1
SET #LatestYear = #LatestYear + 1
END
SET #CurrentCount = #CurrentCount + 1
END
SELECT #ForecastData
(This solution only for SQL 2005+)I am extremely rusty on CTE's but I figured I would give my attempt at this as a CTE also. This replaces everything from above from CREATE TABLE #ForecastData.... down:
;
WITH ForecastData (ProductCode, MonthNum, Year)
AS
(
-- Anchor member definition
SELECT ProductCode, MonthNum, #LatestYear AS Year, Id AS LastId
FROM #TempOrder
WHERE Id = 1
UNION ALL
-- Recursive member definition
SELECT ProductCode, MonthNum,
CASE WHEN Id = 1 THEN Year + 1 ELSE Year,
Id AS LastId
FROM #TempOrder
JOIN ForecastData
ON Id = CASE
WHEN LastId = #MaxId THEN 1
ELSE LastId + 1
END
)
-- Statement that executes the CTE
SELECT *
FROM ForecastData;
GO

Related

Auto Populate Table With Date Range SQL

My problem is auto populating Table.
I have table with 1000 record in it, but for testing purpose, i need to insert more data.
ID | PersonID | Date | Time | Sum | TypeID | PlaceID | StatusID
So i need to populate the database with 10000 records where the date is between 1/3/2015 and 1/5/2015, Time is Random, SUM Between 100 and 1000, TypeID between 1 and 2, PlaceID between 1-10, StatusID between 1-3
I would a appreciate any kind of help or suggestion.
Thanks in advance.
Here is some brutal solution but completely randomized:
with rows as(select row_number() over(order by(select null)) as dummy from
(values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1))t1(n)
cross join (values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1))t2(n)
cross join (values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1))t3(n)
cross join (values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1))t4(n))
select *,
cast(dateadd(ms, cast(cast(newid() as varbinary(30)) as int), getdate()) as time) as time
from rows r
cross apply(select top 1 p as place
from (values(1),(2),(3),(4),(5),(6),(7),(8),(9),(10))p(p)
where r.dummy = r.dummy order by newid()) cap
cross apply(select top 1 s as status
from (values(1),(2),(3))s(s)
where r.dummy = r.dummy order by newid()) cas
cross apply(select top 1 t as time
from (values(1),(2))t(t)
where r.dummy = r.dummy order by newid()) cat
cross apply(select top 1 sum from(select 100 + row_number() over(order by(select null)) as sum
from (values(1),(1),(1),(1),(1),(1),(1),(1),(1))t1(n)
cross join (values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1))t2(n)
cross join (values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1))t3(n)) t
where r.dummy = r.dummy order by newid()) casu
cross apply(select top 1 dateadd(dd, s -1, '20150103') as date
from (values(1),(2),(3))s(s)
where r.dummy = r.dummy order by newid()) cad
Fiddle http://sqlfiddle.com/#!6/9eecb7db59d16c80417c72d1/892
You need a small t-sql to do it:
--CREATE TABLE TEST (CID INT, PERSONID INT, TEST_DATE DATE, TEST_TIME TIME, TEST_SUM INT, TYPEID INT, PLACEID INT, STATUSID INT);
--TRUNCATE TABLE TEST;
SET NOCOUNT ON;
DECLARE #X INT, #PERSONID INT, #DATE DATE, #TIME TIME, #SUM INT, #TYPEID INT, #PLACEID INT, #STATUSID INT,#R INT;
SELECT #X=0;
WHILE #X < 10000 BEGIN
SELECT #X=#X +1;
SELECT #DATE = DATEADD(DAY, #X / 4000, '2015-1-3');
SELECT #R=CONVERT(INT, RAND() * 3600 * 24);
SELECT #TIME = DATEADD(SECOND, #R , '00:00:01');
SELECT #SUM = 100 + #R % 900;
SELECT #TYPEID = #R % 2 + 1 ;
SELECT #PLACEID = #R % 10 +1 ;
SELECT #STATUSID = #R % 3 +1 ;
SELECT #PERSONID = #R % 500 +1 ;
INSERT INTO TEST (CID, PERSONID, TEST_DATE, TEST_TIME, TEST_SUM, TYPEID, PLACEID, STATUSID)
VALUES(#X, #PERSONID, #DATE, #TIME, #SUM, #TYPEID, #PLACEID, #STATUSID);
END;
SET NOCOUNT OFF;
Also, please try not to use column names like "ID","Date","Time" and etc which have special meanings in SQL Server.
One is to use the pseudo random values derived from NEWID. You didn't mention how ID and PersonID should be assigned but the ROW_NUMBER value returned by the CTE could be used for that if you need incremental values.
WITH
t4 AS (SELECT n FROM (VALUES(0),(0),(0),(0)) t(n))
,t256 AS (SELECT 0 AS n FROM t4 AS a CROSS JOIN t4 AS b CROSS JOIN t4 AS c CROSS JOIN t4 AS d)
,t16M AS (SELECT ROW_NUMBER() OVER (ORDER BY (a.n)) AS num FROM t256 AS a CROSS JOIN t256 AS b CROSS JOIN t256 AS c)
SELECT
DATEADD(day, CAST(CAST(NEWID() AS varbinary(1)) AS int) % 3, '20150103') AS Date
,DATEADD(millisecond, CAST(CAST(NEWID() AS varbinary(4)) AS int), CAST('' AS time)) AS Time
,(CAST(CAST(NEWID() AS varbinary(3)) AS int) % 900) + 100 AS [Sum]
,(CAST(CAST(NEWID() AS varbinary(3)) AS int) % 2) + 1 AS TypeID
,(CAST(CAST(NEWID() AS varbinary(3)) AS int) % 10) + 1 AS PlaceID
,(CAST(CAST(NEWID() AS varbinary(3)) AS int) % 3) + 1 AS StatisID
FROM t16M
WHERE num <= 10000;

How to implement FIFO in sql

I working on FIFO implementation in sql.
I have Batch number concept in my application.
If suppose I am selling on inventory then my application should tell me that which inventory is the first come.
Lets. Say I purchased Inventory 'A' on 4th-Aug, 5th-Aug & 6th-Aug
On 4th Aug - A Inventory has batch number BT002 - 10 (Qty)
On 5th Aug - A's Inventory has batch number BT003 - 15 (Qty)
On 6th Aug - A's Inventory has batch number BT001 - 10 (Qty)
So, Now I am having stock Now in my hand as following :
A Inventory
BT002 - 10 - 4-Aug
BT003 - 15 - 5-Aug
BT001 - 10 - 6-Aug
Now If I want to sell that Inventory to anybody then my application should tell me that I should sell
BT002 (Batch number) inventory first beacause it came first.
That was the concept I am using in my application.
Now I want to sell 15 Qty from 'A' (Inventory).
Then O/p Should be like this :
BT002 - 10
BT003 - 5
Here's My Query :
SELECT ISNULL(SUM(qty),0) AS Qty,batch_no,accept_date FROM RS_GIN_Master
GROUP BY batch_no,accept_date
HAVING ISNULL(SUM(qty),0) <= 15
ORDER BY accept_date asc
O/p Of Given Query :
How can I get O/P like this :
BT002 - 10
BT003 - 5
Any Help will be appreciated.
Thank you in Advance.
This should work for you:
Working sample on Fiddle
CREATE FUNCTION [dbo].[GetBatchAmounts]
(
#requestedAmount int
)
RETURNS
#tBatchResults TABLE
(
Batch nvarchar(50),
Amount int
)
AS
BEGIN
/*This is just a mock of ersults of your query*/
DECLARE #RS_GIN_Master TABLE(
Qty int,
batch_no NVARCHAR(max),
accept_date DATETIME
)
insert into #RS_GIN_Master(Qty,batch_no,accept_date)
SELECT 10,'BT002', CAST(CAST(2014 AS varchar) + '-' + CAST(8 AS varchar) + '-' + CAST(4 AS varchar) AS DATETIME)
insert into #RS_GIN_Master(Qty,batch_no,accept_date)
SELECT 10,'BT003', CAST(CAST(2014 AS varchar) + '-' + CAST(8 AS varchar) + '-' + CAST(5 AS varchar) AS DATETIME)
insert into #RS_GIN_Master(Qty,batch_no,accept_date)
SELECT 10,'BT001', CAST(CAST(2014 AS varchar) + '-' + CAST(8 AS varchar) + '-' + CAST(6 AS varchar) AS DATETIME)
/*---------------------------*/
DECLARE #Qty int
DECLARE #batch_no NVARCHAR(max)
DECLARE #accept_date DATETIME
DECLARE myCursor CURSOR FOR
SELECT Qty, batch_no, accept_date FROM #RS_GIN_Master ORDER BY accept_date ASC
OPEN myCursor
FETCH NEXT FROM myCursor INTO #Qty, #batch_no,#accept_date
WHILE (##FETCH_STATUS = 0 AND #requestedAmount > 0 )
BEGIN
Declare #actualQty int
IF #requestedAmount > #Qty
SET #actualQty = #Qty
ELSE
SET #actualQty = #requestedAmount
INSERT INTO #tBatchResults (batch, Amount)
SELECT #batch_no, #actualQty
set #requestedAmount = #requestedAmount - #actualQty
FETCH NEXT FROM myCursor INTO #Qty, #batch_no,#accept_date
END /*WHILE*/
CLOSE myCursor
DEALLOCATE myCursor
RETURN
END
Just make sure to replace the marked part of the function with your query...
You need to create a stored procedure in your database and taking the quantity from your stock table. And you should also have the id of each record to update that records from where u have taken that qty.
Alter PROCEDURE sp_UpdateStockForSale
#batchNO varchar(10),
#qty decimal(9,3)
AS
BEGIN
Create Table #tmpOutput(ID int identity(1,1), StockID int, batchNo varchar(10), qty decimal(9,3));
SET NOCOUNT ON;
DECLARE #ID int;
DECLARE #Stock Decimal(9,3);
DECLARE #TEMPID int;
Select #TEMPID=(Max(ID)+1) From RS_GIN_Master Where qty > 0 And batch_no = #batchNO;
While (#qty > 0) BEGIN
Select #ID=ID, #Stock=qty From RS_GIN_Master Where qty > 0 And batch_no = #batchNO AND ID < #TEMPID Order By accept_date Desc;
--If Outward Qty is more than Stock
IF (#Stock < #qty) BEGIN
SET #qty = #qty - #Stock;
SET #Stock = 0;
END
--If Outward Qty is less than Stock
ELSE BEGIN
SET #Stock = #Stock - #qty;
SET #qty = 0;
END
Insert Into #tmpOutput(StockID,batchNo,qty)Values(#ID,#batchNO,#Stock);
SET #TEMPID = #ID;
--This will update that record don't need it now.
--Update RS_GIN_Master Set qty = #Stock Where ID=#ID
END
Select StockID, batchNo, qty From #tmpOutput;
END
GO
The above example is not compiled but, you can get the logic how you can retrieve the records from your stock table according to FIFO method. You can use accept_date instead of ID in RS_GIN_Master table. but, i would prefer to make it unique so, if i want to get a specific record then it can be possible.
One query .. like this
This should be tweaked for you case as you have groups and other stuff, is only for example purposes.
;with qty as (
select 15 as value
)
,l as (
select
ROW_NUMBER () over (order by accept_date desc) rn
,*
from xxx
)
,q as (
select
batch_no
,accept_date
,case when value>qty then value-qty else 0 end as remainder
,case when value>qty then qty else value end as used
,rn
from l
cross join qty
where rn=1
union all
select
r.batch_no
,r.accept_date
,case when q.remainder>r.qty then q.remainder-r.qty else 0 end as remainder
,case when q.remainder>r.qty then r.qty else q.remainder end as used
,r.rn
from q
join l r
on q.rn+1 = r.rn
where q.remainder!=0
)
select *
from q
where used != 0
and the fiffle for it http://sqlfiddle.com/#!6/9b063/34/0
Below should work for you
Create table RS_GIN_Master
(id int,
qty int,
batch_no varchar(5),
accept_date Datetime
)
GO
Insert into RS_GIN_Master (id, qty, batch_no, accept_date)
values(1,10,'BT001','2018-04-06')
Insert into RS_GIN_Master (id, qty, batch_no, accept_date)
values(2,10,'BT002','2018-04-04')
Insert into RS_GIN_Master (id, qty, batch_no, accept_date)
values(3,15,'BT003','2018-05-06')
GO
----------------------------
CREATE PROC FIFO
#TakenQty int
AS
BEGIN
WITH cte AS (SELECT *, SUM(qty) OVER (ORDER BY accept_date, id ASC) as CumQty FROM RS_GIN_Master WHERE qty>0)
SELECT TOP ((SELECT COUNT(*) FROM cte WHERE CumQty <#TakenQty)+1) batch_no, accept_date,
CASE
WHEN CumQty<#TakenQty THEN qty
ELSE #TakenQty -(CumQty-Qty)
END AS TakenOut
FROM cte
END
Result
| batch_no | accept_date | TakenOut |
|----------|----------------------|----------|
| BT002 | 2018-04-04T00:00:00Z | 10 |
| BT001 | 2018-04-06T00:00:00Z | 5 |
http://www.sqlfiddle.com/#!18/f7ee7/1

SQL Query to retrieve the last records till the quantity purchased reaches the total quantity in stock

I have a table that have the ItemCode and Quantity in stock and another table that contains the purchases.
I want a query to get the Quantity in stock (ex. Qty = 5) and to take the purchase table to get the purchase invoices by descending order and take the Item Prices.
The Query has to keep retrieving records from the Purchase table according to the Quantity till we reach sum of Quantity in stock = 5.
ex.
**Purchase No ItemCode Qty Cost Price**
2 123 2 100
3 123 10 105
6 123 2 100
8 123 1 90
9 123 2 120
---------------------------------------------
**ItemCode Qty in Stock**
123 5
--------------------------------------------
In this example I want the query to retrieve for me the last 3 invoices (9,8 and 6) because the Qty (2+1+2 = 5)
Is there any suggestion .
Thank you in advance
This script should do the job.
/* SQL SCRIPT BEGIN */
create table #tmp (PurchaseNo int, ItemCode int, Qty int)
insert into #tmp (PurchaseNo, ItemCode, Qty)
select
p1.PurchaseNo, p1.ItemCode, sum(t.Qty) as Qty
from
Purchases p1
join
(
select
p2.PurchaseNo,
p2.ItemCode, p2.Qty
from
Purchases p2
) t on p1.PurchaseNo <= t.PurchaseNo and p1.ItemCode = t.ItemCode
group by p1.PurchaseNo, p1.ItemCode
order by p1.ItemCode, sum(t.Qty) asc
select * From #tmp
where
ItemCode = 123
and
Qty < 5
union
select top 1 * From #tmp
where
ItemCode = 123
and
Qty >= 5
order by PurchaseNo desc
drop table #tmp
/* SQL SCRIPT END */
Hi This can be the solution :
Here I have Used Result Table which will store the result.
I have used three tables Purchage(PurchageNo,ItemCode,Qty) , Stock(ItemCode,QtyInStock) and result(PurchageNo).
Full Workable Code is Here:
DECLARE #ItemCode int;
DECLARE #AvailableQty int;
SET #ItemCode = 123 ;
SET #AvailableQty = (select QtyInStock from Stock where ItemCode = #ItemCode);
SELECT
RowNum = ROW_NUMBER() OVER(ORDER BY PurchageNo),*
INTO #PurchageTemp
FROM Purchage
DECLARE #MaxRownum INT;
SET #MaxRownum = (select COUNT(*)from #PurchageTemp);
DECLARE #Iter INT;
SET #Iter = 1;
DECLARE #QtySum int=0;
DECLARE #QtySumTemp int=0;
DECLARE #CurrentItem int;
WHILE (#Iter <= #MaxRownum and #QtySum <= #AvailableQty)
BEGIN
set #QtySumTemp=#QtySum;
set #QtySumTemp = #QtySumTemp + (SELECT Qty FROM #PurchageTemp WHERE RowNum = #Iter and ItemCode=#ItemCode);
IF #QtySumTemp <= #AvailableQty
BEGIN
set #QtySum=#QtySumTemp;
set #CurrentItem= (SELECT PurchageNo FROM #PurchageTemp WHERE RowNum = #Iter and ItemCode=#ItemCode);
insert into [Result] values (#CurrentItem);
END
SET #Iter = #Iter + 1
END
DROP TABLE #PurchageTemp

NULL value returned when using SUM function on two columns while populating table in SQL Server 2008

INSERT INTO table is producing a NULL value for the returned recordset. I'm trying to add the SUM(m.earnings + m.fpearn) from pfinancial table and userid & minpay from the userbase table.
What am I doing wrong? Also is there a better way to reiterate through the temp table of id's ?
From the tables below I should populate the publsher_monthly table with one unique id per userid, the minpay amount from the userbase and the sum of the m.earnings + m.fpearn columns from the pfinancial table
publisher_monthly table
pmuseris pmminpay pmearnings
5 20 75
6 30 27
userbase table
userid minpay
5 20
6 30
pfinancial table
userid earnings fpearn
5 10 30
5 20 15
6 15 12
My query:
DECLARE #realuserid bigint
CREATE TABLE #tmppmuserids(
idx bigint Primary Key IDENTITY(1,1),
tmppmuserid bigint NULL
)
INSERT INTO #tmppmuserids
SELECT DISTINCT userid FROM pfinancial
DECLARE #i bigint
DECLARE #numrows bigint
SET #i = 1
SET #numrows = (SELECT COUNT(*) FROM #tmppmuserids)
IF #numrows > 0
WHILE (#i <= (SELECT MAX(idx) FROM #tmppmuserids))
BEGIN
SET #realuserid = (SELECT tmppmuserid FROM #tmppmuserids WHERE idx = #i)
--PROBLEM HERE
INSERT INTO publisher_monthly (pmearnings, pmuserid, pmminpay)
SELECT
SUM(m.earnings + m.fpearn), MAX(#realuserid), MAX(u.minpay)
FROM pfinancial m
INNER JOIN userbase u on u.userid = m.userid
WHERE
m.userid = #realuserid AND
(m.created >= DATEADD(MONTH, DATEDIFF(MONTH, -1, GETDATE()),0)) AND
(m.created < DATEADD(MONTH, DATEDIFF(MONTH, -1, GETDATE()) + 1, 0))
--END PROBLEM
SET #i = #i + 1
END
SELECT * FROM #tmppmuserids
SELECT * FROM publisher_monthly
DROP TABLE #tmppmuserids
Place an IsNull around them:
SELECT SUM(IsNull(m.earnings, 0) + IsNull(m.fpearn, 0))
or even around the whole thing:
SELECT IsNull(SUM(m.earnings + m.fpearn), 0)

SQL query to find Missing sequence numbers

I have a column named sequence. The data in this column looks like 1, 2, 3, 4, 5, 7, 9, 10, 15.
I need to find the missing sequence numbers from the table. What SQL query will find the missing sequence numbers from my table? I am expecting results like
Missing numbers
---------------
6
8
11
12
13
14
I am using only one table. I tried the query below, but am not getting the results I want.
select de.sequence + 1 as sequence from dataentry as de
left outer join dataentry as de1 on de.sequence + 1 = de1.sequence
where de1.sequence is null order by sequence asc;
How about something like:
select (select isnull(max(val)+1,1) from mydata where val < md.val) as [from],
md.val - 1 as [to]
from mydata md
where md.val != 1 and not exists (
select 1 from mydata md2 where md2.val = md.val - 1)
giving summarised results:
from to
----------- -----------
6 6
8 8
11 14
I know this is a very old post but I wanted to add this solution that I found HERE so that I can find it easier:
WITH Missing (missnum, maxid)
AS
(
SELECT 1 AS missnum, (select max(id) from #TT)
UNION ALL
SELECT missnum + 1, maxid FROM Missing
WHERE missnum < maxid
)
SELECT missnum
FROM Missing
LEFT OUTER JOIN #TT tt on tt.id = Missing.missnum
WHERE tt.id is NULL
OPTION (MAXRECURSION 0);
Try with this:
declare #min int
declare #max int
select #min = min(seq_field), #max = max(seq_field) from [Table]
create table #tmp (Field_No int)
while #min <= #max
begin
if not exists (select * from [Table] where seq_field = #min)
insert into #tmp (Field_No) values (#min)
set #min = #min + 1
end
select * from #tmp
drop table #tmp
The best solutions are those that use a temporary table with the sequence. Assuming you build such a table, LEFT JOIN with NULL check should do the job:
SELECT #sequence.value
FROM #sequence
LEFT JOIN MyTable ON #sequence.value = MyTable.value
WHERE MyTable.value IS NULL
But if you have to repeat this operation often (and more then for 1 sequence in the database), I would create a "static-data" table and have a script to populate it to the MAX(value) of all the tables you need.
SELECT CASE WHEN MAX(column_name) = COUNT(*)
THEN CAST(NULL AS INTEGER)
-- THEN MAX(column_name) + 1 as other option
WHEN MIN(column_name) > 1
THEN 1
WHEN MAX(column_name) <> COUNT(*)
THEN (SELECT MIN(column_name)+1
FROM table_name
WHERE (column_name+ 1)
NOT IN (SELECT column_name FROM table_name))
ELSE NULL END
FROM table_name;
Here is a script to create a stored procedure that returns missing sequential numbers for a given date range.
CREATE PROCEDURE dbo.ddc_RolledBackOrders
-- Add the parameters for the stored procedure here
#StartDate DATETIME ,
#EndDate DATETIME
AS
BEGIN
SET NOCOUNT ON;
DECLARE #Min BIGINT
DECLARE #Max BIGINT
DECLARE #i BIGINT
IF OBJECT_ID('tempdb..#TempTable') IS NOT NULL
BEGIN
DROP TABLE #TempTable
END
CREATE TABLE #TempTable
(
TempOrderNumber BIGINT
)
SELECT #Min = ( SELECT MIN(ordernumber)
FROM dbo.Orders WITH ( NOLOCK )
WHERE OrderDate BETWEEN #StartDate AND #EndDate)
SELECT #Max = ( SELECT MAX(ordernumber)
FROM dbo.Orders WITH ( NOLOCK )
WHERE OrderDate BETWEEN #StartDate AND #EndDate)
SELECT #i = #Min
WHILE #i <= #Max
BEGIN
INSERT INTO #TempTable
SELECT #i
SELECT #i = #i + 1
END
SELECT TempOrderNumber
FROM #TempTable
LEFT JOIN dbo.orders o WITH ( NOLOCK ) ON tempordernumber = o.OrderNumber
WHERE o.OrderNumber IS NULL
END
GO
Aren't all given solutions way too complex?
wouldn't this be much simpler:
SELECT *
FROM (SELECT row_number() over(order by number) as N from master..spt_values) t
where N not in (select 1 as sequence union
select 2 union
select 3 union
select 4 union
select 5 union
select 7 union
select 10 union
select 15
)
This is my interpretation of this issue, placing the contents in a Table variable that I can easily access in the remainder of my script.
DECLARE #IDS TABLE (row int, ID int)
INSERT INTO #IDS
select ROW_NUMBER() OVER (ORDER BY x.[Referred_ID]), x.[Referred_ID] FROM
(SELECT b.[Referred_ID] + 1 [Referred_ID]
FROM [catalog].[dbo].[Referrals] b) as x
LEFT JOIN [catalog].[dbo].[Referrals] a ON x.[Referred_ID] = a.[Referred_ID]
WHERE a.[Referred_ID] IS NULL
select * from #IDS
Just for fun, I decided to post my solution.
I had an identity column in my table and I wanted to find missing invoice numbers.
I reviewed all the examples I could find but they were not elegant enough.
CREATE VIEW EENSkippedInvoicveNo
AS
SELECT CASE WHEN MSCNT = 1 THEN CAST(MSFIRST AS VARCHAR (8)) ELSE
CAST(MSFIRST AS VARCHAR (8)) + ' - ' + CAST(MSlAST AS VARCHAR (8)) END AS MISSING,
MSCNT, INV_DT FROM (
select invNo+1 as Msfirst, inv_no -1 as Mslast, inv_no - invno -1 as msCnt, dbo.fmtdt(Inv_dt) AS INV_dT
from (select inv_no as invNo, a4glidentity + 1 as a4glid
from oehdrhst_sql where inv_dt > 20140401) as s
inner Join oehdrhst_sql as h
on a4glid = a4glidentity
where inv_no - invno <> 1
) AS SS
DECLARE #MaxID INT = (SELECT MAX(timerecordid) FROM dbo.TimeRecord)
SELECT SeqID AS MissingSeqID
FROM (SELECT ROW_NUMBER() OVER (ORDER BY column_id) SeqID from sys.columns) LkUp
LEFT JOIN dbo.TimeRecord t ON t.timeRecordId = LkUp.SeqID
WHERE t.timeRecordId is null and SeqID < #MaxID
I found this answer here:
http://sql-developers.blogspot.com/2012/10/how-to-find-missing-identitysequence.html
I was looking for a solution and found many answers. This is the one I used and it worked very well. I hope this helps anyone looking for a similar answer.
-- This will return better Results
-- ----------------------------------
;With CTERange
As (
select (select isnull(max(ArchiveID)+1,1) from tblArchives where ArchiveID < md.ArchiveID) as [from],
md.ArchiveID - 1 as [to]
from tblArchives md
where md.ArchiveID != 1 and not exists (
select 1 from tblArchives md2 where md2.ArchiveID = md.ArchiveID - 1)
) SELECT [from], [to], ([to]-[from])+1 [total missing]
From CTERange
ORDER BY ([to]-[from])+1 DESC;
from to total missing
------- ------- --------------
6 6 1
8 8 1
11 14 4
DECLARE #TempSujith TABLE
(MissingId int)
Declare #Id Int
DECLARE #mycur CURSOR
SET #mycur = CURSOR FOR Select Id From tbl_Table
OPEN #mycur
FETCH NEXT FROM #mycur INTO #Id
Declare #index int
Set #index = 1
WHILE ##FETCH_STATUS = 0
BEGIN
if (#index < #Id)
begin
while #index < #Id
begin
insert into #TempSujith values (#index)
set #index = #index + 1
end
end
set #index = #index + 1
FETCH NEXT FROM #mycur INTO #Id
END
Select Id from tbl_Table
select MissingId from #TempSujith
Create a useful Tally table:
-- can go up to 4 million or 2^22
select top 100000 identity(int, 1, 1) Id
into Tally
from master..spt_values
cross join master..spt_values
Index it, or make that single column as PK.
Then use EXCEPT to get your missing number.
select Id from Tally where Id <= (select max(Id) from TestTable)
except
select Id from TestTable
You could also solve using something like a CTE to generate the full sequence:
create table #tmp(sequence int)
insert into #tmp(sequence) values (1)
insert into #tmp(sequence) values (2)
insert into #tmp(sequence) values (3)
insert into #tmp(sequence) values (5)
insert into #tmp(sequence) values (6)
insert into #tmp(sequence) values (8)
insert into #tmp(sequence) values (10)
insert into #tmp(sequence) values (11)
insert into #tmp(sequence) values (14)
DECLARE #max INT
SELECT #max = max(sequence) from #tmp;
with full_sequence
(
Sequence
)
as
(
SELECT 1 Sequence
UNION ALL
SELECT Sequence + 1
FROM full_sequence
WHERE Sequence < #max
)
SELECT
full_sequence.sequence
FROM
full_sequence
LEFT JOIN
#tmp
ON
full_sequence.sequence = #tmp.sequence
WHERE
#tmp.sequence IS NULL
Hmmmm - the formatting is not working on here for some reason? Can anyone see the problem?
i had made a proc so you can send the table name and the key and the result is a list of missing numbers from the given table
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
create PROCEDURE [dbo].[action_FindMissing_Autoincremnt]
(
#tblname as nvarchar(50),
#tblKey as nvarchar(50)
)
AS
BEGIN
SET NOCOUNT ON;
declare #qry nvarchar(4000)
set #qry = 'declare #min int '
set #qry = #qry + 'declare #max int '
set #qry = #qry +'select #min = min(' + #tblKey + ')'
set #qry = #qry + ', #max = max('+ #tblKey +') '
set #qry = #qry + ' from '+ #tblname
set #qry = #qry + ' create table #tmp (Field_No int)
while #min <= #max
begin
if not exists (select * from '+ #tblname +' where '+ #tblKey +' = #min)
insert into #tmp (Field_No) values (#min)
set #min = #min + 1
end
select * from #tmp order by Field_No
drop table #tmp '
exec sp_executesql #qry
END
GO
SELECT TOP 1 (Id + 1)
FROM CustomerNumberGenerator
WHERE (Id + 1) NOT IN ( SELECT Id FROM CustomerNumberGenerator )
Working on a customer number generator for my company. Not the most efficient but definitely most readable
The table has one Id column.
The table allows for Ids to be inserted at manually by a user off sequence.
The solution solves the case where the user decided to pick a high number