Convert rows to columns without using pivot - sql

How can i convert this rows :
id id_name flag tag_name tag_type tag_value
1 1 163 XO c 10
1 1 163 X1 c 0
1 1 163 AM c 5
to this one row :
1 1 163 XO:c:10 X1:c:0 AM:c:5
Without using pivot ??

You're looking to do the equivalent of a group_concat, rather than a pivot
;with src as (select id, id_name, flag, tag_name+':'+tag_type+':'+CONVERT(varchar(5), tag_value) as tag from yourtable t1 )
SELECT distinct
src.id, src.id_name, src.flag,
tags = STUFF((
SELECT ' ' + s.tag
FROM src s
WHERE s.id=src.id
FOR XML PATH(''), TYPE).value('.', 'varchar(max)'), 1, 1, '')
FROM src

Here is one possible way to pivot your data like you've asked. Generally it would be better to perform this sort of string building/manipulation within your application rather then on the database directly. In this example Table2 can be swapped for a temporary table.
-- Create and Seed Table2
SELECT [id], [id_name], [flag], CAST(NULL as varchar(255)) AS [data]
INTO [Table2] FROM [Table1]
GROUP BY [id], [id_name], [flag];
-- Declare variables to hold row
DECLARE #id int, #id_name int, #flag int,
#tag_name varchar(2), #tag_type varchar(1), #tag_value int;
-- Declare the Cursor
DECLARE PivotCursor CURSOR FOR
SELECT [id], [id_name], [flag], [tag_name], [tag_type], [tag_value]
FROM [Table1] ORDER BY [id], [id_name], [flag], [tag_name]
;
-- Use the cursor to update Table2
OPEN PivotCursor;
FETCH NEXT FROM PivotCursor INTO #id, #id_name, #flag, #tag_name, #tag_type, #tag_value;
WHILE ##FETCH_STATUS = 0
BEGIN
UPDATE Table2 SET
[Data] = ISNULL([Data],'')+(CASE WHEN [Data] IS NULL THEN '' ELSE ' ' END)+#TagName+':'+#TagType+':'+CAST(#TagValue as varchar)
WHERE [id] = #id AND [id_name] = #id_name AND [flag] = #flag;
-- Get Next
FETCH NEXT FROM PivotCursor INTO #id, #id_name, #flag, #tag_name, #tag_type, #tag_value;
END
CLOSE PivotCursor;
DEALLOCATE PivotCursor;
The resulting table looks like this:
id id_name flag data
1 1 163 AM:c:5 X1:c:0 XO:c:10

Related

How to reverse string in column a word by word?

I have a table with a varchar(50) column 'Relation' having more than 1000 rows of data like:
P1_P10_P45_P20
P1_P14_P5_P22
P1_P3
P3_P4_P5_P2_P100_P2_P1
I want the output to have reverse order:
P20_P45_P10_P1
P22_P5_P14_P1
P3_P1
P1_P2_P100_P2_P5_P4_P3
Could you please help me achieve this in single query?
Aditi you can use a Tally table to find all _ and then join them back using STUFF + FOR XML PATH combination like below.
I recommend that you read about Tally tables at earliest possible time here
Also the demo link is here
--create table yourtable(Relation nvarchar(50));
--insert into yourtable values
-- ('P1_P14_P5_P22'),
-- ('P1_P3'),
-- ('P3_P4_P5_P2_P100_P2_P1'), ('P1_P3'),
-- ('P3_P4_P5_P2_P100_P2_P1');
;WITH Tally AS (
SELECT 1 as Num
UNION ALL
SELECT Num + 1 FROM Tally WHERE Num < 51
)
,
InputSet AS
(
select *, RN=row_number() over (order by (select 1)) from yourtable
)
,TempSet AS
(
SELECT
Relation,
Num,
RN,
partBetweenUnderscore = SUBSTRING(Relation, Num, ISNULL(LEAD(Num) OVER (Partition by RN ORDER BY Num ASC),LEN('_'+Relation)+1)-Num-1)
FROM
(
SELECT *
FROM InputSet CROSS JOIN Tally
WHERE CHARINDEX('_','_'+Relation,Num)=Num
)T
)
SELECT
Relation,
NewRelation = STUFF(
(SELECT '_' + T1.partBetweenUnderscore FROM TempSet T1 WHERE T1.RN=T2.RN ORDER BY T1.Num DESC FOR XML PATH ('')
),1,1,'')
FROM TempSet T2
GROUP BY RN, Relation
You need to split the stored strings using a splitter, that returns the substrings and the position of each substring. After that you can easily build the desired output.
If you use SQL Server 2017+, you may try a JSON-based approach. You need to transform each string into a valid JSON array (for example P1_P10_P45_P20 into ["'P1","P10","P45","P20"]), parse this array as a table with OPENJSON() and join the rows with STRING_AGG() to generate the expected output:
Table:
CREATE TABLE Data (Relation varchar(50))
INSERT INTO Data (Relation)
VALUES
('P1_P10_P45_P20'),
('P1_P14_P5_P22'),
('P1_P3'),
('P3_P4_P5_P2_P100_P2_P1')
Statement:
SELECT c.Relation
FROM Data d
OUTER APPLY (
SELECT STRING_AGG([value], '_') WITHIN GROUP (ORDER BY CONVERT(int, [key]) DESC)
FROM OPENJSON(CONCAT('["', REPLACE(d.Relation, '_', '","'), '"]'))
) c (Relation)
Result:
Relation
----------------------
P20_P45_P10_P1
P22_P5_P14_P1
P3_P1
P1_P2_P100_P2_P5_P4_P3
First all of the prievious comments are correct, especially that this is a data model problem. Here is a very kludgy solution. I offer it because you only have 1000 records. This is not efficient, nor will it scale up. The following works on MS SQL Server 2017.
Drop table if exists Relation
create table Relation (Relation varchar(50))
INSERT INTO Relation (Relation)
VALUES
('P1_P10_P45_P20'),
('P1_P14_P5_P22'),
('P1_P3'),
('P3_P4_P5_P2_P100_P2_P1');
DROP TABLE IF EXISTS Rev
create table Rev (Relation varchar(50), Rev varchar(50))
DROP TABLE IF EXISTS Z
create table Z (token varchar(50))
declare #Reverse varchar(50)
set #Reverse = ''
declare #token varchar(50)
declare #Relation varchar(50)
declare cur cursor for select * from Relation
open cur
fetch next from cur into #Relation
while ##FETCH_STATUS = 0
begin
with T(Relation, starts, pos) as (
select #Relation, 1, charindex('_', #Relation)
union all
select #Relation, pos + 1, charindex('_', #Relation, pos + 1)
from T
where pos > 0)
insert into Z select substring(#Relation, starts, case when pos > 0 then pos - starts else len(#Relation) end) token
from T
declare cur2 cursor for select token from Z
open cur2
fetch next from cur2 into #token
while ##FETCH_STATUS = 0
begin
set #Reverse = #token + '_' + #Reverse
fetch next from cur2 into #token
end
close cur2
deallocate cur2
set #Reverse = (select left(#Reverse,len(#Reverse)-1))
insert into Rev select #Relation, #Reverse
set #Reverse = ''
delete Z
fetch next from cur into #Relation
end;
close cur
deallocate cur
select * from Rev
SELECT ##version

SQL Server query performance: Nested cursors

I have a stored procedure which is selecting some 1-n relations and related data to the referenced column as XML data.
The purpose is to return a record and it's 1-n relations as ONE record with extra data columns, this is done using adding these related data as XML.
Reference table: (TABLE A)
ID NAME VALUE
---------------------
1 Sepehr 1000
2 Sarah 1001
Related table: (TABLE B)
ID Value FK_Value ORDER TITLE
-------------------------------------
1 A 1000 1 t1
2 B 1000 2 t2
3 C 1000 3 t3
I want to get this output:
ID NAME FK_Value Attribs
-----------------------------------------------------
1 Sepehr 1000 <XML><ID>1</ID><ID>2</ID><ID>3</ID></XML>
2 Sarah 1001 null
Actually I was hoping to create a view to do this, but I couldn't and someone told me its not possible using views.
Finally this is the stored procedure I have written - is this a correct approach or are there any other ways?
DECLARE #T1 table (A_ID int,Attribs XML)
DECLARE db_cursorLegendRowsValues CURSOR FOR
SELECT ID, VALUE
FROM A
OPEN db_cursorLegendRowsValues
FETCH NEXT FROM db_cursorLegendRowsValues INTO #loop_ID, #loop_VALUE
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE db_cursorInternal CURSOR FOR
SELECT TITLE, ORDER
FROM B
WHERE FK_Value = #loop_VALUE
OPEN db_cursorInternal
FETCH NEXT FROM db_cursorInternal INTO #tmpTitle, #ORDER
WHILE ##FETCH_STATUS = 0
BEGIN
SET #querySelect = #querySelect + ', MAX(CASE WHEN order = ' + cast(#ORDER as nvarchar(max)) + ' THEN value END) AS [' +REPLACE (#tmpTitle,' ','_') + '] '
FETCH NEXT FROM db_cursorInternal INTO #tmpTitle, #ORDER
END
CLOSE db_cursorInternal
DEALLOCATE db_cursorInternal
SET #query =
' SELECT ' + cast(#loop_ID as nvarchar(max)) +',(
SELECT A.Value,
'
SET #query = #query + STUFF(#querySelect,1,1,'') + ' FROM A
WHERE [A.Value] = ' + cast(#loop_VALUE as nvarchar(max)) + '
FOR XML RAW (''Item''), root (''Items'') , ELEMENTS XSINIL )'
SET #querySelect = ''
--PRINT(#query)
INSERT into #T1 execute (#query )
FETCH NEXT FROM db_cursorLegendRowsValues INTO #loop_ID, #loop_VALUE
END
CLOSE db_cursorLegendRowsValues
DEALLOCATE db_cursorLegendRowsValues
SELECT * FROM #T1
The whole lot can be condensed to a few lines, no cursors needed at all. This wil be useable in a VIEW:
DECLARE #tblA TABLE(ID INT IDENTITY,NAME VARCHAR(100),VALUE INT);
INSERT INTO #tblA VALUES
('Sepehr',1000)
,('Sarah',1001);
DECLARE #tblB TABLE(ID INT IDENTITY,Value VARCHAR(100),FK_Value INT,[ORDER] INT,TITLE VARCHAR(100));
INSERT INTO #tblB VALUES
('A',1000,1,'t1')
,('B',1000,2,'t2')
,('C',1000,3,'t3');
SELECT a.*
,(SELECT ID FROM #tblB AS b WHERE b.FK_Value=a.VALUE FOR XML PATH(''),ROOT('XML'),TYPE) AS Attribs
FROM #tblA AS a
The result
ID NAME VALUE Attribs
1 Sepehr 1000 <XML><ID>1</ID><ID>2</ID><ID>3</ID></XML>
2 Sarah 1001 NULL
It is possible to do this with just one query - you can use a subquery with your select as follows:
select id
, name
, value as fk_value
, (select id from #table_b b
where a.value = b.fk_value
for xml path (''), root ('xml'))
from #table_a a

How to tune the following query?

This query gives me the desired result but i can't run this query every time.The 2 loops is costing me.So i need to implement something like view.But the logic has temp tables involved which isn't allowed in views as well.so, is there any other way to store this result or change the query so that it will cost me less.
DECLARE #Temp TABLE (
[SiteID] VARCHAR(100)
,[StructureID] INT
,[row] DECIMAL(4, 2)
,[col] DECIMAL(4, 2)
)
DECLARE #siteID VARCHAR(100)
,#structureID INT
,#struct_row INT
,#struct_col INT
,#rows_count INT
,#cols_count INT
,#row INT
,#col INT
DECLARE structure_cursor CURSOR
FOR
SELECT StructureID
,SiteID
,Cols / 8.5 AS Cols
,Rows / 11 AS Rows
FROM Structure
WHERE SellerID = 658 --AND StructureID = 55
OPEN structure_cursor
FETCH NEXT
FROM structure_cursor
INTO #structureID
,#siteID
,#struct_col
,#struct_row
SELECT #rows_count = 1
,#cols_count = 1
,#row = 1
,#col = 1
WHILE ##FETCH_STATUS = 0
BEGIN
WHILE #row <= #struct_row
BEGIN
WHILE #col <= #struct_col
BEGIN
--PRINT 'MEssage';
INSERT INTO #Temp (
SiteID
,StructureID
,row
,col
)
VALUES (
#siteID
,#structureID
,#rows_count
,#cols_count
)
SET #cols_count = #cols_count + 1;
SET #col = #col + 1;
END
SET #cols_count = 1;
SET #col = 1;
SET #rows_count = #rows_count + 1;
SET #row = #row + 1;
END
SET #row = 1;
SET #col = 1;
SET #rows_count = 1;
FETCH NEXT
FROM structure_cursor
INTO #structureID
,#siteID
,#struct_col
,#struct_row
END
CLOSE structure_cursor;
DEALLOCATE structure_cursor;
SELECT * FROM #Temp
Do this with a set-based operation. I think you just want insert . . . select:
INSERT INTO #Temp (SiteID, StructureID, row, col)
SELECT StructureID, SiteID, Cols / 8.5 AS Cols, Rows / 11 AS Rows
FROM Structure
WHERE SellerID = 658;
You should avoid cursors, unless you really need them for some reason (such as calling a stored procedure or using dynamic SQL on each row).
EDIT:
Reading the logic, it looks like you want to insert rows for based on the limits in each row. You still don't want to use a cursor. For that, you need a number generator and master..spt_values is a convenient one, if it has enough rows. So:
with n as (
select row_number() over (order by (select null)) as n
from master..spt_values
)
INSERT INTO #Temp (SiteID, StructureID, row, col)
SELECT StructureID, SiteID, ncol.n / 8.5 AS Cols, nrow.n / 11 AS Rows
FROM Structure s JOIN
n ncol
ON ncol.n <= s.struct_col CROSS JOIN
n nrow
ON nrow <= s.struct_row
WHERE SellerID = 658;
You can generate the number of rows and columns and then CROSS APPLY with those, like below. I've left out your SellerID condition.
;WITH Cols
AS
(
SELECT StructureID, SiteID, CAST(Cols / 8.5 AS INT) AS Col
FROM Structure
UNION ALL
SELECT s.StructureID, s.SiteID, Col - 1
FROM Structure s
INNER JOIN Cols c ON s.StructureID = c.StructureID AND s.SiteID = c.SiteID
WHERE Col > 1
)
, Rows
AS
(
SELECT StructureID, SiteID, CAST(Rows / 11 AS INT) AS Row
FROM Structure
UNION ALL
SELECT s.StructureID, s.SiteID, Row - 1
FROM Structure s
INNER JOIN Rows r ON s.StructureID = r.StructureID AND s.SiteID = r.SiteID
WHERE Row > 1
)
--INSERT INTO #Temp (SiteID, StructureID, row, col)
SELECT s.SiteID, s.StructureID, r.Row, c.Col
FROM Structure s
CROSS APPLY Cols c
CROSS APPLY Rows r
WHERE s.StructureID = c.StructureID AND s.SiteID = c.SiteID
AND s.StructureID = r.StructureID AND s.SiteID = r.SiteID
We can do this by using CROSS APPLY and CTE.
CREATE TABLE Structure(SiteID varchar(20), StructureID int,
Cols decimal(18,2), [Rows] decimal(18,2))
INSERT INTO Structure (SiteID, StructureID, Cols, [Rows])
VALUES
('MN353970', 51,17,22),
('MN272252', 52,17,11)
;WITH RowCTE([Rows]) AS
(
SELECT 1
UNION ALL
SELECT 2
),
ColCTE(Cols) AS
(
SELECT 1
UNION ALL
SELECT 2
)
SELECT SiteID, StructureID, R.Rows, C.Cols
FROM Structure s
CROSS APPLY
(
SELECT Cols FROM ColCTE
) C
CROSS APPLY
(
SELECT [Rows] FROM RowCTE
) R
Sql Fiddle Demo

How to create loop based on value of row?

I have problem when I use my query bellow to have a looping inside the cursor.
data in table1 will be like this:
id | data
----|---------
A | 4
B | 2
C | 5
the result in table2 should be like this:
id | data
----|---------
A | 1
A | 1
A | 1
A | 1
B | 1
B | 1
C | 1
C | 1
C | 1
C | 1
C | 1
I have SQL query with cursor like this:
DECLARE #table2 table ( id VARCHAR(500), data INTEGER)
DECLARE Cur CURSOR FOR
SELECT id, data FROM table1
OPEN Cur
WHILE ( ##FETCH_STATUS = 0 )
BEGIN
DECLARE #LoopNum INTEGER
DECLARE #tempID VARCHAR(255)
DECLARE #tempDATA INTEGER
FETCH NEXT FROM Cur INTO #tempID, #tempDATA
set #LoopNum = 0
WHILE #LoopNum < #tempDATA
BEGIN
INSERT INTO table2 (id, data)
VALUES( #tempID, 1)
SET #LoopNum = #LoopNum + 1
END
END
CLOSE Cur
DEALLOCATE Cur
SELECT * FROM table2
but the query didn't work. is there something wrong with my query?
Thank you.
Use this query to the expected result.
CREATE TABLE #test
(id CHAR(1),data INT)
INSERT #test VALUES ('A',4)
INSERT #test VALUES('B',2)
INSERT #test VALUES('C',5);
SELECT s.id, 1 AS data
FROM #test s
INNER JOIN
master.dbo.spt_values t ON t.type='P'
AND t.number BETWEEN 1 AND s.data
Note: Refer this Why (and how) to split column using master..spt_values?
You actually don't need a loop
IF OBJECT_ID('TEMPDB..#TEMP') IS NOT NULL
DROP TABLE #TEMP
SELECT 'A' AS ID, 4 AS DATA
INTO #TEMP UNION
SELECT 'B', 2 UNION
SELECT 'C', 5
;WITH CTE AS
(
SELECT 1 AS NUMBER
UNION ALL
SELECT NUMBER + 1
FROM CTE
WHERE NUMBER < 100
)
SELECT T.ID, 1
FROM CTE C
INNER JOIN #TEMP T
ON C.NUMBER <= T.DATA
ORDER BY T.ID
Carefull that if you want ot generate a large set of numbers in the CTE it may become slower.
Use a Recursive CTE which will help you to loop through the records.
CREATE TABLE #test
(id CHAR(1),data INT)
INSERT #test
VALUES ('A',4),('B',2),('C',5);
WITH cte
AS (SELECT 1 AS da,id,data
FROM #test a
UNION ALL
SELECT da + 1,id,data
FROM cte a
WHERE da < (SELECT data
FROM #test b
WHERE a.id = b.id))
SELECT id,
1 AS data
FROM cte
ORDER BY id
i used two loops
1. for each row
2. for number for duplicate insert
SET NOCOUNT on;
DECLARE #t table(row int IDENTITY(1,1),id varchar(10),data int)
INSERT INTO #t
SELECT * from xyz
DECLARE #x table(id varchar(10),data int) --table to hold the new data
DECLARE #i int=(SELECT count (*) from xyz) --number of rows main table
DECLARE #y int --number of duplicate
DECLARE #p int=1 --number of rows
WHILE #i!=0 --loop until last row of main table
BEGIN
SET #y=(SELECT data FROM #t WHERE row=#p) --set #y for number of 'row duplicate'
WHILE #y!=0
BEGIN
INSERT INTO #x
SELECT id,1
FROM #t
WHERE row=#p
SET #y=#y-1
END
SET #p=#p+1
SET #i=#i-1
END
SELECT * FROM #x

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