我遇到Oracle DB-Link连接问题(Oracle 11g)。让我们考虑以下情况:
CREATE DATABASE LINK "CHECK_CONNECTION"
CONNECT TO USER2
IDENTIFIED BY "password1"
USING 'DATABASE_B';
SELECT * FROM DUAL@CHECK_CONNECTION
Error at line 1
ORA-01017: invalid username/password; logon denied
DROP DATABASE LINK CHECK_CONNECTION
CREATE DATABASE LINK "CHECK_CONNECTION"
CONNECT TO USER2
IDENTIFIED BY "password2"
USING 'DATABASE_B';
SELECT * FROM DUAL@CHECK_CONNECTION
DUMMY
-----
X
1 row selected.
DROP DATABASE LINK CHECK_CONNECTION
CREATE DATABASE LINK "CHECK_CONNECTION"
CONNECT TO USER2
IDENTIFIED BY "password1"
USING 'DATABASE_B';
SELECT * FROM DUAL@CHECK_CONNECTION
DUMMY
-----
X
1 row selected.
CREATE DATABASE LINK "CHECK_CONNECTION_2"
CONNECT TO USER2
IDENTIFIED BY "password1"
USING 'DATABASE_B';
SELECT * FROM DUAL@CHECK_CONNECTION_2
Error at line 1
ORA-01017: invalid username/password; logon denied
如果密码错误,为什么连接正确?
答案 0 :(得分:1)
从命令alter session close database link上的手册:
当您发出使用数据库链接的语句时,Oracle数据库 使用该链接在远程数据库上为您创建会话。该 连接保持打开状态,直到您结束本地会话...
每次使用数据库链接时,Oracle都不会重新连接。但即使数据库链接发生变化,保持连接仍然存在似乎是一个小错误。我已经证实这仍然发生在12c。
这应该不是什么大问题,因为数据库链接应该保持相当静态。就像应用程序不应该为每个查询重新连接到数据库的方式一样,数据库会话不应经常更改链接。
许多奇怪的事情发生在数据库链接上。让您的远程过程尽可能简单。
答案 1 :(得分:1)
您需要检查通话计划。有可能是第一次从数据库中获取值,但是当第二次调用它时,它是从缓存中读取的。举个小例子:
SQL> set autotrace traceonly;
SQL> select * from dual;
Execution Plan
----------------------------------------------------------
Plan hash value: 272002086
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 2 | 2 (0)| 00:00:01 |
| 1 | TABLE ACCESS FULL| DUAL | 1 | 2 | 2 (0)| 00:00:01 |
--------------------------------------------------------------------------
Statistics
----------------------------------------------------------
1 recursive calls
0 db block gets
3 consistent gets
2 physical reads
0 redo size
522 bytes sent via SQL*Net to client
523 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
SQL> select * from dual;
Execution Plan
----------------------------------------------------------
Plan hash value: 272002086
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 2 | 2 (0)| 00:00:01 |
| 1 | TABLE ACCESS FULL| DUAL | 1 | 2 | 2 (0)| 00:00:01 |
--------------------------------------------------------------------------
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
3 consistent gets
0 physical reads
0 redo size
522 bytes sent via SQL*Net to client
523 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
SQL>
从迹象中可以明显看出,在第一次调用期间,有2次物理读取。但是当再次调用相同的语句时,有0个物理读取,这意味着结果是从缓存中读取的。
在你的情况下,虽然db链接的定义是更改但基本的sql保持不变,因此发生了缓存命中。
答案 2 :(得分:0)
@Jon Heller
您的回答很有帮助 - 如果将在步骤6中使用,则会在删除和创建正确的数据库链接之间使用会话。如果在丢弃不正确的数据库链接之后使用它,它就无法工作 - 只是因为没有创建连接。
但是你告诉我,也许我解决问题的概念不正确。
它不应该是一个大问题,因为数据库链接应该保持公平 静态的。就像应用程序不应该重新连接到的方式一样 对于每个查询的数据库,数据库会话不应该改变 经常链接。
我希望从级别USER1使用PL / SQL过程验证与其他数据库的连接。
CREATE OR REPLACE PROCEDURE USER1.CHECK_CONNECTION_PROC (
P_ID IN NUMBER,
P_USERNAME IN VARCHAR2,
P_PASSWORD IN VARCHAR2,
P_DATABASE IN VARCHAR2,
P_RESULT OUT VARCHAR2)
IS
M_DBLINK_NAME VARCHAR2 (100) DEFAULT 'CHECK_CONNECTION';
M_IS_EXISTS NUMBER;
BEGIN
EXECUTE IMMEDIATE
'CREATE DATABASE LINK "'
|| M_DBLINK_NAME
|| '" CONNECT TO '
|| P_USERNAME
|| ' IDENTIFIED BY "'
|| P_PASSWORD
|| '" USING '''
|| P_DATABASE
|| '''';
EXECUTE IMMEDIATE 'SELECT COUNT(*) FROM DUAL@' || M_DBLINK_NAME;
P_RESULT := 'PASSED';
EXECUTE IMMEDIATE 'DROP DATABASE LINK ' || M_DBLINK_NAME;
EXCEPTION
WHEN LOGIN_DENIED
THEN
P_RESULT := 'LOGIN_DENIED';
EXECUTE IMMEDIATE 'DROP DATABASE LINK ' || M_DBLINK_NAME;
WHEN OTHERS
THEN
P_RESULT := SUBSTR (SQLERRM, 1, 200);
SELECT SIGN (COUNT (*))
INTO M_IS_EXISTS
FROM ALL_DB_LINKS
WHERE DB_LINK = M_DBLINK_NAME;
IF M_IS_EXISTS = 1
THEN
EXECUTE IMMEDIATE 'DROP DATABASE LINK ' || M_DBLINK_NAME;
END IF;
END CHECK_CONNECTION_PROC;
DECLARE
M_RESULT VARCHAR2 (4000);
BEGIN
EXECUTE IMMEDIATE 'TRUNCATE TABLE TEMPORARY_TABLE';
INSERT ALL
INTO TEMPORARY_TABLE (NUMERIC_VALUE1,
VARCHAR_VALUE1,
VARCHAR_VALUE2,
VARCHAR_VALUE3)
VALUES (1,
'USER2',
'password2',
'DATABASE_B')
INTO TEMPORARY_TABLE (NUMERIC_VALUE1,
VARCHAR_VALUE1,
VARCHAR_VALUE2,
VARCHAR_VALUE3)
VALUES (2,
'USER3',
'password3',
'DATABASE_C')
INTO TEMPORARY_TABLE (NUMERIC_VALUE1,
VARCHAR_VALUE1,
VARCHAR_VALUE2,
VARCHAR_VALUE3)
VALUES (3,
'USER4',
'password4',
'DATABASE_D')
SELECT * FROM DUAL;
FOR CNT IN ( SELECT TMP.NUMERIC_VALUE1 ID,
TMP.VARCHAR_VALUE1 USERNAME,
TMP.VARCHAR_VALUE2 PASSWORD,
TMP.VARCHAR_VALUE3 DATABASE
FROM TEMPORARY_TABLE TMP
ORDER BY 1)
LOOP
CHECK_CONNECTION_PROC (CNT.ID,
CNT.USERNAME,
CNT.PASSWORD,
CNT.DATABASE,
M_RESULT);
UPDATE TEMPORARY_TABLE
SET VARCHAR_VALUE4 = M_RESULT
WHERE NUMERIC_VALUE1 = CNT.ID;
END LOOP;
END;
SELECT TMP.NUMERIC_VALUE1 ID,
TMP.VARCHAR_VALUE1 USERNAME,
TMP.VARCHAR_VALUE2 PASSWORD,
TMP.VARCHAR_VALUE3 DATABASE,
TMP.VARCHAR_VALUE4 RESULT
FROM TEMPORARY_TABLE TMP;
ID USERNAME PASSWORD DATABASE RESULT
---------------------------------------------------
1 USER2 password2 DATABASE_B PASSED
2 USER3 password3 DATABASE_C PASSED
3 USER4 password4 DATABASE_D PASSED
3 rows selected.
至于现在,除了创建临时DB-Link之外,我还没有找到其他方法来验证其他数据库的用户名和密码的正确性。