TSQL:如何检测和插入丢失的记录

时间:2015-12-18 21:50:20

标签: sql sql-server tsql

我有下面的T-SQL表。

Sub Transpose()

Dim ws As Worksheet
Set ws1 = ThisWorkbook.Worksheets("Sheet1") 'Sheets("Sheet1")
Set ws2 = Workbooks("Workbook2").Worksheets("Sheet1") 'Sheets("Sheet2")

ws2.Range("A1:D1").Value = Array("Name", "Value", "Test", "Defintion")

With ws1

    'how many groups are there so we know how many times to transpose
    'we find this out by counting the number of times "Defintion" appears
    Dim lDef As Long
    lDef = Application.WorksheetFunction.CountIf(.Rows(2), "Definition")

    'get last row where grouped data appears
    Dim lRow As Long
    lRow = .Range("A" & .Rows.Count).End(xlUp).Row

    Dim l As Long
    For l = 3 To lRow 'loop through items

        Dim rDef As Range, sFirst As String
        Set rDef = .Rows(2).Find("Definition") 'find first instance of "Definition"
        sFirst = rDef.Address 'get address of first occurence so we can test if we reached it again

        'list Name (aka Item) (for as many rows as needed defined by how many groups * 4 (1 for each test))
        With ws2
            .Range("A" & .Rows.Count).End(xlUp).Offset(1).Resize(4 * lDef).Value = ws1.Range("A" & l)
        End With


        Do

            'transpose values
            rDef.Offset(l - 2, 1).Resize(1, 4).Copy 'uses l-2 to offset for each row throughout the loop

            With ws2
                'paste values (test results)
                .Range("B" & .Rows.Count).End(xlUp).Offset(1).Resize(4, 1).PasteSpecial xlPasteValues, Transpose:=True
                'load test cases
                .Range("C" & .Rows.Count).End(xlUp).Offset(1).Resize(4, 1).Value = Application.WorksheetFunction.Transpose(Array("A", "B", "C", "D"))
                'load definitions
                .Range("D" & .Rows.Count).End(xlUp).Offset(1).Resize(4, 1).Value = Application.WorksheetFunction.Transpose(rDef.Offset(1).Value)
            End With

            Set rDef = .Rows(2).FindNext(After:=rDef) 'find next definition

        Loop Until rDef Is Nothing Or rDef.Address = sFirst

    Next


End With


End Sub

上表必须有10行,ID为1到10.所以它缺少7行。如何插入具有正确ID的缺失行。成本&丢失行的maxcost将为零。我是否需要创建一个包含1到10个数字的临时表?

3 个答案:

答案 0 :(得分:2)

不需要临时表,简单的计数派生表和LEFT OUTER JOIN就足够了:

CREATE TABLE #tab(ID INT, Cost INT, MaxCost INT);

INSERT INTO #tab(ID, Cost, MaxCost)
VALUES (2, 200,300),(3, 400, 1000) ,(6, 20, 100);

DECLARE @range_start INT = 1
       ,@range_end INT = 10;

;WITH tally AS
(
  SELECT TOP 1000 r = ROW_NUMBER() OVER (ORDER BY name)
  FROM master..spt_values
)
INSERT INTO #tab(id, Cost, MaxCost)
SELECT t.r, 0, 0
FROM tally t
LEFT JOIN #tab c
  ON t.r = c.ID
WHERE t.r BETWEEN @range_start AND @range_end
  AND c.ID IS NULL;

SELECT *
FROM #tab
ORDER BY ID;

LiveDemo

修改

Tally表只是数字表。使用subquery实现目标的方法有很多种:

  • recursive cte
  • 系统表中的
  • ROW_NUMBER()包含许多值(在此处使用)
  • UNION ALLCROSS JOIN
  • VALUES(...)
  • 使用OPENJSON(SQL Server 2016 +)
  • ...

TOP 1000如果您知道自己需要更多资料,则只能生成1000条记录:

SELECT TOP 1000000 r = ROW_NUMBER() OVER (ORDER BY (SELECT 1))
FROM master..spt_values c
CROSS JOIN master..spt_values c2;

答案 1 :(得分:0)

由于您的行数较少,您可以明确定义数据...

CREATE TABLE Data(ID INT, Cost INT, MaxCost INT);

INSERT INTO Data(ID, Cost, MaxCost) VALUES(2, 200, 300);
INSERT INTO Data(ID, Cost, MaxCost) VALUES(3, 400, 1000);
INSERT INTO Data(ID, Cost, MaxCost) VALUES(6, 20, 100);

和查询...

select *
from (VALUES(1),(2),(3),(4),(5),(6),(7),(8),(9),(10)) RowNums (ID)
left outer join Data on RowNums.ID = Data.ID

第一部分定义了行1-10的列ID,然后将外部联接留给您的数据。它的美妙之处在于它非常易读。

答案 2 :(得分:0)

我喜欢谷歌寻找新的和更好的方法来做事情。所以我偶然发现了这篇文章... ...在SQL7中运行良好并且在SQL2016中运行良好的是只使用外部联接并查找NULL值(null是缺少数据)....

insert into DestTable (keyCol1,col1,col2,col3...)
select keyCol1,col1,col2,col3,...)
  from SouceTable as s
  left outer join DestTable as d on d.KeyCol1=s.KeyCol1
 where d.KeyCol1 is null
   and ...

随意测试它 将您的语句包装在一个事务中,删除几行并看到它们返回select语句,该语句通常会在目标表中插入行...

BEGIN TRAN
--> delete a subset of the data, in this case 5 rows
set rowcount 5;

-->delete and show what is deleted
delete from DestTable;
OUTPUT deleted.*,'DELETD' as [Action]

--> Perform the select to see if the selected rows that are retured match the deleted rows
--insert into DestTable (keyCol1,col1,col2,col3...)
Select keyCol1,col1,col2,col3,...)
from SouceTable as s
left outer join DestTable as d on d.KeyCol1=s.KeyCol1
where d.KeyCol1 is null
and ...

ROLLBACK  

另一种方式是TSQL合并,谷歌,如果你还需要更新和可选删除......