Fill in missing rows in a table SQL - sql

I have a report that needs the top 18 id codes for each case. Some cases only have all 18 rows and some only have a few. Here is an example of the output:
Case idcode value
2 3 122
2 6 52
2 15 121
3 1 111
3 3 555
3 6 322
What I need the output to have is 18 rows per record (idcodes 1-18) and to put "none" for the value if it is added. What is the best way to add in the missing rows if I do not know which ones are missing ahead of time?
Here is my query:
SELECT
rcl.CaseCaseId as Case, cce.StringValue as Value, cce.CorpIdCodeId as idcode
FROM
CaseIdCodeEntry AS cce
INNER JOIN
CorpIdCodes AS cid ON cce.CorpIdCodeId = cid.CorpIdCodeId
INNER JOIN
PhdRpt.ReportCaseList_542 AS rcl ON cce.CaseCaseId = rcl.CaseCaseId
WHERE
(cce.CorpIdCodeId < 19)

I would use a recursive CTE to auto-generate a numbered list of 1-18, and then LEFT JOIN off of that. Then use a CASE statement to adjust the Value field.
;WITH cte AS
( SELECT DISTINCT CaseCaseId AS CaseID, 1 AS idcode
FROM PhdRpt.ReportCaseList_542 UNION ALL
SELECT CaseID, idcode+1 FROM cte WHERE idcode < 18 )
SELECT cte.CaseID AS [Case],
CASE WHEN cce.CorpIdCodeId IS NULL THEN 'None' ELSE cce.StringValue END AS Value,
cte.idcode AS idcode
FROM cte
LEFT JOIN CaseIdCodeEntry cid ON cid.CorpCodeId = cte.idcode
LEFT JOIN CorpIdCodes cid ON cce.CorpIdCodeId = cid.CorpIdCodeId
LEFT JOIN PhdRpt.ReportCaseList_542 rcl ON cce.CaseCaseId = rcl.CaseCaseId

Try this seems works fine
create table #temp(iCase int, idcode int,value int)
Insert into #temp values(2,3,122)
Insert into #temp values(2,6,52)
Insert into #temp values(2,15,121)
Insert into #temp values(3,1,11)
Insert into #temp values(3,3,555)
Insert into #temp values(3,6,322)
create table #Val(Id int)
declare #count int =1
while (#count<=18)
begin
insert into #Val values(#count)
set #count=#count+1
end
DECLARE #CaseId INT
DECLARE #DataCursor CURSOR
SET #DataCursor = CURSOR FOR
SELECT distinct iCase
From #temp
OPEN #DataCursor
FETCH NEXT
FROM #DataCursor INTO #CaseId
WHILE ##FETCH_STATUS = 0
BEGIN
INSERT INTO #temp
SELECT #CaseId,Id,null
FROM #Val
WHERE Id NOT IN (
SELECT idcode
FROM #temp
WHERE iCase=#CaseId )
FETCH NEXT
FROM #DataCursor INTO #CaseId
END
CLOSE #DataCursor
DEALLOCATE #DataCursor
Select * from #temp

TL;DR SQL Fiddle
Like Jon of All Trades I also favour the use of a sequence (or number) table.
CREATE TABLE Seq ( seq_num int)
You can use this to populate the missing rows from your original data; assuming you have a table T holding your data
CREATE TABLE T ( case_num int
,code_id int
,value char(4))
You can use the following query to get your fully populated results
WITH All_Codes AS (
SELECT DISTINCT case_num, seq_num AS code_id
FROM T, Seq
)
SELECT All_Codes.case_num
,All_Codes.code_id
,CASE WHEN value IS NULL THEN 'none' ELSE value END AS value
FROM All_Codes LEFT JOIN T
ON All_Codes.case_num = T.case_num AND All_Codes.code_id = T.code_id
The result is
case_num code_id value
2 1 none
2 2 none
2 3 122
2 4 none
2 5 none
2 6 52
2 7 none
2 8 none
2 9 none
2 10 none
2 11 none
2 12 none
2 13 none
2 14 none
2 15 121
2 16 none
2 17 none
2 18 none
3 1 111
3 2 none
3 3 555
3 4 none
3 5 none
3 6 322
3 7 none
3 8 none
3 9 none
3 10 none
3 11 none
3 12 none
3 13 none
3 14 none
3 15 none
3 16 none
3 17 none
3 18 none

Humpty and Matt's solutions should work, but as a purist I'd recommend using a Numbers table rather than a cursor or CTE. It's simpler (IMHO) and for large quantities it should be significantly faster:
SELECT
X.CaseId, N.Number, X.Value
FROM
Numbers AS N
LEFT JOIN
(
SELECT
CICE.CaseCaseId AS CaseId, CICE.StringValue AS Value, CICE.CorpIdCodeId AS IdCode
FROM
CaseIdCodeEntry AS CICE
INNER JOIN CorpIdCodes AS CIC ON CICE.CorpIdCodeId = CIC.CorpIdCodeId
INNER JOIN PhdRpt.ReportCaseList_542 AS RCL ON CICE.CaseCaseId = RCL.CaseCaseId
) AS X ON N.Number = X.IdCode
WHERE
N.Number BETWEEN 1 AND 18
Incidentally, are you sure you need to join CaseIdCodeEntry to CorpIdCodes and ReportCaseList_542? If they're there to filter the data, that's fine, but as they're not contributing to the output I have to wonder.

This is the solution that I used. I used some of #Jon of All Trades and #huMpty duMpty code.
SELECT cice.CaseCaseId, cice.CorpIdCodeId, cice.DateValue, cice.DoubleValue, cice.StringValue, cic.Label
into #TT
FROM CaseIdCodeEntry AS cice INNER JOIN
CorpIdCodes AS cic ON cice.CorpIdCodeId = cic.CorpIdCodeId INNER JOIN
PhdRpt.ReportCaseList_542 AS rcl ON cice.CaseCaseId = rcl.CaseCaseId
WHERE (cice.CorpIdCodeId <= 18)
ORDER BY cice.CaseCaseId, cice.CorpIdCodeId
create table #Val(Id int)
declare #count int =1
while (#count<=18)
begin
insert into #Val values(#count)
set #count=#count+1
end
DECLARE #CaseId INT
DECLARE #DataCursor CURSOR
SET #DataCursor = CURSOR FOR
SELECT distinct CaseCaseId
From #TT
OPEN #DataCursor
FETCH NEXT
FROM #DataCursor INTO #CaseId
WHILE ##FETCH_STATUS = 0
BEGIN
INSERT INTO #TT
SELECT #CaseId,Id,null, null, null, 'none'
FROM #Val
WHERE Id NOT IN (
SELECT CorpIdCodeId
FROM #TT
WHERE CaseCaseId=#CaseId )
FETCH NEXT
FROM #DataCursor INTO #CaseId
END
CLOSE #DataCursor
DEALLOCATE #DataCursor
Select * from #TT
order by CaseCaseId, CorpIdCodeId
drop table #TT, #Val

Related

Difference between two columns into separate rows for each value between the two

I'm having trouble explaining my problem but I have this table
ID START END
1 10 12
2 30 31
3 11 13
and want something like this:
ID NUMBER
1 10
1 11
1 12
2 30
2 31
3 11
3 12
3 13
I need the all unique whole numbers between the two columns transform into separate rows.
Here's how I want the transformation to look like
I haven't tried anything because I don't even know how to call such procedure so any help is appreciated
If you don't have a numbers table (highly recommended), you can use an ad-hoc tally table in concert with a CROSS APPLY
Example
Select A.ID
,B.Number
From YourTable A
Cross Apply ( Select Top ([End]-[Start]+1)
Number=[START]-1+Row_Number() Over (Order By (Select NULL))
From master..spt_values n1, master..spt_values n2
) B
Returns
ID Number
1 10
1 11
1 12
2 30
2 31
3 11
3 12
3 13
In SQL Server, you can use a recursive CTE:
with cte as (
select id, [start] as number, [end] as end_number
from t
union all
select id, number + 1
from cte
where number < end_number
)
select id, number
from cte;
Note: If the span can exceed 100, you need option (maxrecursion) for the query.
Recursive CTEs are usually a bit slower than a numbers table. However, I find them much faster than I would expect.
--create table NewTable
--(
--ID int,
--NUMBER int
--)
DECLARE #ID nvarchar(50)
declare #START int
declare #END int
DECLARE Cursor_Name CURSOR
FOR
select ID, [START], [End] from tblSequences
OPEN Cursor_Name
FETCH NEXT FROM Cursor_Name INTO #ID,#START,#END
WHILE ##FETCH_STATUS = 0
begin
WHILE #START<=#END
begin
insert into NewTable (ID, NUMBER) Values (#ID, #START)
set #START = #START+1
end
FETCH NEXT FROM Cursor_Name INTO #ID,#START,#END
end
CLOSE Cursor_Name
DEALLOCATE Cursor_Name
select * from NewTable

In SQL , how to build a loop that copies a row number of times

could someone please help? My starting table looks like this with 2 fields:
Name Counter
dave 2
Joe 3
I want my result to look like this:
Name Counter
dave 1
dave 2
joe 1
joe 2
joe 3
Essentially creating n number of records base on the counter and starts at 1. I tried to do a loop using counter as a variable, but the code just runs nonstop.. could someone help?
A procedural SQL Server solution:
declare #input table
(
name nvarchar(100)
,wantedrows int
,processed bit
,id uniqueidentifier
);
declare #output table
(
name nvarchar(100)
,rownum int
);
insert into #input
select 'Dave',3,0,newid()
union
select 'Joe',2,0,newid();
while exists(select * from #input where processed = 0)
begin
declare #currentid uniqueidentifier = (select top 1 id from #input where processed = 0);
declare #currentwantedrows int = (select wantedrows from #input where id = #currentid);
declare #i int = 0;
while #i < #currentwantedrows
begin
insert into #output
select name,#i+1
from #input
where id = #currentid;
set #i = #i + 1;
end;
update #input set processed = 1 where id = #currentid;
end
select name,wantedrows from #input;
select * from #output;
You can use a number-table or following trick using a system view to build a sequence:
WITH Nums AS
(
SELECT n = ROW_NUMBER() OVER (ORDER BY [object_id])
FROM sys.all_objects
)
SELECT Name, Counter = n
FROM Nums n CROSS JOIN Table1 t1
WHERE n BETWEEN 1 AND Counter
ORDER BY Name, Counter;
Demo
This view has only about 2000 rows, so if you need more you could use a number-table.
http://sqlperformance.com/2013/01/t-sql-queries/generate-a-set-1
( presuming SQL-Server )
Is a hundred copies enough?
create table #c (num)
insert into #c (num)
select 0 union
select 1 union
select 2 union
select 3 union
select 4 union
select 5 union
select 6 union
select 7 union
select 8 union
select 9
select T.Name, c1.num * 10 + c0.num + 1
from T, #c c1, #c c0
where c1.num * 10 + c0.num < T.Counter
drop table #c
You didn't say which version of Sybase. The old ones I've worked on didn't allow derived tables so I had to throw the values into a temp table. But you can see how to extend the idea. This may not be the best approach if this is something you need to do more than once though.

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

How to find Missing Numbers in a table using sqlserver?

I want to find Missing Numbers in a table..Table Like this.
Sno Branch
1 ABC
2 ABC
3 ABC
5 ABC // 4th sno is missing
6 ABC
8 ABC // 7th sno is missing
10 ABC // 9th sno is missing
I found the missing SNo using this Query
ALTER proc [dbo].[K_RT_DCNoMissing]--1,50
as
begin
declare #id int
set #id = 0
declare #maxid int
--set #id = #fromvalue
select #maxid = (select count(*) dcno from K_RT_Dailyentryretail nolock)
create table #IDSeq
(
id int
)
while 0<#maxid--whatever you max is
begin
insert into #IDSeq values(#id)
set #id = #id + 1
set #maxid = #maxid - 1
-- print #maxid
end
select
s.id
from
#idseq s
left join K_RT_Dailyentryretail t on
s.id = t.dcno
where t.dcno is null order by s.id asc
drop table #IDSeq
end
I am getting out put like this..
MissingNo's
4
7
9
Now I want to Display Sno with Branch Name like.
MissingNo's Branch
4 ABC
7 ABC
9 ABC
How can i get the branch name...
Am getting output as
4 abc
4 cde
4 fgh
7 abc
7 cde
7 fgh
but what actually am expecting is
4 abc
7 cde
. ..
. ..
etc.
You can use a CTE to build a table of all the branches and the full range of numbers for each. Then left join that to the main table to find what is missing. This will allow you to get consecutive missing numbers, e.g. 3, 4 and 5 missing. You can adjust #minid and #maxid to whatever you want your range to be. If #maxid can be greater than 32767, then you will need to do something with batches of ranges.
declare #minid int, #maxid int
set #minid = 1
set #maxid = (select count(*) dcno from K_RT_Dailyentryretail with (nolock))
; with Branches as (
select distinct Branch from K_RT_Dailyentryretail
)
, CTE as
(
select #minid as Sno, Branch from Branches
union all
select Sno + 1, Branch from CTE where Sno < #maxid
)
select CTE.Sno, CTE.Branch from CTE
left outer join K_RT_Dailyentryretail k on k.Branch = CTE.Branch and CTE.Sno = k.Sno
where k.Sno is null
option (MAXRECURSION 32767);
SQLFiddle here: http://sqlfiddle.com/#!3/13653/13
I am considering that GAP between you number will be always of only 1 digit.
In this case following will work for you,
;With CTE as
(
select Sno,Branch,ROW_NUMBER() over (order by Sno) R
from C
)
select a.Sno+ 1,a.Branch from CTE as a
left join CTE as b on a.R + 1= b.R
where a.Sno + 1 <> b.Sno
One more thing is that, for missing row you want branch, I have thought that for missing row you want to have branch of previous row.
Assuming that each branch has it's own set of sno numbers, this works:
select sno-1 as sno, branch
from t1
where sno-1 NOT in (select sno from t1 as t2 where t1.branch = t2.branch)
and sno > 1
;
Which you can check here: http://sqlfiddle.com/#!3/daa81d/3
If sno is a unique key, let me know and I will revise the answer.
EDIT: Like AK47's answer, I'm assuming that the gap is only one row.

get specific rows of table given a rule SQL Server 2008

I have a table like:
ID NAME VAL
----------------------
1 a1*a1 90052
2 a1*a2 236
3 a1*a3 56
4 a1*a4 6072
5 a1*a5 1004
6 a2*a2 4576
7 a2*a3 724
8 a2*a4 230
9 a2*a5 679
10 a3*a3 5
11 a3*a4 644
12 a3*a5 23423
13 a4*a4 42354
14 a4*a5 10199
15 a5*a5 10279
Given a number given S = 5, I want to query
the rows wth id: 1,6,10,13,15
they are a1*a1,a2*a2,a3*a3,a4*a4 and a5*a5
I would like something like:
INSERT #NEW_TABLE (ID,NAME,Value) (
SELECT ordinal, NAME, VAL FROM myTable where id = 1,6,10,13,15)
to get
ID NAME VAL
----------------------
1 a1*a1 90052
2 a2*a2 4576
3 a3*a3 5
4 a4*a4 42354
5 a5*a5 10279
Is there a way to do this for any given S, Maybe wth dynamic sql?
I was getting the formula and I got this:
S=5
ID formula
1 1
6 1+S
10 1+S+ (S-1)
13 1+S+ (S-1) + (S-2)
15 1+S+ (S-1) + (S-2) + (S-3)
Is there a way to do this inside a case or a while loop?
This worked in testing.
You can just inner join on #Tab to limit your results. You probably also want to add some traps for values below 3, which I haven't done.
The basic process is
Declare your #s value
Insert the first two rows since they will always be the same
In a loop, insert one row at a time with an incrementing difference
Loop exits once it has run #s-2 times
Try:
DECLARE #Tab Table (id INT)
DECLARE #S int = 5,
#ct int
DECLARE #cur int = (1 + #S)
INSERT INTO #Tab SELECT 1
INSERT INTO #Tab SELECT (1 + #S)
SET #ct = 1
WHILE #ct <= #S - 2
BEGIN
SET #cur = #cur + (#S - #ct)
INSERT INTO #Tab SELECT #cur
SET #ct = #ct + 1
END
SELECT * FROM #Tab
ORDER BY id
To use this in your query, you can do either:
SELECT ordinal, NAME, VAL
FROM myTable
WHERE id IN (SELECT id FROM #Tab)
-- OR
SELECT ordinal, NAME, VAL
FROM myTable t
INNER JOIN #tab t2
ON t2.id = t.id