如何使用 PL/SQL 函数比较两个表?
Posted
技术标签:
【中文标题】如何使用 PL/SQL 函数比较两个表?【英文标题】:How to compare two tables using PL/SQL function? 【发布时间】:2013-09-05 10:31:27 【问题描述】:我是 plsql 新手,正在努力编写代码。我想使用 plsql 函数比较两个表(除了记录之外,两个表完全相同)。输入参数将是 (table1, table2) - 它应该比较 table1 和 table2 并返回三个输出。
-
表之间的任何不匹配记录
table1 中存在但 table2 中不存在的任何行。
table2 中存在但 table1 中不存在的任何行。
我用 SQL 编写了查询,它可以工作,但我在这里硬编码了一个特定的表名。 请告知如何转换为函数。
SET TERMOUT OFF CONCAT OFF VERIFY OFF
SET FEEDBACK OFF
SET MARKUP html ON SPOOL ON
SPOOL ACTION.HTML
set pages 1000 lines 125
----
PROMPT SHOWING THE DIFFERENCE WITH THE MATCHING RECORDS FOR TABLE ACTION
BREAK ON ACTIONID duplicates skip page
column tblname format a10
select ACTIONID,tblname "tblname",ACTIONDESC,ACTIONNAME
from ((SELECT 'OLD VALUES' tblname,ACTIONID,ACTIONDESC,ACTIONNAME
FROM 'DEPL_ACTION
MINUS
SELECT 'NEW VALUES' tblname,ACTIONID,ACTIONDESC,ACTIONNAME
FROM ACTION )
UNION ALL
( SELECT 'NEW VALUES' tblname,ACTIONID,ACTIONDESC,ACTIONNAME
FROM ACTION
MINUS
SELECT 'OLD VALUES' tblname,ACTIONID,ACTIONDESC,ACTIONNAME
FROM 'DEPL_ACTION )) aa where (ACTIONID) in ( select ACTIONID from (
(SELECT ACTIONID,ACTIONDESC,ACTIONNAME
FROM 'DEPL_ACTION
MINUS
SELECT ACTIONID,ACTIONDESC,ACTIONNAME
FROM ACTION )
UNION ALL
( SELECT ACTIONID,ACTIONDESC,ACTIONNAME
FROM ACTION
MINUS
SELECT ACTIONID,ACTIONDESC,ACTIONNAME
FROM 'DEPL_ACTION ) ) group by ACTIONID having count(*) > 1 ) order by ACTIONID;
CLEAR BREAKS
PROMPT SHOWING THE EXTRA RECORDS IN OLD VALUES FOR TABLE ACTION
select ACTIONID,tblname "tblname",ACTIONDESC,ACTIONNAME
from ((SELECT 'OLD VALUES' tblname,ACTIONID,ACTIONDESC,ACTIONNAME
FROM 'DEPL_ACTION
MINUS
SELECT 'NEW VALUES' tblname,ACTIONID,ACTIONDESC,ACTIONNAME
FROM ACTION )
UNION ALL
( SELECT 'NEW VALUES' tblname,ACTIONID,ACTIONDESC,ACTIONNAME
FROM ACTION
MINUS
SELECT 'OLD VALUES' tblname,ACTIONID,ACTIONDESC,ACTIONNAME
FROM 'DEPL_ACTION )) aa where (ACTIONID) in ( select ACTIONID from (
(SELECT ACTIONID,ACTIONDESC,ACTIONNAME
FROM 'DEPL_ACTION
MINUS
SELECT ACTIONID,ACTIONDESC,ACTIONNAME
FROM ACTION )
UNION ALL
( SELECT ACTIONID,ACTIONDESC,ACTIONNAME
FROM ACTION
MINUS
SELECT ACTIONID,ACTIONDESC,ACTIONNAME
FROM 'DEPL_ACTION ) ) group by ACTIONID having count(*) = 1 ) order by ACTIONID;
PROMPT SHOWING THE EXTRA RECORDS IN NEW VALUES FOR TABLE ACTION
select ACTIONID,tblname "tblname",ACTIONDESC,ACTIONNAME
from ((SELECT 'OLD VALUES' tblname,ACTIONID,ACTIONDESC,ACTIONNAME
FROM 'DEPL_ACTION
MINUS
SELECT 'NEW VALUES' tblname,ACTIONID,ACTIONDESC,ACTIONNAME
FROM ACTION )
UNION ALL
( SELECT 'NEW VALUES' tblname,ACTIONID,ACTIONDESC,ACTIONNAME
FROM ACTION
MINUS
SELECT 'OLD VALUES' tblname,ACTIONID,ACTIONDESC,ACTIONNAME
FROM 'DEPL_ACTION )) aa where (ACTIONID) in ( select ACTIONID from (
(SELECT ACTIONID,ACTIONDESC,ACTIONNAME
FROM 'DEPL_ACTION
MINUS
SELECT ACTIONID,ACTIONDESC,ACTIONNAME
FROM ACTION )
UNION ALL
( SELECT ACTIONID,ACTIONDESC,ACTIONNAME
FROM ACTION
MINUS
SELECT ACTIONID,ACTIONDESC,ACTIONNAME
FROM 'DEPL_ACTION ) ) group by ACTIONID having count(*) = 1 ) order by ACTIONID;
SET MARKUP HTML OFF
SPOOL OFF
EXIT
【问题讨论】:
您在为哪一部分苦苦挣扎?接受函数中的表名,或从函数中投影行,还是其他? 如果我想执行上述操作,我应该使用哪种返回类型??@DavidAldridge 首先我想问你为什么要用函数来做这件事——结果直接来自 SQL,所以为了方便你可以将逻辑嵌入到视图中,但使用函数确实似乎不是一个好的选择 我必须把它嵌入到一个套件中。所以我不能使用 sqlplus 。所以我们将以加密方式获得数据库连接,所以我们想使用一个函数@DavidAldridge 这个环境不能只对数据库运行查询吗? 【参考方案1】:我不会为它创建一个终极功能,维护它会非常困难。为每对表创建一个包。
标题:
CREATE OR REPLACE PACKAGE tab_a_pkg AS
TYPE tab_a_ntt IS TABLE OF tab_a%ROWTYPE;
FUNCTION mismatched RETURN tab_a_ntt;
FUNCTION a_minus_b RETURN tab_a_ntt;
FUNCTION b_minus_a RETURN tab_a_ntt;
END tab_a_pkg;
主体:
CREATE OR REPLACE PACKAGE BODY tab_a_pkg AS
FUNCTION mismatched RETURN tab_a_ntt
AS
l_return tab_a_ntt;
BEGIN
SELECT NVL(col_a1, col_b1) AS col_1
, NVL(col_a2, col_b2) AS col_2
BULK COLLECT INTO l_return
FROM tab_a
FULL JOIN tab_b
ON col_a1 = col_b1
AND col_a2 = col_b2
WHERE col_a1 IS NULL
OR col_b1 IS NULL;
RETURN l_return;
END mismatched;
FUNCTION a_minus_b RETURN tab_a_ntt
AS
l_return tab_a_ntt;
BEGIN
SELECT *
BULK COLLECT INTO l_return
FROM
(
SELECT * FROM tab_a
MINUS
SELECT * FROM tab_b
);
RETURN l_return;
END a_minus_b;
FUNCTION b_minus_a RETURN tab_a_ntt
AS
l_return tab_a_ntt;
BEGIN
SELECT *
BULK COLLECT INTO l_return
FROM
(
SELECT * FROM tab_b
MINUS
SELECT * FROM tab_a
);
RETURN l_return;
END b_minus_a;
END tab_a_pkg;
运行:
DECLARE
l_tab_a tab_a_pkg.tab_a_ntt;
BEGIN
l_tab_a := tab_a_pkg.mismatched();
FOR indx IN 1..l_tab_a.COUNT LOOP
DBMS_OUTPUT.PUT_LINE(l_tab_a(indx).col_a1 || ' ' || l_tab_a(indx).col_a2);
END LOOP;
DBMS_OUTPUT.PUT_LINE('------');
l_tab_a := tab_a_pkg.a_minus_b();
FOR indx IN 1..l_tab_a.COUNT LOOP
DBMS_OUTPUT.PUT_LINE(l_tab_a(indx).col_a1 || ' ' || l_tab_a(indx).col_a2);
END LOOP;
DBMS_OUTPUT.PUT_LINE('------');
l_tab_a := tab_a_pkg.b_minus_a();
FOR indx IN 1..l_tab_a.COUNT LOOP
DBMS_OUTPUT.PUT_LINE(l_tab_a(indx).col_a1 || ' ' || l_tab_a(indx).col_a2);
END LOOP;
DBMS_OUTPUT.PUT_LINE('------');
END;
/*
23 223
24 224
13 123
14 124
------
13 123
14 124
------
23 223
24 224
------
*/
【讨论】:
以上代码运行良好,我们是否应该为每对表创建包,我必须比较 12 对表,这是否意味着 12 个不同的包? @the_slk 是的,没错。如果您必须为其中一对夫妇进行更改,则其他 11 对是安全的。它易于维护、测试和发布。【参考方案2】:这个 proc 存储可能在您搜索时 我把它发布到了这个网站的另一个主题,但我不记得是哪一个了:)
用于比较具有相同 pk 的 2 个表中的选定字段 ps_TableGap 'tbl1','tbl2', 'fld1,fld2,fld3...fields of tbl1', 'Fld4,fld5,fld6...fields of tbl2 (optional us as tbl1 is empty)'
您有 3 个基于 tbl1 'DEL'、'ADD'、'GAP' 的结果 tbl2的字段以_为前缀表示不同(需要有不同的字段名称才能将结果推送到报告服务中)
/****** 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
-- =============================================
alter 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
【讨论】:
以上是关于如何使用 PL/SQL 函数比较两个表?的主要内容,如果未能解决你的问题,请参考以下文章