How to calculate no. of days between two datetime column in SQL Server - sql

There are two tables, T1 includes start date column and T2 includes end date column. Both the columns have both date and time.
T1 has all the rows unique while T2 can have multiple rows for same id and in all the row the end date column might be different (each row with different date and time).
I want to calculate the difference (no. of days) between End date and the start date while keeping in mind that we have to only pick the last date which is lying in the End date column.

;WITH MaxEndDate AS
(
SELECT
T2.PrimaryKey,
MaxEndDate = MAX(EndDate)
FROM
T2
GROUP BY
T2.PrimaryKey
)
SELECT
T1.PrimaryKey,
T1.StartDate,
M.MaxEndDate,
AmountDays = DATEDIFF(DAY, T1.StartDate, M.MaxEndDate)
FROM
T1
INNER JOIN MaxEndDate AS M ON T1.PrimaryKey = M.PrimaryKey

I would recommend creating a temporary table using T2 something like
Select Distinct ID
,MAX(ENDDATE) As [ENDDATE]
INTO #TMPTABLE1
then you include this into T1
Select A.ID
,Startdate
,B.ENDDATE
,Datediff(day,A.STARTDATE,B.ENDDATE) as DAYS
From T1 as A inner join
#TEMPTABLE1 as B on A.ID = B.ID

--------get no of days among multiple dates----------
DECLARE #i INT , #numrows INT , #days INT , #Fdate DATETIME , #Tdate DATETIME;
SET
#days = 0;
DECLARE #employee_table TABLE ( idx SMALLINT PRIMARY KEY IDENTITY(1,
1) ,
EmpId INT ,
SinceDate DATETIME ,
TillDate DATETIME );
-- populate employee table INSERT #employee_table
SELECT
EmpId ,
SinceDate ,
TillDate
FROM
T_EmpPosting_PIS
WHERE
EmpId = 18
AND OfficeTypeID = 1
ORDER BY
TransDate;
--SELECT
*
FROM
#employee_table -- enumerate the table
SET
#i = 1;
SET
#numrows = ( SELECT
COUNT(*)
FROM
#employee_table );
IF #numrows > 0 WHILE ( #i <= ( SELECT
MAX(idx)
FROM
#employee_table ) ) BEGIN
SET
#Fdate = ( SELECT
SinceDate
FROM
#employee_table
WHERE
idx = #i );
IF ( #i = #numrows ) BEGIN
SET
#Tdate = GETDATE();
END;
ELSE BEGIN
SET
#Tdate = ( SELECT
TillDate
FROM
#employee_table
WHERE
idx = #i );
END;
SET
#days = ( SELECT
DATEDIFF(DAY,
#Fdate,
#Tdate) ) #days;
SET
#i = #i 1;
END;
SELECT
#days;

Related

Transaction was deadlocked on lock resources while reading and inserting

normally it works fine (it recalculates time to time production based on daily meter readings , the problem starts when other apps are inserting data into [dbo].[MeterReading] ( at least I belive this is the source of deadlocks), what would you reccomend to avoid exceptions ?
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
declare #ResultVar float;
DECLARE #Val1 TABLE
(
RowID INT IDENTITY ( 1 , 1 ),
InputId int,
TimeStampUtc1 smalldatetime,
Val1 float
);
DECLARE #Val2 TABLE
(
RowID INT IDENTITY ( 1 , 1 ),
InputId int,
TimeStampUtc2 smalldatetime,
Val2 float
);
WITH CTE
AS
(
SELECT [Val],m.InverterInputId, m.[TimeStampUtc]
,ROW_NUMBER() OVER(PARTITION BY m.InverterInputId ORDER BY m.[TimeStampUtc] DESC) AS RN
FROM [dbo].[MeterReading] AS m
inner join InverterInput AS ii on m.InverterInputId = ii.InverterInputId
inner join Inverter AS i on ii.InverterId = i.InverterId
where ii.InputName = 'TotalYield' and i.PlantId = #plantId and m.[TimeStampUtc] >= #from and m.[TimeStampUtc] <= #to
)
INSERT INTO #Val1 (Val1,InputId,TimeStampUtc1 ) Select [Val],[InverterInputId],[TimeStampUtc] FROM CTE WHERE RN = 1;
WITH CTE
AS
(
SELECT [Val],m.InverterInputId, m.[TimeStampUtc]
,ROW_NUMBER() OVER(PARTITION BY m.InverterInputId ORDER BY m.[TimeStampUtc] ASC) AS RN
FROM [dbo].[MeterReading] AS m
inner join InverterInput AS ii on m.InverterInputId = ii.InverterInputId
inner join Inverter AS i on ii.InverterId = i.InverterId
where ii.InputName = 'TotalYield' and i.PlantId = #plantId and m.[TimeStampUtc] >= #from and m.[TimeStampUtc] <= #to
)
INSERT INTO #Val2 (Val2,InputId,TimeStampUtc2) Select [Val],[InverterInputId],[TimeStampUtc] FROM CTE WHERE RN = 1;
--select Val1 , Val2, Val1-Val2 as Result, [#Val1].InputId,[#Val1].TimeStampUtc1,[#Val2].TimeStampUtc2 from #Val1
select Sum(Val1-Val2) as Result from #Val1
inner join #Val2 on [#Val1].InputId = [#Val2].InputId
END

Iterate through derived value from temp table so it value can be used it a where condition using for loop

I can get the total for each of the items from a derived table like so:
declare #laneNum int
declare #startDate date = '2019-02-07'
declare #class int = 1
declare #id int
if OBJECT_ID('tempdb..#tempLaneNumber') IS NOT NULL
drop table [#tempLaneNumber]
create table #tempLaneNumber
(
LANE_NUMBER INT NULL
)
INSERT INTO #tempLaneNumber (LANE_NUMBER)
SELECT DISTINCT EXIT_LANE
FROM [dbo].[TOLL]
ORDER BY EXIT_LANE DESC
select l.LANE_NUMBER, COUNT(*)
from [dbo].[TOLL] t
inner join #tempLaneNumber l on t.EXIT_LANE = l.LANE_NUMBER
where convert(date, TRXN_DTIME) = #startDate
GROUP BY l.LANE_NUMBER
But what I need now is to iterate through each of the derived values so I can use it in a statement where each result can be placed in a variable. This is what I get in my current code...
I need to put LANE_NUMBER 4 into x4 variable and LANE_NUMBER 6 into x6 variable and so forth. How do I get to it?
EDIT
declare #laneNum int
declare #startDate date = '2019-02-07'
declare #class int = 1
declare #id int
if OBJECT_ID('tempdb..#tempLaneNumber') IS NOT NULL
drop table [#tempLaneNumber]
create table #tempLaneNumber
(
LANE_NUMBER INT NULL
)
INSERT INTO #tempLaneNumber (LANE_NUMBER)
SELECT DISTINCT EXIT_LANE
FROM [dbo].[TOLL]
ORDER BY EXIT_LANE DESC
;WITH CTE AS
(
select l.LANE_NUMBER, COUNT(*) CT
from [dbo].[TOLL] t
inner join #tempLaneNumber l on t.EXIT_LANE = l.LANE_NUMBER
where convert(date, TRXN_DTIME) = #startDate
GROUP BY l.LANE_NUMBER
)
SELECT * FROM CTE where LANE_NUMBER = 4
This is about right but the problem is I would need to hardcode the value "4" or "6" or "7". This sample has 4 results so it's okay. but what if I have 10 or more?
If you want to use the result later on you can use temp table like following.
create table #Results
(
LANE_NUMBER INT NULL,
[Count_LN] INT
)
INSERT INTO #Results
select l.LANE_NUMBER, COUNT(*) CT
from [dbo].[TOLL] t
inner join #tempLaneNumber l on t.EXIT_LANE = l.LANE_NUMBER
where convert(date, TRXN_DTIME) = #startDate
GROUP BY l.LANE_NUMBER
If you want to use the output immediately in the next statement, you can go for CTE like following.
;WITH CTE AS
(
select l.LANE_NUMBER, COUNT(*) CT
from [dbo].[TOLL] t
inner join #tempLaneNumber l on t.EXIT_LANE = l.LANE_NUMBER
where convert(date, TRXN_DTIME) = #startDate
GROUP BY l.LANE_NUMBER
)
SELECT * FROM CTE --USER YOUR CTE HERE
EDIT:
I am not able to understand your requirement fully, by any reason if you want to iterate the table and store every row's column value into a variable, you can try like following.
create table #Results
(
LANE_NUMBER INT NULL,
[Count_LN] INT
)
INSERT INTO #Results
select l.LANE_NUMBER, COUNT(*) CT
from [dbo].[TOLL] t
inner join #tempLaneNumber l on t.EXIT_LANE = l.LANE_NUMBER
where convert(date, TRXN_DTIME) = #startDate
GROUP BY l.LANE_NUMBER
declare #ln int
declare #ct int
While (Select Count(*) From #Results) > 0
Begin
select top 1 #ln = LANE_NUMBER, #ct = [Count_LN] from #Results
-- Use the variable #ln and #ct. For example, if you want to call a sp
-- exec call_someothersp #ln,#ct
Delete From #Results Where LANE_NUMBER = #ln and [Count_LN]=#ct
End

Check if date is exists among the dates then add extra one day to that date

i want to add the days(for example 3 days) to the given date. Before adding days we can check the holidays which are already configured in one table.
Here is my sample code. But i am unable to achieve it.
declare #HolidaysList NVARCHAR(250) = '2014-06-29,2014-07-02,2014-07-18,2014-07-30,2014-10-26'
DECLARE #RDATE DATE = '2014-06-28'
declare #addDays int = 3
declare #count int = 0
while(#count < #addDays)
BEGIN
set #RDATE = DATEADD(DAY,1,#RDATE)
--print '1 ' +cast( #RDATE as nvarchar(100))
if exists(SELECT ITEM FROM fnSplit(#HolidaysList,',') WHERE item = #RDATE)
begin
SELECT #RDATE= CONVERT(VARCHAR(10),DATEADD(DAY,1,#RDATE),101)
PRINT 'if '+ CAST( #HRDATE AS NVARCHAR(100))
end
set #count = #count+1
END
PRINT #RDATE
Here fnSplit is a function which returns a table.
In the above script i have to add 3 days to #RDate. Before adding i have to check holidays list i.e in #HolidaysList. If holiday is come then we can add extra date.
in the above script the output is: 2014-08-03 because 29th is holiday and 2nd is also holiday. so output will be 2014-08-03
You can do this without loops:
DECLARE #Holidays TABLE (Item DATE);
INSERT #Holidays
VALUES ('2014-06-29'),('2014-07-02'),('2014-07-18'),('2014-07-30'),('2014-10-26');
DECLARE #RDATE DATE = '2014-06-28',
#addDays INT = 3;
WITH CTE AS
( SELECT *,
RowNumber = ROW_NUMBER() OVER(ORDER BY d.Date)
FROM ( SELECT DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY object_id), #RDATE)
FROM sys.all_objects
) AS d (Date)
WHERE NOT EXISTS
( SELECT 1
FROM #Holidays AS h
WHERE h.Item = d.Date
)
)
SELECT Date
FROM CTE
WHERE RowNumber = #addDays;
The principal is this part:
SELECT Date = DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY object_id), #RDATE)
FROM sys.all_objects
Will just generate a list of dates onwards from your starting date. You then exclude all holidays from this list using NOT EXISTS, and add a rank to all these days using ROW_NUMBER(). It is then just a case of selecting the date with the required rank.
declare #HolidaysList NVARCHAR(250) = '2014-06-29,2014-06-30,2014-07-01,2014-07-30,2014-07-18,2014-10-26'
DECLARE #RDATE DATE = '2014-06-28'
declare #addDays int = 3
declare #count int = 0
DECLARE #EXISTS int = 0
while(#count < #addDays)
BEGIN
set #RDATE = DATEADD(DAY,1,#RDATE)
if exists(SELECT ITEM FROM fnSplit(#HolidaysList,',') WHERE item = #RDATE)
set #EXISTS = #EXISTS+1
set #count = #count+1
END
if(#EXISTS is not null)
set #RDATE = DATEADD(DAY,#EXISTS,#RDATE)
print #RDATE

SQL Right Join?

Arrrgh, I am not getting this.
I have a table of accounts from Dynamics GP that has 7 columns. I need to fill in the blank months for accounts that didn't have any activity for a given month.
I have created an in memory table #MONTHS that has lines like so:
Account, Description, Year, Month, Month Name, Netchange, PeriodBal
1110000, NULL, 2006, 1, NULL, 0, NULL
This should match up with the same information coming from Dynamics GP. A similar line from GP would look like this:
1110000, Petty Cash, 2006, 1, January, 15.00, 343.97
If we did not spend any petty cash in February, then there would be no line for that account in 2/2006, I want to make the #MONTHS table RIGHT JOIN with the DynamicsGP table so that empty months are filled in.
Here's the abbreviated SQL shortened for readability:
SELECT Z.GPACCOUNTNO,
Z.DESCRIPTION,
Z.FISCALYEAR,
Z.FISCALPERIOD,
Z.FISCALPERIODNAME,
Z.NETCHANGE,
Z.PERIODBALANCE
FROM Z
RIGHT JOIN #MONTHS M
ON Z.GPACCOUNTNO = M.GPACCOUNTNO
AND Z.FISCALPERIOD = M.FISCALPERIOD
AND Z.FISCALYEAR = M.FISCALYEAR
The SQL just runs forever. (i.e. 5 minutes before I lose my patience)
I have verified that my #MONTHS table looks like I intend. I have tried doing a "UNION ALL" with the two tables and it gives me duplicates.
If Table Z does not have a current line for a given account/year/month, I want my #MONTHS table to add that line with a Netchange balance of 0.
Thank you for your help. The full SQL is below.
/* Create in memory table to hold account numbers */
DECLARE #i int
DECLARE #c int
DECLARE #ACCT char(129)
DECLARE #numrows int
DECLARE #numyears int
DECLARE #y int
DECLARE #m int
DECLARE #ACCT_TABLE TABLE (
idx smallint Primary Key IDENTITY(1,1),
account char(129)
)
/* Populate account number table */
INSERT #ACCT_TABLE
select distinct ACTNUMST from SBM01.[dbo].[GL00105]
/* Year table reads available years in the DB */
DECLARE #YEAR_TABLE TABLE (
idx smallint Primary Key IDENTITY(1,1),
YEAR1 smallint
)
/* Populate year table */
INSERT #YEAR_TABLE
SELECT distinct YEAR1 FROM SBM01.dbo.SY40101 ORDER BY YEAR1
/* Create our table of months to UNION to the main accounts */
DECLARE #MONTHS table (
GPACCOUNTNO char(129),
DESCRIPTION char(51),
FISCALYEAR smallint ,
FISCALPERIOD smallint,
FISCALPERIODNAME char(21),
NETCHANGE numeric(19, 5),
PERIODBALANCE numeric(19, 5)
)
/* Here comes the heavy lifting.
We loop over the account numbers and add year and month values.
*/
SET #i = 1
SET #numrows = (SELECT COUNT(*) FROM #ACCT_TABLE)
IF #numrows > 0
WHILE(#i <= (SELECT MAX(idx) FROM #ACCT_TABLE))
BEGIN
/* Get the next account number */
SET #ACCT = (SELECT account FROM #ACCT_TABLE WHERE idx = #i)
SET #c = 1
SET #numyears = (SELECT COUNT(*) FROM #YEAR_TABLE)
WHILE(#c <= (SELECT MAX(idx) FROM #YEAR_TABLE))
BEGIN
SET #y = (SELECT YEAR1 FROM #YEAR_TABLE WHERE idx = #c)
SET #m = '0'
WHILE(#m < '13')
BEGIN
INSERT INTO #MONTHS (GPACCOUNTNO, DESCRIPTION, FISCALPERIOD, FISCALYEAR, FISCALPERIODNAME, NETCHANGE, PERIODBALANCE)
VALUES (#ACCT, NULL, #m, #y, NULL, '0', NULL)
SET #m = #m + 1
END
SET #c = #c + 1
END
SET #i = #i + 1
END
/* We should now have a populated Database */
SELECT Z.GPACCOUNTNO, Z.DESCRIPTION, Z.FISCALYEAR, Z.FISCALPERIOD, Z.FISCALPERIODNAME, Z.NETCHANGE, Z.PERIODBALANCE
FROM ( SELECT RTRIM(B.[ACTNUMST]) AS GPACCOUNTNO,
RTRIM(C.[ACTDESCR]) AS DESCRIPTION,
A.[YEAR1] AS FISCALYEAR,
A.[PERIODID] AS FISCALPERIOD,
E.[PERNAME] AS FISCALPERIODNAME,
ISNULL(A.[PERDBLNC], 0) AS NETCHANGE,
( SELECT ISNULL(SUM(D.[PERDBLNC]), 0)
FROM SBM01.[dbo].[GL10110] D
WHERE D.[ACTINDX] = A.[ACTINDX]
AND D.[YEAR1] = A.[YEAR1]
AND D.[PERIODID] <= A.[PERIODID]
) AS PERIODBALANCE
FROM SBM01.[dbo].[GL10110] A
INNER JOIN SBM01.[dbo].[GL00105] B ON B.[ACTINDX] = A.[ACTINDX]
INNER JOIN SBM01.[dbo].[GL00100] C ON C.[ACTINDX] = A.[ACTINDX]
INNER JOIN SBM01.[dbo].[SY40100] E ON E.[YEAR1] = A.[YEAR1]
AND E.[PERIODID] = A.[PERIODID]
AND E.[SERIES] = 0
UNION ALL
SELECT RTRIM(B.[ACTNUMST]) AS GPACCOUNTNO,
RTRIM(C.[ACTDESCR]) AS DESCRIPTION,
A.[YEAR1] AS FISCALYEAR,
A.[PERIODID] AS FISCALPERIOD,
E.[PERNAME] AS FISCALPERIODNAME,
ISNULL(A.[PERDBLNC], 0) AS NETCHANGE,
( SELECT ISNULL(SUM(D.[PERDBLNC]), 0)
FROM SBM01.[dbo].[GL10111] D
WHERE D.[ACTINDX] = A.[ACTINDX]
AND D.[YEAR1] = A.[YEAR1]
AND D.[PERIODID] <= A.[PERIODID]
) AS PERIODBALANCE
FROM SBM01.[dbo].[GL10111] A
INNER JOIN SBM01.[dbo].[GL00105] B ON B.[ACTINDX] = A.[ACTINDX]
INNER JOIN SBM01.[dbo].[GL00100] C ON C.[ACTINDX] = A.[ACTINDX]
INNER JOIN SBM01.[dbo].[SY40100] E ON E.[YEAR1] = A.[YEAR1]
AND E.[PERIODID] = A.[PERIODID]
AND E.[SERIES] = 0
) Z
RIGHT JOIN #MONTHS M
ON Z.GPACCOUNTNO = M.GPACCOUNTNO
AND Z.FISCALPERIOD = M.FISCALPERIOD
AND Z.FISCALYEAR = M.FISCALYEAR
ORDER BY Z.[GPACCOUNTNO],
M.[FISCALYEAR],
M.[FISCALPERIOD]
Why don't you use the #Months table as the starting point (since it already gives you all the months you need) and fill-in the values from Z if they are available?
SELECT
M.GPACCOUNTNO,
M.DESCRIPTION,
M.FISCALYEAR,
M.FISCALPERIOD,
M.FISCALPERIODNAME,
ISNULL(Z.NETCHANGE, 0) as NETCHANGE
ISNULL(Z.PERIODBALANCE, 0) as PERIODBALANCE
FROM #MONTHS M
LEFT JOIN Z
ON Z.GPACCOUNTNO = M.GPACCOUNTNO
AND Z.FISCALPERIOD = M.FISCALPERIOD
AND Z.FISCALYEAR = M.FISCALYEAR
You can use a SQL case statement to join when null
CREATE TABLE #TMP
(
id int,
[month] datetime
)
INSERT INTO #TMP(id,[month])values(1,GETDATE())
INSERT INTO #TMP(id,[month])values(2,null)
INSERT INTO #TMP(id,[month])values(3,GETDATE())
INSERT INTO #TMP(id,[month])values(4,GETDATE())
CREATE TABLE #TMP2
(
id int,
[month] datetime
)
INSERT INTO #TMP2(id,[month])values(1,GETDATE())
INSERT INTO #TMP2(id,[month])values(2,GETDATE())
INSERT INTO #TMP2(id,[month])values(3,GETDATE())
INSERT INTO #TMP2(id,[month])values(4,GETDATE())
select * from #TMP
select * from #TMP2
SELECT #TMP.[id], case when #TMP.[month] is null then #TMP2.[month] else #TMP.month end
from #tmp
inner join #tmp2 on #tmp.id= #tmp2.id
drop table #tmp,#tmp2

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