Ответ 1
Вы должны иметь возможность "MINUS" или "EXCEPT" в зависимости от вкуса SQL, используемого вашей СУБД.
select * from tableA
minus
select * from tableB
Если запрос не возвращает строк, то данные будут точно такими же.
У меня есть 2 таблицы TableA
и TableB
, которые имеют одинаковый формат столбца, например, обе таблицы TableA
и TableB
имеют столбцы
A B C D E F
где A и B являются первичными ключами.
Как написать SQL, чтобы проверить, что если TableA
и TableB
, имеющие одинаковые первичные ключи, содержат одинаковое значение в каждом столбце.
Это означает, что эти две таблицы имеют точно такие же данные.
Вы должны иметь возможность "MINUS" или "EXCEPT" в зависимости от вкуса SQL, используемого вашей СУБД.
select * from tableA
minus
select * from tableB
Если запрос не возвращает строк, то данные будут точно такими же.
Использование реляционных операторов:
SELECT * FROM TableA
UNION
SELECT * FROM TableB
EXCEPT
SELECT * FROM TableA
INTERSECT
SELECT * FROM TableB;
Измените EXCEPT
на MINUS
для Oracle.
Слегка придирчивая точка: вышеупомянутое полагается на приоритет оператора, который, согласно стандарту SQL, зависит от реализации, поэтому YMMV. Он работает для SQL Server, для которого приоритет:
INTERSECT
EXCEPT
и UNION
оцениваются слева направо.dietbuddha имеет приятный ответ. В тех случаях, когда у вас нет MINUS или EXCEPT, один из вариантов состоит в том, чтобы сделать объединение между таблицами, группировать по всем столбцам и убедиться, что есть два из всего:
SELECT col1, col2, col3
FROM
(SELECT * FROM tableA
UNION ALL
SELECT * FROM tableB) data
GROUP BY col1, col2, col3
HAVING count(*)!=2
SELECT c.ID
FROM clients c
WHERE EXISTS(SELECT c2.ID
FROM clients2 c2
WHERE c2.ID = c.ID);
Вернет все идентификаторы, которые являются одинаковыми в обеих таблицах. Чтобы получить различия, EXISTS НЕ СУЩЕСТВУЕТ.
Взяв script из 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
просто для завершения, proc, сохраненный с использованием метода except для сравнения 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
[email protected] = table 1
[email protected] = table 2
[email protected] = Fields to compare from table 1
[email protected] 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
Усиление ответа на диетубаддха...
select * from
(
select * from tableA
minus
select * from tableB
)
union all
select * from
(
select * from tableB
minus
select * from tableA
)
Вы можете найти различия между двумя таблицами, используя комбинацию вставки всех и полного внешнего соединения в 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
Хотя использование "ИЛИ" в предложении where не рекомендуется и обычно приводит к снижению производительности, вы все равно можете использовать приведенный выше запрос, если ваши таблицы не являются массивными. Если есть какой-либо результат для вышеупомянутого запроса, это точно различия двух таблиц, основанные на сравнении полей 1,2,3,4. Для повышения производительности запроса вы также можете отфильтровать его по дате (см. Прокомментированную часть).
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])
Результат равен null, но источники разные!
Но:
(
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])
)
работы.
У меня была такая же проблема в SQL Server, и я написал этот T-SQL script для автоматизации процесса (на самом деле это версия с увлажненной версией, я написал все данные для отдельной таблицы для удобства отчетности).
Обновите "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.'[email protected]+' AS '[email protected][email protected]+'
,t2.'[email protected]+' AS '[email protected][email protected]+'
FROM '[email protected]+' t1
LEFT JOIN '[email protected]+' t2 ON t1.Identifier = t2.Identifier
WHERE t1.'[email protected]+' <> t2.'[email protected])
SET @Counter = @Counter - 1
END
Я написал это, чтобы сравнить результаты довольно неприятного представления, которое я портировал с 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.'
Большинство ответов, кажется, игнорируют проблему, поднятую Камилом. (Вот где таблицы содержат одинаковые строки, но разные таблицы повторяются в каждой таблице.) К сожалению, я не могу использовать его решение, потому что я в 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, ...
)
Мы можем сравнить данные из двух таблиц таблиц 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
КОНЕЦ результат: - ВЫБЕРИТЕ T1.col1, T1.col3, T1.col5 из S.T1 МИНУС ВЫБРАТЬ T2.col1, T2.col3, T2.col5 из S.T1;
Если запрос не возвращает строк, данные точно такие же.
Сравнить 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)
A и B должны быть одного типа. Вы можете использовать ИНФОРМАЦИОННУЮ СХЕМУ для генерации SELECT. Не забывайте КОАЛЕСС.
Например, для столбцов типа 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');
В 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, ...)
Альтернативный расширенный запрос, основанный на ответе dietbuddha и IanMc. Запрос включает описание, чтобы помочь показать, где строки существуют и отсутствуют. (NB: для 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
)
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)
;
"Х" - это любое значение.
Переключите таблицы, чтобы увидеть различные расхождения.
Обязательно объедините ключевые поля в ваших таблицах.
Или просто используйте оператор MINUS с двумя операторами выбора, однако, MINUS может работать только в Oracle.