How to implement FIFO in sql - 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

Related

How to aggregate only few rows of data based on one column in SQL

Here I am trying to display the single date column as two different dates - one as start date and the other as end date when we have there is only one transaction type. If we have two credit records - we need to sum up the amounts and the start date should be the 1st-row date and the 2nd-row date as the end date. If there is only one row of transaction type then the same date will be as the start date and end date.
Input :
Id, Date,Transaction_type, amount
1,'01/09/2021','credit',10
2,'02/09/2021','credit',20
3,'03/09/2021','debit',30
4,'04/09/2021','debit',40
5,'05/09/2021','credit',50
6,'06/09/2021','debit',60
7,'07/09/2021','credit',70
8,'08/09/2021','credit',80
9,'09/09/2021','debit',90
Output :
Start_date, End_date, Transaction_type, amount
'01/09/2021','02/09/2021','credit',30
'03/09/2021','04/09/2021','debit',70
'05/09/2021','05/09/2021','credit',50
'06/09/2021','06/09/2021','debit',60
'07/09/2021','08/09/2021','credit',150
'09/09/2021','09/09/2021','debit',90
1st - Do not name columna as Date.
2nd - You should set another column to indicate which credit lines should be used as one.
At this form it's doable, but it's poorly designed. Do you read it with SQL or C#/Python/VBA etc? Any UI? What SQL are u using SQL server? Oracle? Can u use functions, procedures etc.
Escentially with procedure you could:
Create temp table
With while function insert data into that table
Read data from temp table
Destroy temp table.
Create Procedure:
Create Procedure dbo.TestExtract
As
BEGIN TRY
declare #startID as int
declare #endID as int
declare #TT1 as varchar(20)
declare #TT2 as varchar(20)
--create temp table
create table #temp(Start_date date, End_date Date, Transaction_type Varchar(50), amount Numeric(18))
--get first id
set #startID = (select top 1 ID from dbo.Test order by ID asc)
set #endID = (select top 1 ID from dbo.Test order by ID desc)
--loop from lowest to biggest id
while #startID <= #endID
begin
--get adjecent (with id) transaction types
select top 1 #TT1 = Transaction_type from dbo.Test where ID = #startID
select top 1 #TT2 = Transaction_type from dbo.Test where ID = #startID + 1
--compare transaction adjecent types
--if same get startd ID from first, and end date from second and sum both entries and iterate by 2
if #TT1 = #TT2
begin
Insert Into #Temp
select top 1
(select top 1 RecDate from dbo.Test Where ID = #startID) as Start_date,
(select top 1 RecDate from dbo.Test Where ID = #startID + 1) as End_date,
(select top 1 Transaction_type from dbo.Test Where ID = #startID)as Transaction_type,
(select sum(amount) from dbo.Test Where ID = #startID or ID = #startID + 1) as amount
from dbo.Test
set #startID = #startID + 2
end
--if different get only record on one line and iterate by 1
else
begin
Insert Into #Temp
select top 1
(select top 1 RecDate from dbo.Test Where ID = #startID) as Start_date,
(select top 1 RecDate from dbo.Test Where ID = #startID) as End_date,
(select top 1 Transaction_type from dbo.Test Where ID = #startID) as Transaction_type,
(select amount from dbo.Test Where ID = #startID) as amount
from dbo.Test
set #startID = #startID + 1
end
end
select * from #temp
drop table #temp
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0
ROLLBACK TRAN
DECLARE #ErrorMessage NVARCHAR(4000) = ERROR_MESSAGE()
DECLARE #ErrorSeverity INT = ERROR_SEVERITY()
DECLARE #ErrorState INT = ERROR_STATE()
RAISERROR (#ErrorMessage, #ErrorSeverity, #ErrorState);
END CATCH
Go
Extract with procedure:
exec dbo.TestExtract

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

SQL Server Data Query on Possible Rows

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

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

How to Split Sql Int Value into Multiple Rows

Lets say I have the following table in MS SQL 2000
Id | description | quantity |
-------------------------------
1 my desc 3
2 desc 2 2
I need to display multiple rows based on the quantity, so I need the following output:
Id | description | quantity |
-----------------------------
1 my desc 1
1 my desc 1
1 my desc 1
2 desc 2 1
2 desc 2 1
Any ideas how to accomplish this?
This works just fine, no need for any cursors on this one. It may be possible to fangle something out without a number table as well.
Note if going for this kind of solution I would keep a number table around and not recreate it every time I ran the query.
create table #splitme (Id int, description varchar(255), quantity int)
insert #splitme values (1 ,'my desc', 3)
insert #splitme values (2 ,'desc 2', 2)
create table #numbers (num int identity primary key)
declare #i int
select #i = max(quantity) from #splitme
while #i > 0
begin
insert #numbers default values
set #i = #i - 1
end
select Id, description, 1 from #splitme
join #numbers on num <= quantity
DECLARE #Id INT
DECLARE #Description VARCHAR(32)
DECLARE #Quantity INT
DECLARE #Results TABLE (Id INT, [description] VARCHAR(32), quantity INT)
DECLARE MyCursor CURSOR FOR
SELECT Id, [description], quantity
FROM
MyTable
OPEN MyCursor
FETCH NEXT FROM MyCursor INTO #Id, #Description, #Quantity
WHILE ##FETCH_STATUS = 0
BEGIN
WHILE #Quantity > 0
BEGIN
INSERT INTO #Results (
Id,
[description],
quantity
) VALUES (
#Id,
#Description,
1
)
SET #Quantity = #Quantity - 1
END
FETCH NEXT FROM MyCursor INTO #Id, #Description, #Quantity
END
CLOSE MyCursor
DEALLOCATE MyCursor
SELECT *
FROM
#Results
By the way, cursors are generally considered evil. So I will both recommend against something like this, and thank everyone in advance for their flames ;) (But it should work)
Any one looking for a CTE based solution, here is one:
create table test_splitme (Id int, description varchar(255), quantity int)
insert test_splitme values (1 ,'my desc', 3)
insert test_splitme values (2 ,'desc 2', 2)
;
--TSQL solution
with splitcte as(
select Id,description, 1 as quantity, quantity as orig_quantity, 1 as cnt
from test_splitme
union all
select Id, description, quantity, orig_quantity,cnt+1
from splitcte
where cnt < orig_quantity
)
select Id, description, quantity
from splitcte
order by 1