แบบสอบถาม SQL เพื่อส่งกลับความแตกต่างระหว่างสองตาราง


198

ฉันพยายามเปรียบเทียบสองตารางคือ SQL Server เพื่อตรวจสอบข้อมูลบางอย่าง ฉันต้องการส่งคืนแถวทั้งหมดจากตารางทั้งสองที่มีข้อมูลอยู่ในตารางใดตารางหนึ่งหรืออีกแถวหนึ่ง ในสาระสำคัญฉันต้องการแสดงความแตกต่างทั้งหมด ฉันต้องตรวจสอบข้อมูลสามส่วนในการทำเช่นนั้นชื่อนามสกุลและผลิตภัณฑ์

ฉันค่อนข้างใหม่กับ SQL และดูเหมือนว่าโซลูชันจำนวนมากที่ฉันค้นหาพบนั้นซับซ้อนเกินความจำเป็น ฉันไม่ต้องกังวลเกี่ยวกับ NULLs

ฉันเริ่มต้นด้วยการลองสิ่งนี้:

SELECT DISTINCT [First Name], [Last Name], [Product Name] FROM [Temp Test Data]
WHERE ([First Name] NOT IN (SELECT [First Name] 
FROM [Real Data]))

ฉันมีปัญหาในการดำเนินการต่อไปแม้ว่า

ขอบคุณ!

แก้ไข:

จากคำตอบโดย @treaschf ฉันพยายามใช้รูปแบบของแบบสอบถามต่อไปนี้:

SELECT td.[First Name], td.[Last Name], td.[Product Name]
FROM [Temp Test Data] td FULL OUTER JOIN [Data] AS d 
ON td.[First Name] = d.[First Name] AND td.[Last Name] = d.[Last Name] 
WHERE (d.[First Name] = NULL) AND (d.[Last Name] = NULL)

แต่ฉันได้รับผลลัพธ์ 0 กลับมาเมื่อฉันรู้ว่ามีอย่างน้อย 1 แถวใน td ที่ไม่ได้อยู่ใน d

แก้ไข:

ตกลงฉันคิดว่าฉันคิดออก อย่างน้อยในไม่กี่นาทีของการทดสอบดูเหมือนว่าจะทำงานได้ดีพอ

SELECT [First Name], [Last Name]
FROM [Temp Test Data] AS td
WHERE (NOT EXISTS
        (SELECT [First Name], [Last Name]
         FROM [Data] AS d
         WHERE ([First Name] = td.[First Name]) OR ([Last Name] = td.[Last Name])))

นี่เป็นพื้นฐานที่จะบอกฉันว่ามีอะไรอยู่ในข้อมูลการทดสอบของฉันที่ไม่ได้อยู่ในข้อมูลจริงของฉัน ซึ่งสมบูรณ์ดีสำหรับสิ่งที่ฉันต้องทำ


3
ตัวอย่างข้อยกเว้นด้านล่างนี้เร็วกว่านี้ประมาณ 100 เท่า
Eric Hanson

1
บางคนสามารถยืนยันได้ว่าการทำงานนี้หรือไม่ ใช้งานไม่ได้ในตอนท้ายของฉันและไม่เห็นจุด "AS d" หาก "d" ไม่ได้ใช้งานทุกที่อาจมีข้อผิดพลาดบ้างไหม?
โรเบิร์ตซินแคล

คำตอบ:


222

หากคุณมีตารางAและBทั้งคู่กับ colum Cนี่คือระเบียนที่มีอยู่ในตารางAแต่ไม่ใช่ในB:

SELECT A.*
FROM A
    LEFT JOIN B ON (A.C = B.C)
WHERE B.C IS NULL

ในการรับความแตกต่างทั้งหมดด้วยการสืบค้นเพียงครั้งเดียวจะต้องใช้การเข้าร่วมแบบเต็มเช่นนี้

SELECT A.*, B.*
FROM A
    FULL JOIN B ON (A.C = B.C)
WHERE A.C IS NULL OR B.C IS NULL

สิ่งที่คุณต้องรู้ในกรณีนี้คือเมื่อมีการบันทึกในAแต่ไม่Bเกินกว่าคอลัมน์ที่มาจากBจะเป็น NULL และในทำนองเดียวกันสำหรับผู้ที่อยู่ในBและไม่อยู่ในAคอลัมน์จากAจะ ว่างเปล่า


ฉันมีปัญหาในการทำให้การทำงานถูกต้องดูการแก้ไขล่าสุดของฉัน
Casey

ปัญหาอาจเกิดจากการที่คุณไม่สามารถเปรียบเทียบค่ากับ null โดยใช้ '=' (หรืออย่างน้อยเมื่อ SET ANSI_NULLS เปิดอยู่) คุณต้องพูดว่า: value is NULL หรือค่า IS ไม่ใช่ NULL
Treaschf

ฉันทำเครื่องหมายสิ่งนี้เป็นคำตอบที่ฉันใช้เพราะในการทำวิธีนี้ฉันสามารถทำสิ่งอื่น ๆ ได้สองสามอย่างที่ฉันต้องทำในภายหลัง
Casey

1
The objects "a.dbo.student" and "b.dbo.student" in the FROM clause have the same exposed names. Use correlation names to distinguish them.ฉันได้รับข้อผิดพลาดหากชื่อตารางเหมือนกันและคุณดึงพวกเขาจากฐานข้อมูลที่แตกต่างกันสองแห่ง
Hammad Khan

@Thecrocodilehunter คุณจำเป็นต้องเปลี่ยนชื่อของตารางเหมือนa.dbo.student asและb.dbo.student bsแล้วอ้างถึงตารางที่มีasและbs
AAA

249
(   SELECT * FROM table1
    EXCEPT
    SELECT * FROM table2)  
UNION ALL
(   SELECT * FROM table2
    EXCEPT
    SELECT * FROM table1) 

1
ยอดเยี่ยม มันใช้งานได้สำหรับฉันและฉันกำลังดึงสองตารางจากฐานข้อมูลที่แตกต่างกันสองตัวบนเซิร์ฟเวอร์สองเครื่อง
dotnetN00b

1
ฉันได้รับข้อผิดพลาดเช่นกันข่าวสารเกี่ยวกับ 205 ระดับ 16 สถานะ 1 บรรทัด 5 แบบสอบถามทั้งหมดที่รวมกันโดยใช้ตัวดำเนินการ UNION, INTERSECT หรือ EXCEPT จะต้องมีจำนวนนิพจน์เท่ากันในรายการเป้าหมาย
cdub

4
หากคุณได้รับข้อผิดพลาดคุณต้องตรวจสอบให้แน่ใจว่าคุณได้เลือกเขตข้อมูลเดียวกัน (ประเภทเดียวกัน) ในลำดับเดียวกัน SELECT *ทำงานถ้าเป็นกรณีนี้สำหรับทุกคอลัมน์; ถ้าไม่ใช่เพียงเลือกเซ็ตย่อย
ashes999

5
คำตอบที่ดี มีวิธีที่ฉันสามารถเพิ่มเขตข้อมูลที่จะบอกฉันที่ 2 ตารางที่บันทึกกลับมาจากไหน?
Juan Velez

2
ระวังตัวด้วยวิธีนี้ทิ้งซ้ำซ้อน
Jin Kim

37

ฉันรู้ว่านี่อาจไม่ใช่คำตอบที่ได้รับความนิยม แต่ฉันเห็นด้วยกับ @Randy Minder ในการใช้เครื่องมือของบุคคลที่สามเมื่อต้องการการเปรียบเทียบที่ซับซ้อนยิ่งขึ้น

กรณีเฉพาะที่นี่เป็นเรื่องง่ายและในกรณีนี้เครื่องมือดังกล่าวไม่จำเป็น แต่อาจซับซ้อนได้ง่ายหากคุณแนะนำคอลัมน์เพิ่มเติมฐานข้อมูลบนเซิร์ฟเวอร์สองเครื่องเกณฑ์การเปรียบเทียบที่ซับซ้อนยิ่งขึ้นและเช่นนั้น

มีเครื่องมือเหล่านี้มากมายเช่นApexSQL Data DiffหรือQuest Toadและคุณสามารถใช้มันในโหมดทดลองเพื่อให้งานสำเร็จ


ตัวอย่างของการแก้ปัญหาฐานข้อมูลไม่เชื่อเรื่องพระเจ้าฟอสส์ที่ทำงานร่วมกับแหล่งที่มาของตารางใด ๆ ของข้อมูลจากฐานข้อมูลต่างๆหรือระบบไฟล์Diffkit
wwmbes

ไมโครซอฟท์ยังได้ของ SQL Server tablediffอรรถประโยชน์บรรทัดคำสั่งเรียกที่นี่
wwmbes

15

หากต้องการรับความแตกต่างทั้งหมดระหว่างสองตารางคุณสามารถใช้คำขอ SQL นี้แบบฉัน:

SELECT 'TABLE1-ONLY' AS SRC, T1.*
FROM (
      SELECT * FROM Table1
      EXCEPT
      SELECT * FROM Table2
      ) AS T1
UNION ALL
SELECT 'TABLE2-ONLY' AS SRC, T2.*
FROM (
      SELECT * FROM Table2
      EXCEPT
      SELECT * FROM Table1
      ) AS T2
;

9

รูปแบบที่เรียบง่ายของคำตอบ @erikkallen ที่แสดงให้เห็นว่าตารางใดมีแถวอยู่ใน:

(   SELECT 'table1' as source, * FROM table1
    EXCEPT
    SELECT * FROM table2)  
UNION ALL
(   SELECT 'table2' as source, * FROM table2
    EXCEPT
    SELECT * FROM table1) 

หากคุณได้รับข้อผิดพลาด

แบบสอบถามทั้งหมดที่รวมกันโดยใช้ตัวดำเนินการ UNION, INTERSECT หรือ EXCEPT จะต้องมีจำนวนนิพจน์ที่เท่ากันในรายการเป้าหมาย

จากนั้นอาจช่วยในการเพิ่ม

(   SELECT 'table1' as source, * FROM table1
    EXCEPT
    SELECT 'table1' as source, * FROM table2)  
UNION ALL
(   SELECT 'table2' as source, * FROM table2
    EXCEPT
    SELECT 'table2' as source, * FROM table1) 

2
ฉันได้รับข้อผิดพลาดAll queries combined using a UNION, INTERSECT or EXCEPT operator must have an equal number of expressions in their target lists.จากการเลือกนี้ ฉันต้องเพิ่ม'table1' as source, ส่วนนี้ไปยังส่วนยกเว้น - ฉันแก้ไขคำตอบนี้แล้ว
knut

6

หากคุณต้องการได้รับค่าคอลัมน์ที่แตกต่างกันคุณสามารถใช้โมเดล Entity-Attribute-Value:

declare @Data1 xml, @Data2 xml

select @Data1 = 
(
    select * 
    from (select * from Test1 except select * from Test2) as a
    for xml raw('Data')
)

select @Data2 = 
(
    select * 
    from (select * from Test2 except select * from Test1) as a
    for xml raw('Data')
)

;with CTE1 as (
    select
        T.C.value('../@ID', 'bigint') as ID,
        T.C.value('local-name(.)', 'nvarchar(128)') as Name,
        T.C.value('.', 'nvarchar(max)') as Value
    from @Data1.nodes('Data/@*') as T(C)    
), CTE2 as (
    select
        T.C.value('../@ID', 'bigint') as ID,
        T.C.value('local-name(.)', 'nvarchar(128)') as Name,
        T.C.value('.', 'nvarchar(max)') as Value
    from @Data2.nodes('Data/@*') as T(C)     
)
select
    isnull(C1.ID, C2.ID) as ID, isnull(C1.Name, C2.Name) as Name, C1.Value as Value1, C2.Value as Value2
from CTE1 as C1
    full outer join CTE2 as C2 on C2.ID = C1.ID and C2.Name = C1.Name
where
not
(
    C1.Value is null and C2.Value is null or
    C1.Value is not null and C2.Value is not null and C1.Value = C2.Value
)

ตัวอย่าง SQL FIDDLE


ขอบคุณฉันทำให้รหัสนี้เป็นแบบไดนามิกเล็กน้อย ตอนนี้จะช่วยให้คุณผ่านสองตารางคุณต้องการเปรียบเทียบ ... thitos.blogspot.com/2014/03/compare-data-from-two-tables.html
Thato

2

ลองสิ่งนี้:

SELECT 
    [First Name], [Last Name]
FROM 
    [Temp Test Data] AS td EXCEPTION JOIN [Data] AS d ON 
         (d.[First Name] = td.[First Name] OR d.[Last Name] = td.[Last Name])

อ่านง่ายกว่ามาก


2

สิ่งนี้จะทำกลอุบายคล้ายกับวิธีแก้ปัญหาของTiagoคืนค่าตาราง "แหล่งที่มา" ด้วย

select [First name], [Last name], max(_tabloc) as _tabloc
from (
  select [First Name], [Last name], 't1' as _tabloc from table1
  union all
  select [First name], [Last name], 't2' as _tabloc from table2
) v
group by [Fist Name], [Last name]
having count(1)=1

ผลจะมีความแตกต่างระหว่างตารางในคอลัมน์ _tabloc คุณจะมีการอ้างอิงตาราง


2

นำเสนอ Cadillac of Diffs ในฐานะ SP ดูภายในสำหรับเทมเพลตพื้นฐานที่ยึดตามคำตอบของ @erikkallen มันรองรับ

  • การตรวจจับแถวซ้ำซ้อน (คำตอบอื่น ๆ ส่วนใหญ่ที่นี่ไม่ได้)
  • เรียงลำดับผลลัพธ์ตามอาร์กิวเมนต์
  • จำกัด เฉพาะคอลัมน์
  • ละเว้นคอลัมน์ (เช่น ModifiedUtc)
  • ชื่อตารางฐานข้อมูลข้าม
  • ตารางชั่วคราว (ใช้เป็นวิธีแก้ปัญหาเพื่อมุมมองต่าง

การใช้งาน:

exec Common.usp_DiffTableRows '#t1', '#t2';

exec Common.usp_DiffTableRows 
    @pTable0          = 'ydb.ysh.table1',
    @pTable1          = 'xdb.xsh.table2',
    @pOrderByCsvOpt   = null,  -- Order the results
    @pOnlyCsvOpt      = null,  -- Only compare these columns
    @pIgnoreCsvOpt    = null;  -- Ignore these columns (ignored if @pOnlyCsvOpt is specified)

รหัส:

alter proc [Common].[usp_DiffTableRows]    
    @pTable0          varchar(300),
    @pTable1          varchar(300),
    @pOrderByCsvOpt   nvarchar(1000) = null,  -- Order the Results
    @pOnlyCsvOpt      nvarchar(4000) = null,  -- Only compare these columns
    @pIgnoreCsvOpt    nvarchar(4000) = null,  -- Ignore these columns (ignored if @pOnlyCsvOpt is specified)
    @pDebug           bit = 0
as
/*---------------------------------------------------------------------------------------------------------------------
    Purpose:  Compare rows between two tables.

      Usage:  exec Common.usp_DiffTableRows '#a', '#b';

    Modified    By          Description
    ----------  ----------  -------------------------------------------------------------------------------------------
    2015.10.06  crokusek    Initial Version
    2019.03.13  crokusek    Added @pOrderByCsvOpt
    2019.06.26  crokusek    Support for @pIgnoreCsvOpt, @pOnlyCsvOpt.    
    2019.09.04  crokusek    Minor debugging improvement
    2020.03.12  crokusek    Detect duplicate rows in either source table
  ---------------------------------------------------------------------------------------------------------------------*/
begin try

    if (substring(@pTable0, 1, 1) = '#')
        set @pTable0 = 'tempdb..' + @pTable0; -- object_id test below needs full names for temp tables

    if (substring(@pTable1, 1, 1) = '#')
        set @pTable1 = 'tempdb..' + @pTable1; -- object_id test below needs full names for temp tables

    if (object_id(@pTable0) is null)
        raiserror('Table name is not recognized: ''%s''', 16, 1, @pTable0);

    if (object_id(@pTable1) is null)
        raiserror('Table name is not recognized: ''%s''', 16, 1, @pTable1);

    create table #ColumnGathering
    (
        Name nvarchar(300) not null,
        Sequence int not null,
        TableArg tinyint not null
    );

    declare
        @usp          varchar(100) = object_name(@@procid),    
        @sql          nvarchar(4000),
        @sqlTemplate  nvarchar(4000) = 
        '  
            use $database$;

            insert into #ColumnGathering
            select Name, column_id as Sequence, $TableArg$ as TableArg
              from sys.columns c
             where object_id = object_id(''$table$'', ''U'')
        ';          

    set @sql = replace(replace(replace(@sqlTemplate,
        '$TableArg$', 0),
        '$database$', (select DatabaseName from Common.ufn_SplitDbIdentifier(@pTable0))),
        '$table$', @pTable0);

    if (@pDebug = 1)
        print 'Sql #CG 0: ' + @sql;

    exec sp_executesql @sql;

    set @sql = replace(replace(replace(@sqlTemplate,
        '$TableArg$', 1),
        '$database$', (select DatabaseName from Common.ufn_SplitDbIdentifier(@pTable1))),
        '$table$', @pTable1);

    if (@pDebug = 1)
        print 'Sql #CG 1: ' + @sql;

    exec sp_executesql @sql;

    if (@pDebug = 1)
        select * from #ColumnGathering;

    select Name, 
           min(Sequence) as Sequence, 
           convert(bit, iif(min(TableArg) = 0, 1, 0)) as InTable0,
           convert(bit, iif(max(TableArg) = 1, 1, 0)) as InTable1
      into #Columns
      from #ColumnGathering
     group by Name
    having (     @pOnlyCsvOpt is not null 
             and Name in (select Value from Common.ufn_UsvToNVarcharKeyTable(@pOnlyCsvOpt, default)))
        or 
           (     @pOnlyCsvOpt is null
             and @pIgnoreCsvOpt is not null 
             and Name not in (select Value from Common.ufn_UsvToNVarcharKeyTable(@pIgnoreCsvOpt, default)))
        or 
           (     @pOnlyCsvOpt is null
             and @pIgnoreCsvOpt is null)

    if (exists (select 1 from #Columns where InTable0 = 0 or InTable1 = 0))
    begin
        select 1; -- without this the debugging info doesn't stream sometimes
        select * from #Columns order by Sequence;        
        waitfor delay '00:00:02';  -- give results chance to stream before raising exception
        raiserror('Columns are not equal between tables, consider using args @pIgnoreCsvOpt, @pOnlyCsvOpt.  See Result Sets for details.', 16, 1);    
    end

    if (@pDebug = 1)
        select * from #Columns order by Sequence;

    declare 
        @columns nvarchar(4000) = --iif(@pOnlyCsvOpt is null and @pIgnoreCsvOpt is null,
           -- '*',     
            (
              select substring((select ',' + ac.name
                from #Columns ac
               order by Sequence
                 for xml path('')),2,200000) as csv
            );

    if (@pDebug = 1)
    begin
        print 'Columns: ' + @columns;
        waitfor delay '00:00:02';  -- give results chance to stream before possibly raising exception
    end

    -- Based on https://stackoverflow.com/a/2077929/538763
    --     - Added sensing for duplicate rows
    --     - Added reporting of source table location
    --
    set @sqlTemplate = '
            with 
               a as (select ~, Row_Number() over (partition by ~ order by (select null)) -1 as Duplicates from $a$), 
               b as (select ~, Row_Number() over (partition by ~ order by (select null)) -1 as Duplicates from $b$)
            select 0 as SourceTable, ~
              from 
                 (
                   select * from a
                   except
                   select * from b
                 )  anb
              union all
             select 1 as SourceTable, ~
               from 
                 (
                   select * from b
                   except
                   select * from a
                 )  bna
             order by $orderBy$
        ';    

     set @sql = replace(replace(replace(replace(@sqlTemplate, 
            '$a$', @pTable0), 
            '$b$', @pTable1),
            '~', @columns),
            '$orderBy$', coalesce(@pOrderByCsvOpt, @columns + ', SourceTable')
        );

     if (@pDebug = 1)
        print 'Sql: ' + @sql;

     exec sp_executesql @sql;

end try
begin catch
    declare        
        @CatchingUsp  varchar(100) = object_name(@@procid);    

    if (xact_state() = -1)
        rollback;    

    -- Disabled for S.O. post

    --exec Common.usp_Log
        --@pMethod = @CatchingUsp;

    --exec Common.usp_RethrowError        
        --@pCatchingMethod = @CatchingUsp;

    throw;
end catch
go

create function Common.Trim
(
    @pOriginalString nvarchar(max), 
    @pCharsToTrim nvarchar(50) = null -- specify null or 'default' for whitespae 
)  
returns table
with schemabinding
as 
/*--------------------------------------------------------------------------------------------------
    Purpose:   Trim the specified characters from a string.

    Modified    By              Description
    ----------  --------------  --------------------------------------------------------------------
    2012.09.25  S.Rutszy/crok   Modified from https://dba.stackexchange.com/a/133044/9415    
  --------------------------------------------------------------------------------------------------*/ 
return
with cte AS
(
  select patindex(N'%[^' + EffCharsToTrim + N']%', @pOriginalString) AS [FirstChar],
         patindex(N'%[^' + EffCharsToTrim + N']%', reverse(@pOriginalString)) AS [LastChar],
         len(@pOriginalString + N'~') - 1 AS [ActualLength]
   from
   (
         select EffCharsToTrim = coalesce(@pCharsToTrim, nchar(0x09) + nchar(0x20) + nchar(0x0d) + nchar(0x0a))
   ) c
)
select substring(@pOriginalString, [FirstChar],
                 ((cte.[ActualLength] - [LastChar]) - [FirstChar] + 2)
       ) AS [TrimmedString]
       --
       --cte.[ActualLength],
       --[FirstChar],
       --((cte.[ActualLength] - [LastChar]) + 1) AS [LastChar]              
from cte;
go

create function [Common].[ufn_UsvToNVarcharKeyTable] (
    @pCsvList     nvarchar(MAX),
    @pSeparator   nvarchar(1) = ','       -- can pass keyword 'default' when calling using ()'s
    )    
    --
    -- SQL Server 2012 distinguishes nvarchar keys up to maximum of 450 in length (900 bytes)
    -- 
    returns @tbl table (Value nvarchar(450) not null primary key(Value)) as
/*-------------------------------------------------------------------------------------------------
    Purpose:  Converts a comma separated list of strings into a sql NVarchar table.  From

              http://www.programmingado.net/a-398/SQL-Server-parsing-CSV-into-table.aspx     

              This may be called from RunSelectQuery:

                  GRANT SELECT ON Common.ufn_UsvToNVarcharTable TO MachCloudDynamicSql;

    Modified    By              Description
    ----------  --------------  -------------------------------------------------------------------
    2011.07.13  internet        Initial version
    2011.11.22  crokusek        Support nvarchar strings and a custom separator.
    2017.12.06  crokusek        Trim leading and trailing whitespace from each element.
    2019.01.26  crokusek        Remove newlines
  -------------------------------------------------------------------------------------------------*/     
begin
    declare 
        @pos      int,
        @textpos  int,
        @chunklen smallint,
        @str      nvarchar(4000),
        @tmpstr   nvarchar(4000),
        @leftover nvarchar(4000),
        @csvList nvarchar(max) = iif(@pSeparator not in (char(13), char(10), char(13) + char(10)),
            replace(replace(@pCsvList, char(13), ''), char(10), ''),
            @pCsvList); -- remove newlines

    set @textpos = 1
    set @leftover = ''  
    while @textpos <= len(@csvList)
    begin
        set @chunklen = 4000 - len(@leftover)
        set @tmpstr = ltrim(@leftover + substring(@csvList, @textpos, @chunklen))
        set @textpos = @textpos + @chunklen

        set @pos = charindex(@pSeparator, @tmpstr)
        while @pos > 0
        begin
            set @str = substring(@tmpstr, 1, @pos - 1)
            set @str = (select TrimmedString from Common.Trim(@str, default));
            insert @tbl (value) values(@str);
            set @tmpstr = ltrim(substring(@tmpstr, @pos + 1, len(@tmpstr)))
            set @pos = charindex(@pSeparator, @tmpstr)
        end

        set @leftover = @tmpstr
    end

    -- Handle @leftover

    set @str = (select TrimmedString from Common.Trim(@leftover, default));

    if @str <> ''
       insert @tbl (value) values(@str);

    return
end
GO

create function Common.ufn_SplitDbIdentifier(@pIdentifier nvarchar(300))
returns @table table 
(    
    InstanceName          nvarchar(300) not null,
    DatabaseName          nvarchar(300) not null,
    SchemaName            nvarchar(300),
    BaseName              nvarchar(300) not null,
    FullTempDbBaseName    nvarchar(300),            -- non-null for tempdb (e.g. #Abc____...)
    InstanceWasSpecified  bit not null,
    DatabaseWasSpecified  bit not null,
    SchemaWasSpecified    bit not null,
    IsCurrentInstance     bit not null,
    IsCurrentDatabase     bit not null,
    IsTempDb              bit not null,
    OrgIdentifier         nvarchar(300) not null
) as
/*-----------------------------------------------------------------------------------------------------------
    Purpose:  Split a Sql Server Identifier into its parts, providing appropriate default values and
              handling temp table (tempdb) references.

    Example:  select * from Common.ufn_SplitDbIdentifier('t')
              union all
              select * from Common.ufn_SplitDbIdentifier('s.t')
              union all
              select * from Common.ufn_SplitDbIdentifier('d.s.t')
              union all
              select * from Common.ufn_SplitDbIdentifier('i.d.s.t')
              union all
              select * from Common.ufn_SplitDbIdentifier('#d')
              union all
              select * from Common.ufn_SplitDbIdentifier('tempdb..#d'); 

              -- Empty
              select * from Common.ufn_SplitDbIdentifier('illegal name'); 

    Modified    By              Description
    ----------  --------------  -----------------------------------------------------------------------------
    2013.09.27  crokusek        Initial version.  
  -----------------------------------------------------------------------------------------------------------*/
begin
    declare 
        @name nvarchar(300) = ltrim(rtrim(@pIdentifier));

    -- Return an empty table as a "throw"
    --
    --Removed for SO post
    --if (Common.ufn_IsSpacelessLiteralIdentifier(@name) = 0)
      --  return;

    -- Find dots starting from the right by reversing first.

    declare 
        @revName nvarchar(300) = reverse(@name);

    declare
        @firstDot int = charindex('.', @revName);

    declare
        @secondDot  int = iif(@firstDot = 0,  0, charindex('.', @revName, @firstDot + 1));

    declare
        @thirdDot   int = iif(@secondDot = 0, 0, charindex('.', @revName, @secondDot + 1));

    declare
        @fourthDot  int = iif(@thirdDot = 0, 0, charindex('.', @revName, @thirdDot + 1));

    --select @firstDot, @secondDot, @thirdDot, @fourthDot, len(@name);

    -- Undo the reverse() (first dot is first from the right).
    --
    set @firstDot = iif(@firstDot = 0, 0, len(@name) - @firstDot + 1);
    set @secondDot = iif(@secondDot = 0, 0, len(@name) - @secondDot + 1);
    set @thirdDot = iif(@thirdDot = 0, 0, len(@name) - @thirdDot + 1);
    set @fourthDot = iif(@fourthDot = 0, 0, len(@name) - @fourthDot + 1);

    --select @firstDot, @secondDot, @thirdDot, @fourthDot, len(@name);

    declare
        @baseName   nvarchar(300)  = substring(@name, @firstDot + 1, len(@name) - @firstdot);

    declare
        @schemaName nvarchar(300) = iif(@firstDot - @secondDot - 1 <= 0, 
                                        null,
                                        substring(@name, @secondDot + 1, @firstDot - @secondDot - 1));
    declare
        @dbName     nvarchar(300) = iif(@secondDot - @thirdDot - 1 <= 0, 
                                        null,
                                        substring(@name, @thirdDot + 1, @secondDot - @thirdDot - 1));
    declare
        @instName   nvarchar(300) = iif(@thirdDot - @fourthDot - 1 <= 0, 
                                        null, 
                                        substring(@name, @fourthDot + 1, @thirdDot - @fourthDot - 1));

    with input as (
        select
           coalesce(@instName, '[' + @@servername + ']') as InstanceName,
           coalesce(@dbName,     iif(left(@baseName, 1) = '#', 'tempdb', db_name())) as DatabaseName,
           coalesce(@schemaName, iif(left(@baseName, 1) = '#', 'dbo', schema_name())) as SchemaName,
           @baseName as BaseName,
           iif(left(@baseName, 1) = '#',
               (
                  select [name] from tempdb.sys.objects
                  where object_id = object_id('tempdb..' + @baseName)
               ), 
               null) as FullTempDbBaseName,                
           iif(@instName is null, 0, 1) InstanceWasSpecified,       
           iif(@dbName is null, 0, 1) DatabaseWasSpecified,
           iif(@schemaName is null, 0, 1) SchemaWasSpecified    
     )
     insert into @table           
     select i.InstanceName, i.DatabaseName, i.SchemaName, i.BaseName, i.FullTempDbBaseName,
            i.InstanceWasSpecified, i.DatabaseWasSpecified, i.SchemaWasSpecified,
            iif(i.InstanceName = '[' + @@servername + ']', 1, 0) as IsCurrentInstance,
            iif(i.DatabaseName = db_name(), 1, 0) as IsCurrentDatabase,
            iif(left(@baseName, 1) = '#', 1, 0) as IsTempDb,
            @name as OrgIdentifier
       from input i;

    return;
end
GO

2

คุณสามารถใช้ยกเว้นตัวอย่างเช่นสิ่งนี้:

-- DB1..Tb1 have values than DB2..Tb1 not have
Select Col1,Col2,Col3 From DB1..Tb1
except
Select Col1,Col2,Col3 From DB2..Tb1
-- Now we change order
-- DB2..Tb1 have values than DB1..Tb1 not have
Select Col1,Col2,Col3 From DB2..Tb1
except
Select Col1,Col2,Col3 From DB1..Tb1

1

สำหรับการทดสอบควันง่าย ๆ ที่คุณพยายามให้แน่ใจว่าสองตารางตรงกับที่ไม่ต้องกังวลเกี่ยวกับชื่อคอลัมน์:

--ensure tables have matching records
Select count (*) from tbl_A
Select count (*) from tbl_B

--create temp table of all records in both tables
Select * into #demo from tbl_A 
Union All
Select * from tbl_B

--Distinct #demo records = Total #demo records/2 = Total tbl_A records = total tbl_B records
Select distinct * from #demo 

คุณสามารถเขียนขั้นตอนการจัดเก็บเพื่อเปรียบเทียบชุดของตารางได้อย่างง่ายดาย


0

มีปัญหาด้านประสิทธิภาพที่เกี่ยวข้องกับการเข้าร่วมด้านซ้ายรวมทั้งการเข้าร่วมเต็มรูปแบบกับข้อมูลขนาดใหญ่

ในความคิดของฉันนี้เป็นทางออกที่ดีที่สุด:

select [First Name], count(1) e from (select * from [Temp Test Data] union all select * from [Temp Test Data 2]) a group by [First Name] having e = 1
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.