TSQL Multiple count using same table with different JOIN - sql

I have a weird situation and not too sure how to approach it.
I have 2 separate tables:
Table A is submissions
id
submitterQID
nomineeQID
story
Table B is employees
QID
Name
Department
I am trying to get the total number of submissions grouped by department as well as the total number of nominations.
This is what my Stored procedure looks like:
BEGIN
SELECT TOP 50 count(A.[nomineeQID]) AS totalNominations,
count(A.[subQID]) AS totalSubmissions,
B.[DepartmentDesc] AS department
FROM empowermentSubmissions AS A
JOIN empTable AS B
ON B.[qid] = A.[nomineeQID]
WHERE A.[statusID] = 3
AND A.[locationID] = #locale
GROUP BY B.[Department]
ORDER BY totalNominations DESC
FOR XML PATH ('data'), TYPE, ELEMENTS, ROOT ('root');
END
This issue with this is that the JOIN is joining by the nomineeQID only and not the subQID as well.
My end result I am looking for is:
Department Customer Service has 25 submissions and 90 nominations
ORDERED BY the SUM of both counts...
I tried to just JOIN again on the subQID but was told I cant join on the same table twice.
Is there an easier way to accomplish this?

This is a situaton where you'll need to gather your counts independently of each other. Using two left joins will cause some rows to be counted twice in the first left join when the join condition is met for both. Your scenario can be solved using either correlated subqueries or an outer apply gathering the counts on different criteria. I did not present a COUNT(CASE ... ) option here, because you don't have an either-or scenario in the data, you have two foreign keys to the employees table. So, setting up sample data:
declare #empowermentSubmissions table (submissionID int primary key identity(1,1), submissionDate datetime, nomineeQID INT, submitterQID INT, statusID INT, locationID INT)
declare #empTable table (QID int primary key identity(1,1), AreaDesc varchar(10), DepartmentDesc varchar(20))
declare #locale INT = 0
declare #n int = 1
while #n < 50
begin
insert into #empTable (AreaDesc, DepartmentDesc) values ('Area ' + cast((#n % 2)+1 as varchar(1)), 'Department ' + cast((#n % 4)+1 as varchar(1)))
set #n = #n + 1
end
set #n = 1
while #n < 500
begin
insert into #empowermentSubmissions (submissionDate, nomineeQID, submitterQID, StatusID, locationID) values (dateadd(dd,-(cast(rand()*600 as int)),getdate()), (select top 1 QID from #empTable order by newid()), (select top 1 QID from #empTable order by newid()), 3 + (#n % 2) - (#n % 3), (#n % 2) )
set #n = #n + 1
end
And now the OUTER APPLY option:
SELECT TOP 50 E.DepartmentDesc, SUM(N.Nominations) Nominations, SUM(S.TotalSubmissions) TotalSubmissions
FROM #empTable E
OUTER APPLY (
SELECT COUNT(submissionID) Nominations
FROM #empowermentSubmissions A
WHERE A.statusID = 3
AND A.nomineeQID = E.QID
AND A.locationID = #locale
) N
OUTER APPLY (
SELECT COUNT(submissionID) TotalSubmissions
FROM #empowermentSubmissions A
WHERE A.statusID = 3
AND A.submitterQID = E.QID
AND A.locationID = #locale
) S
GROUP BY E.DepartmentDesc
ORDER BY SUM(Nominations) + SUM(TotalSubmissions) DESC

Related

Sql how to convert nest while loops to a CTE using two temp tables

I have one temp table that pulls from the Abstracting table for dx codes and Registration table for the account number. It joins by the VisitID and the dx codes are in order by DiagnosisUrn - #Visits
I need to join it to the DiagTable. The dx codesin this table are the primary ones for billing, in
order by Transaction Number ID with the lowest number being the most primary.
In the end, for each visit id , account id, I need to for the Dx codes to print out
like this example:
Account ICD10 SequenceNumber
AT000118 R06.00 1
AT000118 I25.10 2
AT000118 I65.23 3
AT000118 E11.29 4
AT000118 Z95.1 5
AT000118 E11.65 6
AT000118 E78.2 7
AT000118 E78.1 8
AT000118 E11.42 9
For this particular account (there are many others similar), all of the dx are in the Visit table
however only the first four are in Diag table with the proper TransactionDiagnosisURN/DiagnosisURN/Sequence Number
The While loops reorder the rest of DX codes that were in the visit temp table and adding them to the end of the ones in the Diag table with the result populating the PBDxCodes temp table.
It needs to run by date range - I've changed it to pull a few account numbers instead to try to work on
a CTE. Using loops causes it to run an extremely long time and sometimes it just times out.
I'm getting a recursion error when trying to convert the query to CTE. I have only used basic CTE in the past and it has been a while.
Below is the query using the while loop and then my attempt at trying to convert one loop to a CTE:
While loops:
DECLARE #NumofVisits AS INT
DECLARE #NumberofTxs AS INT
DECLARE #Looper AS INT
DECLARE #ThisVisitID AS VARCHAR(35)
--Get the VisitIDs to find Dx Codes
DROP TABLE IF EXISTS #Visits
SELECT DISTINCT
RAM.VisitID
,RAM.AccountNumber
,ADX.DiagnosisCode_MisDxID
,CAST(ADX.DiagnosisUrnID AS INT) AS ABSDxCode
INTO #Visits
FROM livefdb.dbo.AbsAcct_Diagnoses ADX
JOIN livefdb.dbo.RegAcct_Main AS RAM
ON RAM.VisitID = ADX.VisitID
AND RAM.SourceID = ADX.SourceID
AND RAM.Facility_MisFacID NOT IN ('KK', 'JFJ','MPM')
AND RAM.RegistrationType_MisRegTypeID IN ('AMB','AMBR','BNV')
LEFT JOIN livefdb.dbo.HimRec_Main AS HRM
ON HRM.SourceID = RAM.SourceID
AND HRM.PatientID = RAM.PatientID
WHERE ADX.VisitID IN ('LE1-B20210114114749175', 'AT0-B20191211155403251'
,'ID0-B20201201104450465','OM1-B20210107093907435'
,'AT0-B20191205154143298','LE0-B20200716135146384'
,'PC1-B20210111125154209','SV1-B20210112122435108'
,'UB0-B20200915140417079','ID1-B20201222150226545'
)
SET #NumofVisits = (SELECT COUNT(DISTINCT VisitID) FROM #Visits )
-- Create a table to hold Dx data for each visit
DROP TABLE IF EXISTS #PBDxCodes
CREATE TABLE #PBDxCodes (
EncounterRecordNumber VARCHAR(18),
ICD10DiagnosisCode VARCHAR(15),
SequenceNumber INT,
)
-- Get the Diagnosis from BAR Transactions. The least transaction number is the Primary Charge
-- and the Diagnosis Codes are the Primary Diagnosis codes
WHILE #NumofVisits > 0
BEGIN
DROP TABLE IF EXISTS #UpdateDiag
CREATE TABLE #UpdateDiag (
DiagnosisUrnID INT IDENTITY(1,1) PRIMARY KEY,
VisitID VARCHAR(35),
Diagnosis_MisDxID VARCHAR(50)
)
SET #ThisVisitID = (SELECT TOP 1 VisitID FROM #Visits)
SELECT DISTINCT
BATD.VisitID,
BATD.TransactionNumberID,
CAST(BATD.TransactionDiagnosisUrnID AS INT) AS TransactionDiagnosisUrnID,
BATD.TransactionDiagnosis_MisDxID
INTO #Diag
FROM livefdb.dbo.BarAcct_TxnDiagnoses AS BATD
WHERE BATD.VisitID = #ThisVisitID
SET #NumberofTxs = (SELECT COUNT(DISTINCT TransactionNumberID) FROM #Diag)
SET #Looper = 0
WHILE ( #NumberofTxs > #Looper)
BEGIN
INSERT INTO #UpdateDiag(VisitID, Diagnosis_MisDxID)
SELECT
VisitID,
TransactionDiagnosis_MisDxID
FROM #Diag
WHERE TransactionNumberID =
(CASE WHEN 1 = (SELECT COUNT(DISTINCT TransactionNumberID) FROM #Diag)
THEN (SELECT TOP 1 TransactionNumberID FROM #Diag )
ELSE (SELECT MIN(TransactionNumberID) FROM #Diag)
END)
AND TransactionDiagnosis_MisDxID NOT IN (SELECT Diagnosis_MisDxID FROM #UpdateDiag)
ORDER BY TransactionDiagnosisUrnID
DELETE FROM #Diag WHERE TransactionNumberID = (SELECT MIN(TransactionNumberID) FROM #Diag)
SET #Looper = #Looper + 1
END
-- Are there Diagnosis in Abstracting that are not in BAR
INSERT INTO #UpdateDiag(VisitID, Diagnosis_MisDxID)
SELECT
VisitID,
DiagnosisCode_MisDxID
FROM #Visits
WHERE #Visits.VisitID = #ThisVisitID
AND #Visits.DiagnosisCode_MisDxID NOT IN (SELECT Diagnosis_MisDxID FROM #UpdateDiag)
ORDER BY #Visits.ABSDxCode
-- Add this visit to the temp table holding final results
INSERT INTO #PBDxCodes (EncounterRecordNumber,SequenceNumber,ICD10DiagnosisCode,SourceSystem)
SELECT DISTINCT
#Visits.AccountNumber AS EncounterRecordNumber,
#UpdateDiag.DiagnosisUrnID AS SequenceNumber,
#UpdateDiag.Diagnosis_MisDxID AS ICD10DiagnosisCode
FROM #UpdateDiag
INNER JOIN #Visits ON #UpdateDiag.VisitID = #Visits.VisitID
DELETE FROM #Visits WHERE VisitID = #ThisVisitID
SET #NumofVisits = #NumofVisits - 1
DROP TABLE #Diag, #UpdateDiag
END
SELECT * FROM #PBDxCodes
WHERE EncounterRecordNumber='AT0001184503'
ORDER BY EncounterRecordNumber, SequenceNumber
DROP TABLE #Visits, #PBDxCodes
CTE Attempt:
DECLARE #NumofVisits AS INT
DECLARE #NumberofTxs AS INT
DECLARE #Looper AS INT
DECLARE #ThisVisitID AS VARCHAR(35)
--Get VisitID,Account Nos and Dx in Abstracting.
DROP TABLE IF EXISTS #Visits
SELECT DISTINCT
RAM.VisitID
,RAM.AccountNumber
,ADX.DiagnosisCode_MisDxID
,CAST(ADX.DiagnosisUrnID AS INT) AS ABSDxCode
INTO #Visits
FROM livefdb.dbo.AbsAcct_Diagnoses ADX
JOIN livefdb.dbo.RegAcct_Main RAM
ON ADX.SourceID=RAM.SourceID
AND ADX.VisitID=RAM.VisitID
AND RAM.Facility_MisFacID NOT IN ('KK', 'JFJ','MPM')
AND RAM.RegistrationType_MisRegTypeID IN ('AMB','AMBR','BNV')
WHERE ADX.VisitID IN ('AT0-B20191211155403251','LE1-B20210114114749175')
SET #NumofVisits = (SELECT COUNT(DISTINCT VisitID) FROM #Visits);
WITH CTE AS (
SELECT
V.ABSDxCode
,#NumofVisits AS n
,V.VisitID
,V.DiagnosisCode_MisDxID
FROM #Visits V
UNION ALL
SELECT
C.ABSDxCode
,C.n+1
,V.VisitID AS VisitID
,V.DiagnosisCode_MisDxID
FROM CTE C
INNER JOIN #Visits AS V ON V.VisitID=C.VisitID
)
SELECT C.ABSDxCode, C.DiagnosisCode_MisDxID
FROM CTE C
WHERE C.n=(SELECT MAX(n) FROM CTE WHERE ABSDxCode=C.ABSDxCode)

SQL: Optimizing Recursive CTE

Example table structure:
EmployeeId TeamleaderId TopTeamleaderId LEVEL ParentTree CompanyId
1 0 0 0 NULL 1
2 1 1 1 2>1 1
3 2 1 2 3>2>1 1
TeamleaderId is foreignKey reference to EmployeeId in the same table
Goal:
Whenever a row is inserted in the table with EmployeeId, TeamleaderId, CompanyId automatically populate TopTeamleaderId, LEVEL and ParentTree with AFTER INSERT trigger
Code:
WITH CTE AS (
SELECT EmployeeId, TeamleaderId,0 AS [Level], CAST(EmployeeId AS varchar(100)) AS Heirarchy, TopTeamleaderId
FROM dbo.Employee
WHERE EmployeeId IN (SELECT EmployeeId FROM Employee WHERE TeamleaderId IS NULL
AND CompanyId IN(SELECT DISTINCT CompanyId FROM INSERTED))
UNION ALL
SELECT mgr.EmployeeId, mgr.TeamleaderId, CTE.[Level] +1 AS [Level],
CAST(( CAST(mgr.EmployeeId AS VARCHAR(100)) + '>' + CTE.Heirarchy) AS varchar(100)) AS Heirarchy, CTE.TopTeamleaderId
FROM CTE
INNER JOIN dbo.Employee AS mgr
ON TaskCTE.EmployeeId = mgr.ParentTeamleaderId
)
UPDATE Employee SET [LEVEL] = TC.[LEVEL], ParentTree = TC.Heirarchy, TopTeamleaderId = TC.TopTeamleaderId
FROM dbo.Employee AS Employee
JOIN (SELECT * FROM CTE WHERE EmployeeId IN(SELECT DISTINCT EmployeeId FROM INSERTED) AND ParentTeamleaderId IS NOT NULL) TC
ON
Employee.EmployeeId = TC.EmployeeId
Problem:
Imagine there are like 1000000 employees in a company, this query would take a long time to execute. How to optimize it so that only the parents of the inserted row are taken in to account?
Recursive CTE's are great, but as you can see the perfomance can suffer with larger hierarchies. It is my firm belief that there is no shame in temp tables.
The following will generate a 200K point hierarchy in 0.784 seconds.
Example
Select EmployeeId
,TeamleaderId
,Lvl=1
,TopTeamleaderId = 0
,ParentTree=cast(EmployeeId as varchar(500))
,CompanyID
Into #TempBld
From Employee
Where TeamleaderId is null
Declare #Cnt int=1
While #Cnt<=30 --<< Set Your Max Level
Begin
Insert Into #TempBld
Select A.EmployeeId
,A.TeamleaderId
,B.Lvl+1
,IIF(B.Lvl=1,B.EmployeeId,B.TopTeamleaderId)
,concat(A.EmployeeId,'>',B.ParentTree)
,A.CompanyID
From Employee A
Join #TempBld B on (B.Lvl=#Cnt and A.TeamleaderId=B.EmployeeId)
Set #Cnt=#Cnt+1
End
--Select * from #TempBld Order by ParentTree
Returns

Is it possible to group unlike items and count them together?

Given the following table:
Chain Name
123 Company 1
124 Other Company 1
123 Whatever Company
125 This One
126 That One
125 Another One
127 Last One
I get the following results when I do a Count on the Chain column:
123 2
124 1
125 2
126 1
127 1
Is it possible to group Chain 123 and 124 so they're counted together? Also group 125 and 126? The modified results would look like this:
123/124 3
125/126 3
127 1
My SQL looks like this:
SELECT Table1.Chain, Count(*) as [Count]
FROM Table1 LEFT JOIN Table2 on Table1.Chain = Table2.Chain
WHERE (((Table1.Chain) IN (Table2.Chain)))
GROUP BY Table1.Chain
ORDER BY Table1.Chain;
Thank you!
Depending upon your needs, this might be a bit of a hack, but I would probably add a table to store the Chain and ChainGroup that you are seeking. Something like this:
Chain ChainGroup
123 123/124
124 123/124
125 125/126
126 125/126
127 127/128
Then, in the query, I would join to this table and instead of grouping by Chain I would group by ChainGroup.
I would prefer this over something like a nested IIF statement as those get pretty difficult to debug, and odds are you'll have additional groupings in the future which would be trivial to add to the table and have the new grouping automatically appear in the query.
yes, you can:
SELECT min(Table1.Chain) & '/' & max(Table1.Chain) as chain, Count(*) as [Count]
FROM Table1 LEFT JOIN Table2 on Table1.Chain = Table2.Chain
WHERE (((Table1.Chain) IN (Table2.Chain)))
GROUP BY int((Table1.Chain-1)/2)
ORDER BY min(Table1.Chain);
You can use a nested Iif statement. Hopefully I've got all my parentheses right below! :-)
SELECT Iif(Table1.Chain="123", "123/124",
Iif(Table1.Chain="124", "123/124",
Iif(Table1.Chain="125", "125/126",
Iif(Table1.Chain="126", "125/126", Table1.Chain)))) as [Chain]
, Count(*) as [Count]
FROM Table1 LEFT JOIN Table2 on Table1.Chain = Table2.Chain
WHERE (((Table1.Chain) IN (Table2.Chain)))
GROUP BY Iif(Table1.Chain="123", "123/124",
Iif(Table1.Chain="124", "123/124",
Iif(Table1.Chain="125", "125/126",
Iif(Table1.Chain="126", "125/126", Table1.Chain))))
ORDER BY Table1.Chain;
You could also move the case statement into a subquery in your from clause or a common table expression if you don't want to write it twice in your query.
consider something like:
SELECT
chain_group, COUNT(*) FROM (
SELECT
Table1.Chain,
switch(Table1.Chain IN("123","124"), "123/124",
Table1.Chain IN("125","126"),"125/126",
Table1.Chain) AS chain_group
FROM
Table1 INNER JOIN
Table2 ON
Table1.Chain = Table2.Chain) t
GROUP BY chain_group
ORDER BY chain_group
You can see the below example
----- Make main Table
CREATE TABLE #test
( id int , Name varchar(100))
INSERT #test(id,Name)
values (123,'Company 1'),
(124,'Other Company 1'),
(123, 'Whatever Company'),
(125, 'This One'),
(126 , 'That One'),
(125, 'Another One'),
(127, 'Last One')
CREATE TABLE #temp
(rowID INT IDENTITY(1,1) , ID INT ,cnt INT )
CREATE TABLE #tempResult
(ID VARCHAR(20) ,cnt INT )
INSERT INTO #temp(ID,cnt)
SELECT ID ,COUNT(1) cnt FROM #test GROUP BY ID
DECLARE #rowCnt INT , #TotalCnt INT , #even INT , #odd INT ,
#idNum VARCHAR(20) , #valueCnt INT , #inStart INT = 1
SET #rowCnt = 1
SET #even = 1
SET #odd = 2
SELECT #TotalCnt = COUNT(1) FROM #temp
WHILE #rowCnt <= #TotalCnt
BEGIN
SET #inStart = 1
SET #odd = #rowCnt
SET #even = #rowCnt + 1
SET #idNum = ''
SET #valueCnt = 0
WHILE #inStart <= 2
BEGIN
IF #inStart = 1
Begin
SELECT #idNum = Convert(VARCHAR(5),ID) , #valueCnt = cnt
FROM #temp WHERE rowID = #odd
End
ELSE
BEGIN
SELECT #idNum = #idNum + '/' + Convert(VARCHAR(5),ID) , #valueCnt = #valueCnt + cnt
FROM #temp WHERE rowID = #even
END
SET #inStart = #inStart + 1
END
INSERT INTO #tempResult (ID, Cnt)
VALUES (#idNum,#valueCnt)
SET #rowCnt = #rowCnt + 2
END
SELECT *
FROM #tempResult

sql query help - trying to get rid of temp tables

I have the following tables -
Resource
--------------------
Id, ProjectId, Hours, ApproverId
Project
--------------------
Id, Name
The input is ApproverId. I need to retrieve all the rows that have matching ApproverId (simple enough). And for every resource that I get back, I also need to get their hours (same table) whose approverId is not the one that is passed in (business requirement, to be grayed out in the UI). What I'm doing right now is - get all resources based on ApproverId, stored them in a temp table, then do a distinct on Resource.Id, store it in a different temp table, and then for every Resource.Id, get the rows where the ApproverId is not the one that is passed. Can I combine it all in a single query instead of using temp tables?
Thanks!
Edit: I'm using SQL Server 2008 R2.
Edit 2: Here's my stored procedure. I have changed the logic slightly after reading the comments. Can we get rid of all temp tables and make it faster -
ALTER PROCEDURE GetResourceDataByApprover
#ApproverId UNIQUEIDENTIFIER
AS
CREATE TABLE #Table1
(
Id SMALLINT PRIMARY KEY
IDENTITY(1, 1) ,
ResourceId UNIQUEIDENTIFIER
)
CREATE TABLE #Table2
(
ResourceId UNIQUEIDENTIFIER ,
ProjectId UNIQUEIDENTIFIER ,
ProjectName NVARCHAR(1024)
)
INSERT INTO #Table1
SELECT DISTINCT
ResourceId
FROM dbo.Resource T
WHERE T.ApproverId = #ApproverId
DECLARE #i INT
DECLARE #numrows INT
DECLARE #resourceId UNIQUEIDENTIFIER
SET #i = 1
SET #numrows = ( SELECT COUNT(*)
FROM #Table1
)
IF #numrows > 0
WHILE ( #i <= ( SELECT MAX(Id)
FROM #Table1
) )
BEGIN
SET #resourceId = ( SELECT ResourceId
FROM #Table1
WHERE Id = #i
)
INSERT INTO #Table2
SELECT
T.ResourceId ,
T.ProjectId ,
P.Name AS ProjectName
FROM dbo.[Resource] T
INNER JOIN dbo.Project P ON T.ProjectId = P.ProjectId
WHERE T.ResourceId = #resourceId
SET #i = #i + 1
END
SELECT *
FROM #Table1
SELECT *
FROM #Table2
DROP TABLE #Table1
DROP TABLE #Table2
This query should return two rows for every resource, one for the specified approver and one for all other approvers.
SELECT
Id,
CASE
WHEN ApproverId=#approverId THEN 'SpecifiedApprover'
ELSE 'OtherApprover'
END AS Approver,
SUM(Hours) AS Hours
FROM Resource
GROUP BY
Id,
CASE
WHEN ApproverId=#approverId THEN 'SpecifiedApprover'
ELSE 'OtherApprover'
END
Do you want to know how concrete Approver wastes his time?
SELECT p.Id, p.Name, SUM(r.Hours) as TotalHours
FROM Resource r
LEFT JOIN Project p
ON r.ProjectId = p.Id
WHERE ApproverId = %ConcreteApproverId%
GROUP BY p.Id, p.Name
HAVING SUM(r.Hours) > 0
This query will produce this table example:
+-----+----------+-------+
| Id | Project | Hours |
+-----+----------+-------+
| 203 | ProjectA | 25 |
| 202 | ProjectB | 34 |
| 200 | ProjectC | 46 |
+-----+----------+-------+

How to find missing id in the table

I have column looks like below
SID101
SID102
SID103
SID105
SID107
In the above criteria i need to find missed SID numbers. SID104 and SID 106 are missed while ordering.
How can i find the missed id numbers.Could any one help me finding it.
Thanks in advance.
If your table contains gaps with length more than 1 item, you can use this query:
declare #t table(s varchar(20))
insert #t values ('SID101'),('SID102'),('SID103'),('SID105'),('SID108');
with cte as
(
select substring(t.s, 4, len(t.s)) [i]
from #t t
)
select 'SID' + cast(m.number as varchar(20))
from master..spt_values m
left join cte c on c.i = m.number
where [Type] = 'P'
and m.number >= (select min(i) from cte)
and m.number <= (select max(i) from cte)
and c.i is null
Output:
-----------------------
SID104
SID106
SID107
Something like this should work:
DECLARE #i INT;
SET #i = 100;
CREATE TABLE #idsToCheck (checkId varchar(100));
WHILE (#i < 200)
BEGIN
INSERT INTO #idsToCheck VALUES ('SID' + CONVERT(varchar(100), #i));
SET #i = #i + 1;
END
SELECT * FROM #idsToCheck itc
LEFT OUTER JOIN MainTable mt ON itc.checkId = mt.realId
WHERE mt.realId = NULL
DROP TABLE #idsToCheck
... where MainTable is your table containing the SID101, SID102, etc. column values, and MainTable.realId is the column containing those IDs. Modify the #i initial value and number in the while loop condition based on which SIDs you want to check from/to.
It's difficult. With
SELECT COUNT(*),MAX(CAST(REPLACE(y.name,'SID','') AS INT)) AS col_max FROM
sys.objects x INNER JOIN sys.columns y ON x.object_id=y.object_id
WHERE x.name='<TABLE_NAME>'
you should know, how much columns are missing (i.e. COUNT(*) is 5 and col_max is 107)
When you have a table, which contains only one column with all possible IDs from 1 to max (i.e. 100,101,102,103,104,...,132) then you could do
SELECT * FROM (
SELECT CAST(REPLACE(y.name,'SID','') AS INT) AS col_id FROM
sys.objects x INNER JOIN sys.columns y ON x.object_id=y.object_id
WHERE x.name='<TABLE_NAME>'
) a
RIGHT JOIN <TABLE_IDS> b ON a.col_id=b.id
WHERE a.col_id IS NULL AND b.id<=(
SELECT MAX(CAST(REPLACE(y.name,'SID','') AS INT)) AS col_max FROM
sys.objects x INNER JOIN sys.columns y ON x.object_id=y.object_id
WHERE x.name='<TABLE_NAME>'
)
EDIT: sorry, I've seen just now, that these values aren't column names, but values. My solution will find missing column names
 Declare #St int
declare #end int
set #st = CAST( (select RIGHT( max(data),4) from orderno)as int)
set #end = CAST( (select RIGHT( min(data),4) from orderno)as int)
create table #temp(data int)
while(#St <= #end )
begin
insert into #temp values(#St)
set #St = #St +1
end
select * from orderno
select * from #temp
select data from #temp where data not in (select cast(RIGHT(data,4))
declare #t table(s varchar(20))
insert #t values ('SID101'),('SID102'),('SID103'),('SID105'),('SID107');
with cte as
(
select substring(t.s, 4, len(t.s)) [i]
from #t t
)
select 'SID' + cast(t1.i + 1 as varchar(20))
from cte t1
join cte t2 on t2.i > t1.i
and not exists(
select 1
from cte c3
where c3.i > t1.i and c3.i < t2.i
)
where t2.i <> t1.i + 1
Output:
-----------------------
SID104
SID106