SQL порівнює дані з двох таблиць


88

У мене 2 таблиці TableAі TableBякі мають той же формат стовпчика, наприклад , обидві таблиці TableAі TableBмають стовпці

A B C D E F 

де А і В - первинні ключі.

Як написати SQL, щоб перевірити, що якщо TableAі TableBщо мають однакові первинні ключі, містить абсолютно однакове значення в кожному стовпці.

Це означає, що ці дві таблиці мають абсолютно однакові дані.

Відповіді:


81

Ви повинні мати можливість "МІНУС" або "КРИМ" залежно від смаку SQL, який використовується СУБД.

select * from tableA
minus
select * from tableB

Якщо запит не повертає жодного рядка, тоді дані абсолютно однакові.


5
Відмінна пропозиція. Однак, я думаю, це може не спрацювати, якщо tableB має додаткові рядки, тому, можливо, ви захочете порівняти кількість рядків додатково.
jzd

5
По-іншому. Це не буде працювати, якщо tableAє зайві рядки. Вам знадобилося б, (A EXCEPT B) INTERSECT (B EXCEPT A)я гадаю, це було б набагато менш ефективним, ніж стандартне з'єднання болота.
Martin Smith

запит повертає два набори результатів ??
BuZz

Цей запит поверне рядки з NULLS, якщо такі є.
Reeya Oberoi

5
@Franklin - Ні, він повинен повертати лише один набір результатів. Якщо ви отримуєте два, використовуйте КРИМ замість МІНУСУ.
МТС

56

Використання реляційних операторів:

SELECT * FROM TableA
UNION 
SELECT * FROM TableB
EXCEPT 
SELECT * FROM TableA
INTERSECT
SELECT * FROM TableB;

Перейдіть EXCEPTна MINUSOracle.

Трохи прискіпливий момент: вищезазначене спирається на пріоритет оператора, який відповідно до стандарту SQL залежить від реалізації, тому YMMV. Він працює для SQL Server, для якого пріоритет:

  1. Вирази в дужках
  2. INTERSECT
  3. EXCEPTі UNIONоцінюється зліва направо.

Для Oracle вам потрібно використовувати дужки навколо UNION, дужки навколо INTERSECT та (як зазначено) замінити EXCEPT на MINUS. HTH.
Doug Clutter

20

дієтобудда має приємну відповідь. У тих випадках, коли у вас немає МІНУСУ або ОКРЕМО, одним із варіантів є об’єднання всіх таблиць, згрупування за всіма стовпцями та переконання, що всього є два:

SELECT col1, col2, col3
FROM
(SELECT * FROM tableA
UNION ALL  
SELECT * FROM tableB) data
GROUP BY col1, col2, col3
HAVING count(*)!=2

Я спробував використовувати це (я отримав це з блогу Jeff's SQL Server ), але я хотів би перерахувати обидва рядки з TableA та TableB, щоб я міг наочно побачити різницю в рядках. Не могли б ви пояснити, як це зробити?
Еммануель Ф

@Agent, це звучить як окреме запитання. Я б запропонував перерахувати його так, щоб його побачили інші, а не просто коментар тут.
jzd

Готово. І готово. Порівняння значень 2 таблиць та перелік різних рядків . Сподіваюся, я отримаю чудові результати. :)
Еммануель Ф

Це добре працює в обмежених середовищах SQL, таких як Visual FoxPro, дякую!
Kit Roed

1
Просто переглядаю це. Варто згадати, що первинні ключі забезпечують унікальні записи в таблицях. Це якщо таблиця (або запит) може мати повторювані рядки, DISTINCT/ GROUP BYпропонується для підзапитів в союзі, щоб забезпечити є тільки один запис у таблиці. В іншому випадку таблиця A може мати 2 записи, а таблиця B може мати 0 і не відповідати умові HAVING.
vol7ron

8
SELECT c.ID
FROM clients c
WHERE EXISTS(SELECT c2.ID 
FROM clients2 c2
WHERE c2.ID = c.ID);

Поверне всі ідентифікатори, які є ОДИННИМИ в обох таблицях. Щоб отримати відмінності, змініть ІСНУЄ на НЕ ІСНУЄ.


3

Взявши сценарій із onedaywhen, я змінив його, щоб також показати, з якої таблиці походить кожен запис.

DECLARE @table1 NVARCHAR(80)= 'table 1 name'
DECLARE @table2 NVARCHAR(80)= 'table 2 name'
DECLARE @sql NVARCHAR (1000)

SET @sql = 
'
SELECT ''' + @table1 + ''' AS table_name,* FROM
(
SELECT * FROM ' + @table1 + '
EXCEPT
SELECT * FROM ' + @table2 + '
) x

UNION 

SELECT ''' + @table2 + ''' AS table_name,* FROM 
(
SELECT * FROM ' + @table2 + '
EXCEPT
SELECT * FROM ' + @table1 + '
) y
'

EXEC sp_executesql @stmt = @sql

2

просто для заповнення, процес, що зберігається з використанням методу за винятком порівняння 2 таблиць і отримання результату в тій самій таблиці зі статусом 3 помилок, таблиця ADD, DEL, GAP повинна мати однаковий PK, ви оголошуєте 2 таблиці та поля для порівняння 1 або обох таблиць

Просто використовуйте так ps_TableGap 'tbl1', 'Tbl2', 'fld1, fld2, fld3', 'fld4'fld5'fld6' (необов'язково)

/****** Object:  StoredProcedure [dbo].[ps_TableGap]    Script Date: 10/03/2013 16:03:44 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

-- =============================================
-- Author:       Arnaud ALLAVENA
-- Create date: 03.10.2013
-- Description: Compare tables
-- =============================================
create PROCEDURE [dbo].[ps_TableGap]
    -- Add the parameters for the stored procedure here
    @Tbl1 as varchar(100),@Tbl2 as varchar(100),@Fld1 as varchar(1000), @Fld2 as varchar(1000)= ''
AS
BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.

    SET NOCOUNT ON;
--Variables
--@Tbl1 = table 1
--@Tbl2 = table 2
--@Fld1 = Fields to compare from table 1
--@Fld2 Fields to compare from table 2
Declare @SQL varchar(8000)= '' --SQL statements
Declare @nLoop int = 1 --loop counter
Declare @Pk varchar(1000)= '' --primary key(s) 
Declare @Pk1 varchar(1000)= '' --first field of primary key
declare @strTmp varchar(50) = '' --returns value in Pk determination
declare @FldTmp varchar (1000) = '' --temporarily fields for alias calculation

--If @Fld2 empty we take @Fld1
--fields rules: fields to be compare must be in same order and type - always returns Gap
If @Fld2 = '' Set @Fld2 = @Fld1

--Change @Fld2 with Alias prefix xxx become _xxx 
while charindex(',',@Fld2)>0
begin
    Set @FldTmp = @FldTmp + (select substring(@Fld2,1,charindex(',',@Fld2)-1) + ' as _' + substring(@Fld2,1,charindex(',',@Fld2)-1) + ',')
    Set @Fld2 = (select ltrim(right(@Fld2,len(@Fld2)-charindex(',',@Fld2))))
end
Set @FldTmp = @FldTmp + @Fld2 + ' as _' + @Fld2
Set @Fld2 = @FldTmp

--Determinate primary key jointure
--rule: same pk in both tables
Set @nLoop = 1
Set @SQL = 'Declare crsr cursor for select COLUMN_NAME from INFORMATION_SCHEMA.KEY_COLUMN_USAGE where TABLE_NAME = '''
 + @Tbl1 + ''' or TABLE_SCHEMA + ''.'' + TABLE_NAME = ''' + @Tbl1 +  ''' or TABLE_CATALOG + ''.'' + TABLE_SCHEMA + ''.'' + TABLE_NAME = ''' + @Tbl1 
 + ''' order by ORDINAL_POSITION'
exec(@SQL)
open crsr 
fetch next from crsr into @strTmp
while @@fetch_status = 0
begin 
    if @nLoop = 1 
    begin 
        Set @Pk = 's.' + @strTmp + ' = b._' + @strTmp
        Set @Pk1 = @strTmp
        set @nLoop = @nLoop + 1 
    end 
    Else
    Set @Pk = @Pk + ' and s.' + @strTmp + ' = b._' + @strTmp
fetch next from crsr into @strTmp 

end 
close crsr
deallocate crsr

--SQL statement build
set @SQL = 'select case when s.' + @Pk1 + ' is null then ''Del'' when b._' + @Pk1 + ' is null then ''Add'' else ''Gap'' end as TypErr, '''
set @SQL = @SQL + @Tbl1 +''' as Tbl1, s.*, ''' + @Tbl2 +''' as Tbl2 ,b.* from (Select ' + @Fld1 + ' from ' + @Tbl1
set @SQL = @SQL + ' EXCEPT SELECT ' + @Fld2 + ' from ' + @Tbl2 + ')s full join (Select ' + @Fld2 + ' from ' + @Tbl2 
set @SQL = @SQL + ' EXCEPT SELECT ' + @Fld1 + ' from ' + @Tbl1 +')b on '+ @Pk 

--Run SQL statement
Exec(@SQL)
END

2

Джерело: Використовуйте NATURAL FULL JOIN для порівняння двох таблиць у SQL Лукаса Едера

Розумний підхід використання NATURAL FULL JOINдля виявлення однакових / різних рядків між двома таблицями.

Приклад 1 - прапор стану:

SELECT t1.*, t2.*, CASE WHEN t1 IS NULL OR t2 IS NULL THEN 'Not equal' ELSE 'Equal' END
FROM t1
NATURAL FULL JOIN t2;

Приклад 2 - фільтрація рядків

SELECT *
FROM (SELECT 't1' AS t1, t1.* FROM t1) t1 
NATURAL FULL JOIN (SELECT 't2' AS t2, t2.* FROM t2) t2 
WHERE t1 IS NULL OR t2 IS NULL -- show differences
--WHERE  t1 IS NOT NULL AND t2 IS NOT NULL    -- show the same

db <> демонстрація скрипки


1

Покращення відповіді дієти Будди ...

select * from
(
    select * from tableA
    minus
    select * from tableB
)
union all
select * from
(
    select * from tableB
    minus
    select * from tableA
)

1

Ви можете знайти відмінності між двома таблицями, використовуючи комбінацію "вставити все" та "повне зовнішнє з'єднання" в Oracle. У sql ви можете витягти відмінності за допомогою повного зовнішнього об'єднання, але здається, що вставка all / first не існує в sql! Отже, замість цього потрібно використовувати такий запит:

select * from A
full outer join B on
A.pk=B.pk
where A.field1!=B.field1
or A.field2!=B.field2 or A.field3!=B.field3 or A.field4!=B.field4 
--and A.Date==Date1

Незважаючи на те, що використання операції OR не рекомендується, і це зазвичай призводить до зниження продуктивності, ви все одно можете використовувати вищезазначений запит, якщо ваші таблиці не масивні. Якщо є якийсь результат для вищезазначеного запиту, це точно різниці у 2 таблицях на основі порівняння полів 1,2,3,4. Для підвищення продуктивності запиту ви можете відфільтрувати його також за датою (перевірте коментовану частину)


0
    SELECT unnest(ARRAY[1,2,2,3,3]) 
    EXCEPT
    SELECT unnest(ARRAY[1,1,2,3,3])
UNION
    SELECT unnest(ARRAY[1,1,2,3,3])
    EXCEPT
    SELECT unnest(ARRAY[1,2,2,3,3])

Результат нульовий, але джерела різні!

Але:

(
    SELECT unnest(ARRAY[1,2,2,3])
    EXCEPT ALL
    SELECT unnest(ARRAY[2,1,2,3])
)
UNION
(
    SELECT unnest(ARRAY[2,1,2,3])
    EXCEPT ALL
    SELECT unnest(ARRAY[1,2,2,3])
)

робіт.


0

У мене була така сама проблема в SQL Server і я написав цей скрипт T-SQL для автоматизації процесу (насправді це розводнена версія, моя записала всю різницю в одну таблицю для зручності звітування).

Оновіть «MyTable» та «MyOtherTable» до назв таблиць, які ви хочете порівняти.

DECLARE @ColName varchar(100)
DECLARE @Table1 varchar(100) = 'MyTable'
DECLARE @Table2 varchar(100) = 'MyOtherTable'


IF (OBJECT_ID('tempdb..#col') IS NOT NULL) DROP TABLE #col
SELECT  IDENTITY(INT, 1, 1) RowNum , c.name
INTO    #col
FROM    SYS.Objects o 
        JOIN SYS.columns c on o.object_id = c.object_id
WHERE   o.name = @Table1 AND NOT c.Name IN ('List','Columns','YouWantToIgnore')

DECLARE @Counter INT = (SELECT MAX(RowNum) FROM #col)

    WHILE @Counter > 0

        BEGIN
            SET @ColName = (SELECT name FROM #Col WHERE RowNum= @Counter)
                EXEC ('SELECT  t1.Identifier
                        ,t1.'+@ColName+' AS '+@Table1+@ColName+'
                        ,t2.'+@ColName+' AS '+@Table2+@ColName+'
                FROM    '+@Table1+' t1
                        LEFT JOIN '+@Table2+' t2 ON t1.Identifier = t2.Identifier 
                WHERE   t1.'+@ColName+' <> t2.'+@ColName)
            SET @Counter = @Counter - 1 
        END

0

Я написав це для порівняння результатів досить неприємного подання, яке я переніс з Oracle на SQL Server. Він створює пару тимчасових таблиць, #DataVariances та #SchemaVariances, з різницею (як ви вже здогадалися) даних у таблицях та схеми самих таблиць.

Це вимагає, щоб обидві таблиці мали первинний ключ, але ви можете залишити його в tempdb із стовпцем ідентифікатора, якщо у вихідних таблицях його немає.

declare @TableA_ThreePartName nvarchar(max) = ''
declare @TableB_ThreePartName nvarchar(max) = ''
declare @KeyName nvarchar(max) = ''

/***********************************************************************************************

    Script to compare two tables and return differneces in schema and data.

    Author: Devin Lamothe       2017-08-11

***********************************************************************************************/
set nocount on

-- Split three part name into database/schema/table
declare @Database_A nvarchar(max) = (
    select  left(@TableA_ThreePartName,charindex('.',@TableA_ThreePartName) - 1))
declare @Table_A nvarchar(max) = (
    select  right(@TableA_ThreePartName,len(@TableA_ThreePartName) - charindex('.',@TableA_ThreePartName,len(@Database_A) + 2)))
declare @Schema_A nvarchar(max) = (
    select  replace(replace(@TableA_ThreePartName,@Database_A + '.',''),'.' + @Table_A,''))

declare @Database_B nvarchar(max) = (
    select  left(@TableB_ThreePartName,charindex('.',@TableB_ThreePartName) - 1))
declare @Table_B nvarchar(max) = (
    select  right(@TableB_ThreePartName,len(@TableB_ThreePartName) - charindex('.',@TableB_ThreePartName,len(@Database_B) + 2)))
declare @Schema_B nvarchar(max) = (
    select  replace(replace(@TableB_ThreePartName,@Database_B + '.',''),'.' + @Table_B,''))

-- Get schema for both tables
declare @GetTableADetails nvarchar(max) = '
    use [' + @Database_A +']
        select  COLUMN_NAME
             ,  DATA_TYPE
          from  INFORMATION_SCHEMA.COLUMNS
         where  TABLE_NAME = ''' + @Table_A + '''
           and  TABLE_SCHEMA = ''' + @Schema_A + '''
    '
create table #Table_A_Details (
    ColumnName nvarchar(max)
,   DataType nvarchar(max)
)
insert into #Table_A_Details
exec (@GetTableADetails)

declare @GetTableBDetails nvarchar(max) = '
    use [' + @Database_B +']
        select  COLUMN_NAME
             ,  DATA_TYPE
          from  INFORMATION_SCHEMA.COLUMNS
         where  TABLE_NAME = ''' + @Table_B + '''
           and  TABLE_SCHEMA = ''' + @Schema_B + '''
    '
create table #Table_B_Details (
    ColumnName nvarchar(max)
,   DataType nvarchar(max)
)
insert into #Table_B_Details
exec (@GetTableBDetails)


-- Get differences in table schema
            select  ROW_NUMBER() over (order by
                        a.ColumnName
                    ,   b.ColumnName) as RowKey
                 ,  a.ColumnName as A_ColumnName
                 ,  a.DataType as A_DataType
                 ,  b.ColumnName as B_ColumnName
                 ,  b.DataType as B_DataType
              into  #FieldList
              from  #Table_A_Details a
   full outer join  #Table_B_Details b
                on  a.ColumnName = b.ColumnName
             where  a.ColumnName is null
                or  b.ColumnName is null
                or  a.DataType <> b.DataType

        drop table  #Table_A_Details
        drop table  #Table_B_Details

            select  coalesce(A_ColumnName,B_ColumnName) as ColumnName
                 ,  A_DataType
                 ,  B_DataType
              into  #SchemaVariances
              from  #FieldList

-- Get differences in table data
declare @LastColumn int = (select max(RowKey) from #FieldList)
declare @RowNumber int = 1
declare @ThisField nvarchar(max)
declare @TestSql nvarchar(max)



create table #DataVariances (
    TableKey            nvarchar(max)
,   FieldName           nvarchar(max)
,   TableA_Value        nvarchar(max)
,   TableB_Value        nvarchar(max)
)

delete from #FieldList where A_DataType in ('varbinary','image') or B_DataType in ('varbinary','image') 

while @RowNumber <= @LastColumn begin
    set @TestSql = '
        select  coalesce(a.[' + @KeyName + '],b.[' + @KeyName + ']) as TableKey
             ,  ''' + @ThisField + ''' as FieldName
             ,  a.[' + @ThisField + '] as [TableA_Value]
             ,  b.[' + @ThisField + '] as [TableB_Value]
          from  [' + @Database_A + '].[' + @Schema_A + '].[' + @Table_A + '] a 
    inner join  [' + @Database_B + '].[' + @Schema_B + '].[' + @Table_B + '] b
            on  a.[' + @KeyName + '] = b.[' + @KeyName + ']
         where  ltrim(rtrim(a.[' + @ThisField + '])) <> ltrim(rtrim(b.[' + @ThisField + ']))
            or (a.[' + @ThisField + '] is null and  b.[' + @ThisField + '] is not null)
            or (a.[' + @ThisField + '] is not null and  b.[' + @ThisField + '] is null)
'

insert into #DataVariances
exec (@TestSql)

set @RowNumber = @RowNumber + 1
set @ThisField = (select coalesce(A_ColumnName,B_ColumnName) from #FieldList a where RowKey = @RowNumber)

end

drop table #FieldList

print 'Query complete.  Select from #DataVariances to verify data integrity or #SchemaVariances to verify schemas match.  Data types varbinary and image are not checked.'

0

Більшість відповідей ігнорують проблему, порушену Камілем. (Саме там таблиці містять однакові рядки, але в кожній таблиці повторюються різні.) На жаль, я не можу використовувати його рішення, оскільки я перебуваю в Oracle. Найкраще, що мені вдалося придумати, це:

SELECT * FROM
   (
   SELECT column1, column2, ..., COUNT(*) AS the_count
   FROM tableA
   GROUP BY column1, column2, ...
   MINUS
   SELECT column1, column2, ..., COUNT(*) AS the_count
   FROM tableB
   GROUP BY column1, column2, ...
   )
UNION ALL
   (
   SELECT column1, column2, ..., COUNT(*) AS the_count
   FROM tableB
   GROUP BY column1, column2, ...
   MINUS
   SELECT column1, column2, ..., COUNT(*) AS the_count
   FROM tableA
   GROUP BY column1, column2, ...
   )

0

Для порівняння T1 (PK, A, B) та T2 (PK, A, B).

Спочатку порівняйте набори первинних ключів, щоб знайти відсутні значення ключа з будь-якої сторони:

SELECT T1.*, T2.* FROM T1 FULL OUTER JOIN T2 ON T1.PK=T2.PK WHERE T1.PK IS NULL OR T2.PK IS NULL;

Потім перелічіть усі невідповідності значень:

SELECT T1.PK, 'A' AS columnName, T1.A AS leftValue, T2.A AS rightValue FROM T1 JOIN T2 ON T1.PK=T2.PK WHERE COALESCE(T1.A,0) != COALESCE(T2.A,0)
UNION ALL
SELECT T1.PK, 'B' AS columnName, T1.B AS leftValue, T2.B AS rightValue FROM T1 JOIN T2 ON T1.PK=T2.PK WHERE COALESCE(T1.B,0) != COALESCE(T2.B,0)

А і В повинні бути одного типу. Ви можете використовувати ІНФОРМАЦІЙНУ СХЕМУ для генерації SELECT. Не забувайте про COALESCE, який також включає результати IS NULL. Ви також можете використовувати ПОВНЕ ЗОВНІШНЄ ПРИЄДНАННЯ та COALESCE (T1.PK, 0) = COALESCE (T2.PK, 0).

Наприклад для стовпців типу varchar:

SELECT concat('SELECT T1.PK, ''', COLUMN_NAME, ''' AS columnName, T1.', COLUMN_NAME, ' AS leftValue, T2.', COLUMN_NAME, ' AS rightValue FROM T1 JOIN T2 ON T1.PK=T2.PK WHERE COALESCE(T1.',COLUMN_NAME, ',0)!=COALESCE(T2.', COLUMN_NAME, ',0)')
FROM INFORMATION_SCHEMA.COLUMNS 
WHERE TABLE_NAME='T1' AND DATA_TYPE IN ('nvarchar','varchar');

0

Ми можемо порівняти дані з двох таблиць таблиць DB2, використовуючи простий запит нижче,

Крок 1: - Виберіть усі стовпці, які нам потрібно порівняти, з таблиці (T1) схеми (S)

     SELECT T1.col1,T1.col3,T1.col5 from S.T1

Крок 2: - Використовуйте ключове слово "Мінус" для порівняння 2 таблиць.

Крок 3: - Виберіть, які всі стовпці нам потрібно порівняти з таблиці (T2) схеми (S)

     SELECT T2.col1,T2.col3,T2.col5 from S.T1

КІНЦЕВИЙ результат:

     SELECT T1.col1,T1.col3,T1.col5 from S.T1
     MINUS 
     SELECT T2.col1,T2.col3,T2.col5 from S.T1;

Якщо запит не повертає жодного рядка, тоді дані абсолютно однакові.


-1

У MySQL, де "мінус" не підтримується, і з урахуванням продуктивності це швидко

query:
SELECT 
t1.id, 
t1.id 
FROM t1 inner join t2 using (id) where concat(t1.C, t1.D, ...)<>concat(t2.C, t2.D, ...)

-1

Альтернативний, розширений запит, заснований на відповіді dietbuddha & IanMc. Запит включає опис, щоб корисно показати, де існують та відсутні рядки. (Примітка: для SQL Server )

(
    select 'InTableA_NoMatchInTableB' as Msg, * from tableA
    except
    select 'InTableA_NoMatchInTableB' , * from tableB
)
union all
(
    select 'InTableB_NoMatchInTableA' as Msg, * from tableB
    except
    select 'InTableB_NNoMatchInTableA' ,* from tableA
)

-1
SELECT * 
FROM TABLE A
WHERE NOT EXISTS (SELECT 'X' 
                  FROM  TABLE B 
                  WHERE B.KEYFIELD1 = A.KEYFIELD1 
                  AND   B.KEYFIELD2 = A.KEYFIELD2 
                  AND   B.KEYFIELD3 = A.KEYFIELD3)
;

'X' - це будь-яке значення.

Змініть таблиці, щоб побачити різні розбіжності.

Обов’язково об’єднайте ключові поля у ваших таблицях.

Або просто використовуйте оператор MINUS з 2-ма операторами вибору, однак MINUS може працювати лише в Oracle.


мінус підтримується не у всіх реалізаціях. (наприклад, серверний сервер використовує крім).
Сер Swears-a-lot
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.